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