xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp (revision 38a52bd3b5cac3da6f7f6eef3dd050e6aa08ebb3)
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}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
58       {{"localeconv", 0},
59        &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
60       {{"asctime", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
61   };
62 
63 public:
64   // Obtain the environment pointer from 'main()' (if present).
65   void checkBeginFunction(CheckerContext &C) const;
66 
67   // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
68   // pointer from 'main()'
69   // Handle functions in PreviousCallInvalidatingFunctions.
70   // Also, check if invalidated region is passed to a
71   // conservatively evaluated function call as an argument.
72   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
73 
74   // Check if invalidated region is being dereferenced.
75   void checkLocation(SVal l, bool isLoad, const Stmt *S,
76                      CheckerContext &C) const;
77 };
78 
79 } // namespace
80 
81 // Set of memory regions that were invalidated
82 REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)
83 
84 // Stores the region of the environment pointer of 'main' (if present).
85 // Note: This pointer has type 'const MemRegion *', however the trait is only
86 // specialized to 'const void*' and 'void*'
87 REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const void *)
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 auto *Reg = State->get<EnvPtrRegion>();
99   if (!Reg)
100     return;
101   const auto *SymbolicEnvPtrRegion =
102       reinterpret_cast<const MemRegion *>(const_cast<const void *>(Reg));
103 
104   State = State->add<InvalidMemoryRegions>(SymbolicEnvPtrRegion);
105 
106   const NoteTag *Note =
107       C.getNoteTag([SymbolicEnvPtrRegion, FunctionName](
108                        PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
109         if (!BR.isInteresting(SymbolicEnvPtrRegion))
110           return;
111         Out << '\'' << FunctionName
112             << "' call may invalidate the environment parameter of 'main'";
113       });
114 
115   C.addTransition(State, Note);
116 }
117 
118 void InvalidPtrChecker::postPreviousReturnInvalidatingCall(
119     const CallEvent &Call, CheckerContext &C) const {
120   ProgramStateRef State = C.getState();
121 
122   const NoteTag *Note = nullptr;
123   const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
124   // Invalidate the region of the previously returned pointer - if there was
125   // one.
126   if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {
127     const MemRegion *PrevReg = *Reg;
128     State = State->add<InvalidMemoryRegions>(PrevReg);
129     Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR,
130                                       llvm::raw_ostream &Out) {
131       if (!BR.isInteresting(PrevReg))
132         return;
133       Out << '\'';
134       FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
135       Out << "' call may invalidate the the result of the previous " << '\'';
136       FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
137       Out << '\'';
138     });
139   }
140 
141   const LocationContext *LCtx = C.getLocationContext();
142   const auto *CE = cast<CallExpr>(Call.getOriginExpr());
143 
144   // Function call will return a pointer to the new symbolic region.
145   DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal(
146       CE, LCtx, CE->getType(), C.blockCount());
147   State = State->BindExpr(CE, LCtx, RetVal);
148 
149   // Remember to this region.
150   const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion());
151   const MemRegion *MR =
152       const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion());
153   State = State->set<PreviousCallResultMap>(FD, MR);
154 
155   ExplodedNode *Node = C.addTransition(State, Note);
156   const NoteTag *PreviousCallNote =
157       C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
158         if (!BR.isInteresting(MR))
159           return;
160         Out << '\'' << "'previous function call was here" << '\'';
161       });
162 
163   C.addTransition(State, Node, PreviousCallNote);
164 }
165 
166 // TODO: This seems really ugly. Simplify this.
167 static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State,
168                                                     const MemRegion *Reg) {
169   while (Reg) {
170     if (State->contains<InvalidMemoryRegions>(Reg))
171       return Reg;
172     const auto *SymBase = Reg->getSymbolicBase();
173     if (!SymBase)
174       break;
175     const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());
176     if (!SRV)
177       break;
178     Reg = SRV->getRegion();
179     if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion()))
180       Reg = VarReg;
181   }
182   return nullptr;
183 }
184 
185 // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
186 // pointer from 'main()' Also, check if invalidated region is passed to a
187 // function call as an argument.
188 void InvalidPtrChecker::checkPostCall(const CallEvent &Call,
189                                       CheckerContext &C) const {
190   // Check if function invalidates 'envp' argument of 'main'
191   if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call))
192     (this->**Handler)(Call, C);
193 
194   // Check if function invalidates the result of previous call
195   if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))
196     (this->**Handler)(Call, C);
197 
198   // Check if one of the arguments of the function call is invalidated
199 
200   // If call was inlined, don't report invalidated argument
201   if (C.wasInlined)
202     return;
203 
204   ProgramStateRef State = C.getState();
205 
206   for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) {
207 
208     if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(
209             Call.getArgSVal(I).getAsRegion())) {
210       if (const MemRegion *InvalidatedSymbolicBase =
211               findInvalidatedSymbolicBase(State, SR)) {
212         ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
213         if (!ErrorNode)
214           return;
215 
216         SmallString<256> Msg;
217         llvm::raw_svector_ostream Out(Msg);
218         Out << "use of invalidated pointer '";
219         Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr,
220                                         C.getASTContext().getPrintingPolicy());
221         Out << "' in a function call";
222 
223         auto Report =
224             std::make_unique<PathSensitiveBugReport>(BT, Out.str(), ErrorNode);
225         Report->markInteresting(InvalidatedSymbolicBase);
226         Report->addRange(Call.getArgSourceRange(I));
227         C.emitReport(std::move(Report));
228       }
229     }
230   }
231 }
232 
233 // Obtain the environment pointer from 'main()', if present.
234 void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const {
235   if (!C.inTopFrame())
236     return;
237 
238   const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl());
239   if (!FD || FD->param_size() != 3 || !FD->isMain())
240     return;
241 
242   ProgramStateRef State = C.getState();
243   const MemRegion *EnvpReg =
244       State->getRegion(FD->parameters()[2], C.getLocationContext());
245 
246   // Save the memory region pointed by the environment pointer parameter of
247   // 'main'.
248   State = State->set<EnvPtrRegion>(
249       reinterpret_cast<void *>(const_cast<MemRegion *>(EnvpReg)));
250   C.addTransition(State);
251 }
252 
253 // Check if invalidated region is being dereferenced.
254 void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S,
255                                       CheckerContext &C) const {
256   ProgramStateRef State = C.getState();
257 
258   // Ignore memory operations involving 'non-invalidated' locations.
259   const MemRegion *InvalidatedSymbolicBase =
260       findInvalidatedSymbolicBase(State, Loc.getAsRegion());
261   if (!InvalidatedSymbolicBase)
262     return;
263 
264   ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
265   if (!ErrorNode)
266     return;
267 
268   auto Report = std::make_unique<PathSensitiveBugReport>(
269       BT, "dereferencing an invalid pointer", ErrorNode);
270   Report->markInteresting(InvalidatedSymbolicBase);
271   C.emitReport(std::move(Report));
272 }
273 
274 void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
275   Mgr.registerChecker<InvalidPtrChecker>();
276 }
277 
278 bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
279   return true;
280 }
281