xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp (revision 734e82fe33aa764367791a7d603b383996c6b40b)
1 //== InvalidPtrChecker.cpp ------------------------------------- -*- C++ -*--=//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines InvalidPtrChecker which finds usages of possibly
10 // invalidated pointer.
11 // CERT SEI Rules ENV31-C and ENV34-C
12 // For more information see:
13 // https://wiki.sei.cmu.edu/confluence/x/8tYxBQ
14 // https://wiki.sei.cmu.edu/confluence/x/5NUxBQ
15 //===----------------------------------------------------------------------===//
16 
17 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19 #include "clang/StaticAnalyzer/Core/Checker.h"
20 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24 
25 using namespace clang;
26 using namespace ento;
27 
28 namespace {
29 
30 class InvalidPtrChecker
31     : public Checker<check::Location, check::BeginFunction, check::PostCall> {
32 private:
33   BugType BT{this, "Use of invalidated pointer", categories::MemoryError};
34 
35   void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const;
36 
37   using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call,
38                                                 CheckerContext &C) const;
39 
40   // SEI CERT ENV31-C
41   const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = {
42       {{{"setenv"}, 3}, &InvalidPtrChecker::EnvpInvalidatingCall},
43       {{{"unsetenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
44       {{{"putenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
45       {{{"_putenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
46       {{{"_wputenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
47   };
48 
49   void postPreviousReturnInvalidatingCall(const CallEvent &Call,
50                                           CheckerContext &C) const;
51 
52   // SEI CERT ENV34-C
53   const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = {
54       {{{"getenv"}, 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
55       {{{"setlocale"}, 2},
56        &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
57       {{{"strerror"}, 1},
58        &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
59       {{{"localeconv"}, 0},
60        &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
61       {{{"asctime"}, 1},
62        &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
63   };
64 
65 public:
66   // Obtain the environment pointer from 'main()' (if present).
67   void checkBeginFunction(CheckerContext &C) const;
68 
69   // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
70   // pointer from 'main()'
71   // Handle functions in PreviousCallInvalidatingFunctions.
72   // Also, check if invalidated region is passed to a
73   // conservatively evaluated function call as an argument.
74   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
75 
76   // Check if invalidated region is being dereferenced.
77   void checkLocation(SVal l, bool isLoad, const Stmt *S,
78                      CheckerContext &C) const;
79 };
80 
81 } // namespace
82 
83 // Set of memory regions that were invalidated
84 REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)
85 
86 // Stores the region of the environment pointer of 'main' (if present).
87 REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const MemRegion *)
88 
89 // Stores key-value pairs, where key is function declaration and value is
90 // pointer to memory region returned by previous call of this function
91 REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *,
92                                const MemRegion *)
93 
94 void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call,
95                                              CheckerContext &C) const {
96   StringRef FunctionName = Call.getCalleeIdentifier()->getName();
97   ProgramStateRef State = C.getState();
98   const MemRegion *SymbolicEnvPtrRegion = State->get<EnvPtrRegion>();
99   if (!SymbolicEnvPtrRegion)
100     return;
101 
102   State = State->add<InvalidMemoryRegions>(SymbolicEnvPtrRegion);
103 
104   const NoteTag *Note =
105       C.getNoteTag([SymbolicEnvPtrRegion, FunctionName](
106                        PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
107         if (!BR.isInteresting(SymbolicEnvPtrRegion))
108           return;
109         Out << '\'' << FunctionName
110             << "' call may invalidate the environment parameter of 'main'";
111       });
112 
113   C.addTransition(State, Note);
114 }
115 
116 void InvalidPtrChecker::postPreviousReturnInvalidatingCall(
117     const CallEvent &Call, CheckerContext &C) const {
118   ProgramStateRef State = C.getState();
119 
120   const NoteTag *Note = nullptr;
121   const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
122   // Invalidate the region of the previously returned pointer - if there was
123   // one.
124   if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {
125     const MemRegion *PrevReg = *Reg;
126     State = State->add<InvalidMemoryRegions>(PrevReg);
127     Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR,
128                                       llvm::raw_ostream &Out) {
129       if (!BR.isInteresting(PrevReg))
130         return;
131       Out << '\'';
132       FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
133       Out << "' call may invalidate the result of the previous " << '\'';
134       FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
135       Out << '\'';
136     });
137   }
138 
139   const LocationContext *LCtx = C.getLocationContext();
140   const auto *CE = cast<CallExpr>(Call.getOriginExpr());
141 
142   // Function call will return a pointer to the new symbolic region.
143   DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal(
144       CE, LCtx, CE->getType(), C.blockCount());
145   State = State->BindExpr(CE, LCtx, RetVal);
146 
147   // Remember to this region.
148   const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion());
149   const MemRegion *MR =
150       const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion());
151   State = State->set<PreviousCallResultMap>(FD, MR);
152 
153   ExplodedNode *Node = C.addTransition(State, Note);
154   const NoteTag *PreviousCallNote =
155       C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
156         if (!BR.isInteresting(MR))
157           return;
158         Out << '\'' << "'previous function call was here" << '\'';
159       });
160 
161   C.addTransition(State, Node, PreviousCallNote);
162 }
163 
164 // TODO: This seems really ugly. Simplify this.
165 static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State,
166                                                     const MemRegion *Reg) {
167   while (Reg) {
168     if (State->contains<InvalidMemoryRegions>(Reg))
169       return Reg;
170     const auto *SymBase = Reg->getSymbolicBase();
171     if (!SymBase)
172       break;
173     const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());
174     if (!SRV)
175       break;
176     Reg = SRV->getRegion();
177     if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion()))
178       Reg = VarReg;
179   }
180   return nullptr;
181 }
182 
183 // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
184 // pointer from 'main()' Also, check if invalidated region is passed to a
185 // function call as an argument.
186 void InvalidPtrChecker::checkPostCall(const CallEvent &Call,
187                                       CheckerContext &C) const {
188   // Check if function invalidates 'envp' argument of 'main'
189   if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call))
190     (this->**Handler)(Call, C);
191 
192   // Check if function invalidates the result of previous call
193   if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))
194     (this->**Handler)(Call, C);
195 
196   // Check if one of the arguments of the function call is invalidated
197 
198   // If call was inlined, don't report invalidated argument
199   if (C.wasInlined)
200     return;
201 
202   ProgramStateRef State = C.getState();
203 
204   for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) {
205 
206     if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(
207             Call.getArgSVal(I).getAsRegion())) {
208       if (const MemRegion *InvalidatedSymbolicBase =
209               findInvalidatedSymbolicBase(State, SR)) {
210         ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
211         if (!ErrorNode)
212           return;
213 
214         SmallString<256> Msg;
215         llvm::raw_svector_ostream Out(Msg);
216         Out << "use of invalidated pointer '";
217         Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr,
218                                         C.getASTContext().getPrintingPolicy());
219         Out << "' in a function call";
220 
221         auto Report =
222             std::make_unique<PathSensitiveBugReport>(BT, Out.str(), ErrorNode);
223         Report->markInteresting(InvalidatedSymbolicBase);
224         Report->addRange(Call.getArgSourceRange(I));
225         C.emitReport(std::move(Report));
226       }
227     }
228   }
229 }
230 
231 // Obtain the environment pointer from 'main()', if present.
232 void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const {
233   if (!C.inTopFrame())
234     return;
235 
236   const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl());
237   if (!FD || FD->param_size() != 3 || !FD->isMain())
238     return;
239 
240   ProgramStateRef State = C.getState();
241   const MemRegion *EnvpReg =
242       State->getRegion(FD->parameters()[2], C.getLocationContext());
243 
244   // Save the memory region pointed by the environment pointer parameter of
245   // 'main'.
246   C.addTransition(State->set<EnvPtrRegion>(EnvpReg));
247 }
248 
249 // Check if invalidated region is being dereferenced.
250 void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S,
251                                       CheckerContext &C) const {
252   ProgramStateRef State = C.getState();
253 
254   // Ignore memory operations involving 'non-invalidated' locations.
255   const MemRegion *InvalidatedSymbolicBase =
256       findInvalidatedSymbolicBase(State, Loc.getAsRegion());
257   if (!InvalidatedSymbolicBase)
258     return;
259 
260   ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
261   if (!ErrorNode)
262     return;
263 
264   auto Report = std::make_unique<PathSensitiveBugReport>(
265       BT, "dereferencing an invalid pointer", ErrorNode);
266   Report->markInteresting(InvalidatedSymbolicBase);
267   C.emitReport(std::move(Report));
268 }
269 
270 void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
271   Mgr.registerChecker<InvalidPtrChecker>();
272 }
273 
274 bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
275   return true;
276 }
277