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