xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
1349cc55cSDimitry Andric //== InvalidPtrChecker.cpp ------------------------------------- -*- C++ -*--=//
2349cc55cSDimitry Andric //
3349cc55cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4349cc55cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5349cc55cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6349cc55cSDimitry Andric //
7349cc55cSDimitry Andric //===----------------------------------------------------------------------===//
8349cc55cSDimitry Andric //
9349cc55cSDimitry Andric // This file defines InvalidPtrChecker which finds usages of possibly
10349cc55cSDimitry Andric // invalidated pointer.
11349cc55cSDimitry Andric // CERT SEI Rules ENV31-C and ENV34-C
12349cc55cSDimitry Andric // For more information see:
13349cc55cSDimitry Andric // https://wiki.sei.cmu.edu/confluence/x/8tYxBQ
14349cc55cSDimitry Andric // https://wiki.sei.cmu.edu/confluence/x/5NUxBQ
15349cc55cSDimitry Andric //===----------------------------------------------------------------------===//
16349cc55cSDimitry Andric 
17349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
20349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
21349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
22349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
23349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24349cc55cSDimitry Andric 
25349cc55cSDimitry Andric using namespace clang;
26349cc55cSDimitry Andric using namespace ento;
27349cc55cSDimitry Andric 
28349cc55cSDimitry Andric namespace {
29349cc55cSDimitry Andric 
30349cc55cSDimitry Andric class InvalidPtrChecker
31349cc55cSDimitry Andric     : public Checker<check::Location, check::BeginFunction, check::PostCall> {
32349cc55cSDimitry Andric private:
33349cc55cSDimitry Andric   BugType BT{this, "Use of invalidated pointer", categories::MemoryError};
34349cc55cSDimitry Andric 
35349cc55cSDimitry Andric   void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const;
36349cc55cSDimitry Andric 
37349cc55cSDimitry Andric   using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call,
38349cc55cSDimitry Andric                                                 CheckerContext &C) const;
39349cc55cSDimitry Andric 
40349cc55cSDimitry Andric   // SEI CERT ENV31-C
41349cc55cSDimitry Andric   const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = {
42*bdd1243dSDimitry Andric       {{{"setenv"}, 3}, &InvalidPtrChecker::EnvpInvalidatingCall},
43*bdd1243dSDimitry Andric       {{{"unsetenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
44*bdd1243dSDimitry Andric       {{{"putenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
45*bdd1243dSDimitry Andric       {{{"_putenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
46*bdd1243dSDimitry Andric       {{{"_wputenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
47349cc55cSDimitry Andric   };
48349cc55cSDimitry Andric 
49349cc55cSDimitry Andric   void postPreviousReturnInvalidatingCall(const CallEvent &Call,
50349cc55cSDimitry Andric                                           CheckerContext &C) const;
51349cc55cSDimitry Andric 
52349cc55cSDimitry Andric   // SEI CERT ENV34-C
53349cc55cSDimitry Andric   const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = {
54*bdd1243dSDimitry Andric       {{{"getenv"}, 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
55*bdd1243dSDimitry Andric       {{{"setlocale"}, 2},
56349cc55cSDimitry Andric        &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
57*bdd1243dSDimitry Andric       {{{"strerror"}, 1},
58349cc55cSDimitry Andric        &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
59*bdd1243dSDimitry Andric       {{{"localeconv"}, 0},
60*bdd1243dSDimitry Andric        &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
61*bdd1243dSDimitry Andric       {{{"asctime"}, 1},
62*bdd1243dSDimitry Andric        &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
63349cc55cSDimitry Andric   };
64349cc55cSDimitry Andric 
65349cc55cSDimitry Andric public:
66349cc55cSDimitry Andric   // Obtain the environment pointer from 'main()' (if present).
67349cc55cSDimitry Andric   void checkBeginFunction(CheckerContext &C) const;
68349cc55cSDimitry Andric 
69349cc55cSDimitry Andric   // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
70349cc55cSDimitry Andric   // pointer from 'main()'
71349cc55cSDimitry Andric   // Handle functions in PreviousCallInvalidatingFunctions.
72349cc55cSDimitry Andric   // Also, check if invalidated region is passed to a
73349cc55cSDimitry Andric   // conservatively evaluated function call as an argument.
74349cc55cSDimitry Andric   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
75349cc55cSDimitry Andric 
76349cc55cSDimitry Andric   // Check if invalidated region is being dereferenced.
77349cc55cSDimitry Andric   void checkLocation(SVal l, bool isLoad, const Stmt *S,
78349cc55cSDimitry Andric                      CheckerContext &C) const;
79349cc55cSDimitry Andric };
80349cc55cSDimitry Andric 
81349cc55cSDimitry Andric } // namespace
82349cc55cSDimitry Andric 
83349cc55cSDimitry Andric // Set of memory regions that were invalidated
84349cc55cSDimitry Andric REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)
85349cc55cSDimitry Andric 
86349cc55cSDimitry Andric // Stores the region of the environment pointer of 'main' (if present).
8781ad6265SDimitry Andric REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const MemRegion *)
88349cc55cSDimitry Andric 
89349cc55cSDimitry Andric // Stores key-value pairs, where key is function declaration and value is
90349cc55cSDimitry Andric // pointer to memory region returned by previous call of this function
91349cc55cSDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *,
92349cc55cSDimitry Andric                                const MemRegion *)
93349cc55cSDimitry Andric 
94349cc55cSDimitry Andric void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call,
95349cc55cSDimitry Andric                                              CheckerContext &C) const {
96349cc55cSDimitry Andric   StringRef FunctionName = Call.getCalleeIdentifier()->getName();
97349cc55cSDimitry Andric   ProgramStateRef State = C.getState();
9881ad6265SDimitry Andric   const MemRegion *SymbolicEnvPtrRegion = State->get<EnvPtrRegion>();
9981ad6265SDimitry Andric   if (!SymbolicEnvPtrRegion)
100349cc55cSDimitry Andric     return;
101349cc55cSDimitry Andric 
102349cc55cSDimitry Andric   State = State->add<InvalidMemoryRegions>(SymbolicEnvPtrRegion);
103349cc55cSDimitry Andric 
104349cc55cSDimitry Andric   const NoteTag *Note =
105349cc55cSDimitry Andric       C.getNoteTag([SymbolicEnvPtrRegion, FunctionName](
106349cc55cSDimitry Andric                        PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
107349cc55cSDimitry Andric         if (!BR.isInteresting(SymbolicEnvPtrRegion))
108349cc55cSDimitry Andric           return;
109349cc55cSDimitry Andric         Out << '\'' << FunctionName
110349cc55cSDimitry Andric             << "' call may invalidate the environment parameter of 'main'";
111349cc55cSDimitry Andric       });
112349cc55cSDimitry Andric 
113349cc55cSDimitry Andric   C.addTransition(State, Note);
114349cc55cSDimitry Andric }
115349cc55cSDimitry Andric 
116349cc55cSDimitry Andric void InvalidPtrChecker::postPreviousReturnInvalidatingCall(
117349cc55cSDimitry Andric     const CallEvent &Call, CheckerContext &C) const {
118349cc55cSDimitry Andric   ProgramStateRef State = C.getState();
119349cc55cSDimitry Andric 
120349cc55cSDimitry Andric   const NoteTag *Note = nullptr;
121349cc55cSDimitry Andric   const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
122349cc55cSDimitry Andric   // Invalidate the region of the previously returned pointer - if there was
123349cc55cSDimitry Andric   // one.
124349cc55cSDimitry Andric   if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {
125349cc55cSDimitry Andric     const MemRegion *PrevReg = *Reg;
126349cc55cSDimitry Andric     State = State->add<InvalidMemoryRegions>(PrevReg);
127349cc55cSDimitry Andric     Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR,
128349cc55cSDimitry Andric                                       llvm::raw_ostream &Out) {
129349cc55cSDimitry Andric       if (!BR.isInteresting(PrevReg))
130349cc55cSDimitry Andric         return;
131349cc55cSDimitry Andric       Out << '\'';
132349cc55cSDimitry Andric       FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
13381ad6265SDimitry Andric       Out << "' call may invalidate the result of the previous " << '\'';
134349cc55cSDimitry Andric       FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
135349cc55cSDimitry Andric       Out << '\'';
136349cc55cSDimitry Andric     });
137349cc55cSDimitry Andric   }
138349cc55cSDimitry Andric 
139349cc55cSDimitry Andric   const LocationContext *LCtx = C.getLocationContext();
140349cc55cSDimitry Andric   const auto *CE = cast<CallExpr>(Call.getOriginExpr());
141349cc55cSDimitry Andric 
142349cc55cSDimitry Andric   // Function call will return a pointer to the new symbolic region.
143349cc55cSDimitry Andric   DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal(
144349cc55cSDimitry Andric       CE, LCtx, CE->getType(), C.blockCount());
145349cc55cSDimitry Andric   State = State->BindExpr(CE, LCtx, RetVal);
146349cc55cSDimitry Andric 
147349cc55cSDimitry Andric   // Remember to this region.
148349cc55cSDimitry Andric   const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion());
149349cc55cSDimitry Andric   const MemRegion *MR =
150349cc55cSDimitry Andric       const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion());
151349cc55cSDimitry Andric   State = State->set<PreviousCallResultMap>(FD, MR);
152349cc55cSDimitry Andric 
153349cc55cSDimitry Andric   ExplodedNode *Node = C.addTransition(State, Note);
154349cc55cSDimitry Andric   const NoteTag *PreviousCallNote =
155349cc55cSDimitry Andric       C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
156349cc55cSDimitry Andric         if (!BR.isInteresting(MR))
157349cc55cSDimitry Andric           return;
158349cc55cSDimitry Andric         Out << '\'' << "'previous function call was here" << '\'';
159349cc55cSDimitry Andric       });
160349cc55cSDimitry Andric 
161349cc55cSDimitry Andric   C.addTransition(State, Node, PreviousCallNote);
162349cc55cSDimitry Andric }
163349cc55cSDimitry Andric 
164349cc55cSDimitry Andric // TODO: This seems really ugly. Simplify this.
165349cc55cSDimitry Andric static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State,
166349cc55cSDimitry Andric                                                     const MemRegion *Reg) {
167349cc55cSDimitry Andric   while (Reg) {
168349cc55cSDimitry Andric     if (State->contains<InvalidMemoryRegions>(Reg))
169349cc55cSDimitry Andric       return Reg;
170349cc55cSDimitry Andric     const auto *SymBase = Reg->getSymbolicBase();
171349cc55cSDimitry Andric     if (!SymBase)
172349cc55cSDimitry Andric       break;
173349cc55cSDimitry Andric     const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());
174349cc55cSDimitry Andric     if (!SRV)
175349cc55cSDimitry Andric       break;
176349cc55cSDimitry Andric     Reg = SRV->getRegion();
177349cc55cSDimitry Andric     if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion()))
178349cc55cSDimitry Andric       Reg = VarReg;
179349cc55cSDimitry Andric   }
180349cc55cSDimitry Andric   return nullptr;
181349cc55cSDimitry Andric }
182349cc55cSDimitry Andric 
183349cc55cSDimitry Andric // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
184349cc55cSDimitry Andric // pointer from 'main()' Also, check if invalidated region is passed to a
185349cc55cSDimitry Andric // function call as an argument.
186349cc55cSDimitry Andric void InvalidPtrChecker::checkPostCall(const CallEvent &Call,
187349cc55cSDimitry Andric                                       CheckerContext &C) const {
188349cc55cSDimitry Andric   // Check if function invalidates 'envp' argument of 'main'
189349cc55cSDimitry Andric   if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call))
190349cc55cSDimitry Andric     (this->**Handler)(Call, C);
191349cc55cSDimitry Andric 
192349cc55cSDimitry Andric   // Check if function invalidates the result of previous call
193349cc55cSDimitry Andric   if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))
194349cc55cSDimitry Andric     (this->**Handler)(Call, C);
195349cc55cSDimitry Andric 
196349cc55cSDimitry Andric   // Check if one of the arguments of the function call is invalidated
197349cc55cSDimitry Andric 
198349cc55cSDimitry Andric   // If call was inlined, don't report invalidated argument
199349cc55cSDimitry Andric   if (C.wasInlined)
200349cc55cSDimitry Andric     return;
201349cc55cSDimitry Andric 
202349cc55cSDimitry Andric   ProgramStateRef State = C.getState();
203349cc55cSDimitry Andric 
204349cc55cSDimitry Andric   for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) {
205349cc55cSDimitry Andric 
206349cc55cSDimitry Andric     if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(
207349cc55cSDimitry Andric             Call.getArgSVal(I).getAsRegion())) {
208349cc55cSDimitry Andric       if (const MemRegion *InvalidatedSymbolicBase =
209349cc55cSDimitry Andric               findInvalidatedSymbolicBase(State, SR)) {
210349cc55cSDimitry Andric         ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
211349cc55cSDimitry Andric         if (!ErrorNode)
212349cc55cSDimitry Andric           return;
213349cc55cSDimitry Andric 
214349cc55cSDimitry Andric         SmallString<256> Msg;
215349cc55cSDimitry Andric         llvm::raw_svector_ostream Out(Msg);
216349cc55cSDimitry Andric         Out << "use of invalidated pointer '";
217349cc55cSDimitry Andric         Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr,
218349cc55cSDimitry Andric                                         C.getASTContext().getPrintingPolicy());
219349cc55cSDimitry Andric         Out << "' in a function call";
220349cc55cSDimitry Andric 
221349cc55cSDimitry Andric         auto Report =
222349cc55cSDimitry Andric             std::make_unique<PathSensitiveBugReport>(BT, Out.str(), ErrorNode);
223349cc55cSDimitry Andric         Report->markInteresting(InvalidatedSymbolicBase);
224349cc55cSDimitry Andric         Report->addRange(Call.getArgSourceRange(I));
225349cc55cSDimitry Andric         C.emitReport(std::move(Report));
226349cc55cSDimitry Andric       }
227349cc55cSDimitry Andric     }
228349cc55cSDimitry Andric   }
229349cc55cSDimitry Andric }
230349cc55cSDimitry Andric 
231349cc55cSDimitry Andric // Obtain the environment pointer from 'main()', if present.
232349cc55cSDimitry Andric void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const {
233349cc55cSDimitry Andric   if (!C.inTopFrame())
234349cc55cSDimitry Andric     return;
235349cc55cSDimitry Andric 
236349cc55cSDimitry Andric   const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl());
237349cc55cSDimitry Andric   if (!FD || FD->param_size() != 3 || !FD->isMain())
238349cc55cSDimitry Andric     return;
239349cc55cSDimitry Andric 
240349cc55cSDimitry Andric   ProgramStateRef State = C.getState();
241349cc55cSDimitry Andric   const MemRegion *EnvpReg =
242349cc55cSDimitry Andric       State->getRegion(FD->parameters()[2], C.getLocationContext());
243349cc55cSDimitry Andric 
244349cc55cSDimitry Andric   // Save the memory region pointed by the environment pointer parameter of
245349cc55cSDimitry Andric   // 'main'.
24681ad6265SDimitry Andric   C.addTransition(State->set<EnvPtrRegion>(EnvpReg));
247349cc55cSDimitry Andric }
248349cc55cSDimitry Andric 
249349cc55cSDimitry Andric // Check if invalidated region is being dereferenced.
250349cc55cSDimitry Andric void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S,
251349cc55cSDimitry Andric                                       CheckerContext &C) const {
252349cc55cSDimitry Andric   ProgramStateRef State = C.getState();
253349cc55cSDimitry Andric 
254349cc55cSDimitry Andric   // Ignore memory operations involving 'non-invalidated' locations.
255349cc55cSDimitry Andric   const MemRegion *InvalidatedSymbolicBase =
256349cc55cSDimitry Andric       findInvalidatedSymbolicBase(State, Loc.getAsRegion());
257349cc55cSDimitry Andric   if (!InvalidatedSymbolicBase)
258349cc55cSDimitry Andric     return;
259349cc55cSDimitry Andric 
260349cc55cSDimitry Andric   ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
261349cc55cSDimitry Andric   if (!ErrorNode)
262349cc55cSDimitry Andric     return;
263349cc55cSDimitry Andric 
264349cc55cSDimitry Andric   auto Report = std::make_unique<PathSensitiveBugReport>(
265349cc55cSDimitry Andric       BT, "dereferencing an invalid pointer", ErrorNode);
266349cc55cSDimitry Andric   Report->markInteresting(InvalidatedSymbolicBase);
267349cc55cSDimitry Andric   C.emitReport(std::move(Report));
268349cc55cSDimitry Andric }
269349cc55cSDimitry Andric 
270349cc55cSDimitry Andric void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
271349cc55cSDimitry Andric   Mgr.registerChecker<InvalidPtrChecker>();
272349cc55cSDimitry Andric }
273349cc55cSDimitry Andric 
274349cc55cSDimitry Andric bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
275349cc55cSDimitry Andric   return true;
276349cc55cSDimitry Andric }
277