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