1811b1736SZurab Tsinadze //== InvalidPtrChecker.cpp ------------------------------------- -*- C++ -*--=//
2811b1736SZurab Tsinadze //
3811b1736SZurab Tsinadze // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4811b1736SZurab Tsinadze // See https://llvm.org/LICENSE.txt for license information.
5811b1736SZurab Tsinadze // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6811b1736SZurab Tsinadze //
7811b1736SZurab Tsinadze //===----------------------------------------------------------------------===//
8811b1736SZurab Tsinadze //
9811b1736SZurab Tsinadze // This file defines InvalidPtrChecker which finds usages of possibly
10811b1736SZurab Tsinadze // invalidated pointer.
11811b1736SZurab Tsinadze // CERT SEI Rules ENV31-C and ENV34-C
12811b1736SZurab Tsinadze // For more information see:
13811b1736SZurab Tsinadze // https://wiki.sei.cmu.edu/confluence/x/8tYxBQ
14811b1736SZurab Tsinadze // https://wiki.sei.cmu.edu/confluence/x/5NUxBQ
15811b1736SZurab Tsinadze //===----------------------------------------------------------------------===//
16811b1736SZurab Tsinadze 
17811b1736SZurab Tsinadze #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18811b1736SZurab Tsinadze #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19811b1736SZurab Tsinadze #include "clang/StaticAnalyzer/Core/Checker.h"
20811b1736SZurab Tsinadze #include "clang/StaticAnalyzer/Core/CheckerManager.h"
210b9d3a6eSBalazs Benics #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
22811b1736SZurab Tsinadze #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
23811b1736SZurab Tsinadze #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24811b1736SZurab Tsinadze 
25811b1736SZurab Tsinadze using namespace clang;
26811b1736SZurab Tsinadze using namespace ento;
27811b1736SZurab Tsinadze 
28811b1736SZurab Tsinadze namespace {
29811b1736SZurab Tsinadze 
30811b1736SZurab Tsinadze class InvalidPtrChecker
31811b1736SZurab Tsinadze     : public Checker<check::Location, check::BeginFunction, check::PostCall> {
32811b1736SZurab Tsinadze private:
33811b1736SZurab Tsinadze   BugType BT{this, "Use of invalidated pointer", categories::MemoryError};
34811b1736SZurab Tsinadze 
35811b1736SZurab Tsinadze   void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const;
36811b1736SZurab Tsinadze 
37811b1736SZurab Tsinadze   using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call,
38811b1736SZurab Tsinadze                                                 CheckerContext &C) const;
39811b1736SZurab Tsinadze 
40811b1736SZurab Tsinadze   // SEI CERT ENV31-C
41811b1736SZurab Tsinadze   const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = {
42811b1736SZurab Tsinadze       {{"setenv", 3}, &InvalidPtrChecker::EnvpInvalidatingCall},
43811b1736SZurab Tsinadze       {{"unsetenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
44811b1736SZurab Tsinadze       {{"putenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
45811b1736SZurab Tsinadze       {{"_putenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
46811b1736SZurab Tsinadze       {{"_wputenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
47811b1736SZurab Tsinadze   };
48811b1736SZurab Tsinadze 
49811b1736SZurab Tsinadze   void postPreviousReturnInvalidatingCall(const CallEvent &Call,
50811b1736SZurab Tsinadze                                           CheckerContext &C) const;
51811b1736SZurab Tsinadze 
52811b1736SZurab Tsinadze   // SEI CERT ENV34-C
53811b1736SZurab Tsinadze   const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = {
54811b1736SZurab Tsinadze       {{"getenv", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
55811b1736SZurab Tsinadze       {{"setlocale", 2},
56811b1736SZurab Tsinadze        &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
57811b1736SZurab Tsinadze       {{"strerror", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
58811b1736SZurab Tsinadze       {{"localeconv", 0},
59811b1736SZurab Tsinadze        &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
60811b1736SZurab Tsinadze       {{"asctime", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
61811b1736SZurab Tsinadze   };
62811b1736SZurab Tsinadze 
63811b1736SZurab Tsinadze public:
64811b1736SZurab Tsinadze   // Obtain the environment pointer from 'main()' (if present).
65811b1736SZurab Tsinadze   void checkBeginFunction(CheckerContext &C) const;
66811b1736SZurab Tsinadze 
67811b1736SZurab Tsinadze   // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
68811b1736SZurab Tsinadze   // pointer from 'main()'
69811b1736SZurab Tsinadze   // Handle functions in PreviousCallInvalidatingFunctions.
70811b1736SZurab Tsinadze   // Also, check if invalidated region is passed to a
71811b1736SZurab Tsinadze   // conservatively evaluated function call as an argument.
72811b1736SZurab Tsinadze   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
73811b1736SZurab Tsinadze 
74811b1736SZurab Tsinadze   // Check if invalidated region is being dereferenced.
75811b1736SZurab Tsinadze   void checkLocation(SVal l, bool isLoad, const Stmt *S,
76811b1736SZurab Tsinadze                      CheckerContext &C) const;
77811b1736SZurab Tsinadze };
78811b1736SZurab Tsinadze 
79811b1736SZurab Tsinadze } // namespace
80811b1736SZurab Tsinadze 
81811b1736SZurab Tsinadze // Set of memory regions that were invalidated
REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions,const MemRegion *)82811b1736SZurab Tsinadze REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)
83811b1736SZurab Tsinadze 
84811b1736SZurab Tsinadze // Stores the region of the environment pointer of 'main' (if present).
85*33ca5a44SBalazs Benics REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const MemRegion *)
86811b1736SZurab Tsinadze 
87811b1736SZurab Tsinadze // Stores key-value pairs, where key is function declaration and value is
88811b1736SZurab Tsinadze // pointer to memory region returned by previous call of this function
89811b1736SZurab Tsinadze REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *,
90811b1736SZurab Tsinadze                                const MemRegion *)
91811b1736SZurab Tsinadze 
92811b1736SZurab Tsinadze void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call,
93811b1736SZurab Tsinadze                                              CheckerContext &C) const {
94811b1736SZurab Tsinadze   StringRef FunctionName = Call.getCalleeIdentifier()->getName();
95811b1736SZurab Tsinadze   ProgramStateRef State = C.getState();
96*33ca5a44SBalazs Benics   const MemRegion *SymbolicEnvPtrRegion = State->get<EnvPtrRegion>();
97*33ca5a44SBalazs Benics   if (!SymbolicEnvPtrRegion)
98811b1736SZurab Tsinadze     return;
99811b1736SZurab Tsinadze 
100811b1736SZurab Tsinadze   State = State->add<InvalidMemoryRegions>(SymbolicEnvPtrRegion);
101811b1736SZurab Tsinadze 
102811b1736SZurab Tsinadze   const NoteTag *Note =
103811b1736SZurab Tsinadze       C.getNoteTag([SymbolicEnvPtrRegion, FunctionName](
104811b1736SZurab Tsinadze                        PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
105811b1736SZurab Tsinadze         if (!BR.isInteresting(SymbolicEnvPtrRegion))
106811b1736SZurab Tsinadze           return;
107811b1736SZurab Tsinadze         Out << '\'' << FunctionName
108811b1736SZurab Tsinadze             << "' call may invalidate the environment parameter of 'main'";
109811b1736SZurab Tsinadze       });
110811b1736SZurab Tsinadze 
111811b1736SZurab Tsinadze   C.addTransition(State, Note);
112811b1736SZurab Tsinadze }
113811b1736SZurab Tsinadze 
postPreviousReturnInvalidatingCall(const CallEvent & Call,CheckerContext & C) const114811b1736SZurab Tsinadze void InvalidPtrChecker::postPreviousReturnInvalidatingCall(
115811b1736SZurab Tsinadze     const CallEvent &Call, CheckerContext &C) const {
116811b1736SZurab Tsinadze   ProgramStateRef State = C.getState();
117811b1736SZurab Tsinadze 
118811b1736SZurab Tsinadze   const NoteTag *Note = nullptr;
119811b1736SZurab Tsinadze   const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
120811b1736SZurab Tsinadze   // Invalidate the region of the previously returned pointer - if there was
121811b1736SZurab Tsinadze   // one.
122811b1736SZurab Tsinadze   if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {
123811b1736SZurab Tsinadze     const MemRegion *PrevReg = *Reg;
124811b1736SZurab Tsinadze     State = State->add<InvalidMemoryRegions>(PrevReg);
125811b1736SZurab Tsinadze     Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR,
126811b1736SZurab Tsinadze                                       llvm::raw_ostream &Out) {
127811b1736SZurab Tsinadze       if (!BR.isInteresting(PrevReg))
128811b1736SZurab Tsinadze         return;
129811b1736SZurab Tsinadze       Out << '\'';
130811b1736SZurab Tsinadze       FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
13187a55137SBrian Tracy       Out << "' call may invalidate the result of the previous " << '\'';
132811b1736SZurab Tsinadze       FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
133811b1736SZurab Tsinadze       Out << '\'';
134811b1736SZurab Tsinadze     });
135811b1736SZurab Tsinadze   }
136811b1736SZurab Tsinadze 
137811b1736SZurab Tsinadze   const LocationContext *LCtx = C.getLocationContext();
138811b1736SZurab Tsinadze   const auto *CE = cast<CallExpr>(Call.getOriginExpr());
139811b1736SZurab Tsinadze 
140811b1736SZurab Tsinadze   // Function call will return a pointer to the new symbolic region.
141811b1736SZurab Tsinadze   DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal(
142811b1736SZurab Tsinadze       CE, LCtx, CE->getType(), C.blockCount());
143811b1736SZurab Tsinadze   State = State->BindExpr(CE, LCtx, RetVal);
144811b1736SZurab Tsinadze 
145811b1736SZurab Tsinadze   // Remember to this region.
1467562f3dfSSimon Pilgrim   const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion());
147811b1736SZurab Tsinadze   const MemRegion *MR =
148811b1736SZurab Tsinadze       const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion());
149811b1736SZurab Tsinadze   State = State->set<PreviousCallResultMap>(FD, MR);
150811b1736SZurab Tsinadze 
151811b1736SZurab Tsinadze   ExplodedNode *Node = C.addTransition(State, Note);
152811b1736SZurab Tsinadze   const NoteTag *PreviousCallNote =
153811b1736SZurab Tsinadze       C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
154811b1736SZurab Tsinadze         if (!BR.isInteresting(MR))
155811b1736SZurab Tsinadze           return;
156811b1736SZurab Tsinadze         Out << '\'' << "'previous function call was here" << '\'';
157811b1736SZurab Tsinadze       });
158811b1736SZurab Tsinadze 
159811b1736SZurab Tsinadze   C.addTransition(State, Node, PreviousCallNote);
160811b1736SZurab Tsinadze }
161811b1736SZurab Tsinadze 
162811b1736SZurab Tsinadze // TODO: This seems really ugly. Simplify this.
findInvalidatedSymbolicBase(ProgramStateRef State,const MemRegion * Reg)163811b1736SZurab Tsinadze static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State,
164811b1736SZurab Tsinadze                                                     const MemRegion *Reg) {
165811b1736SZurab Tsinadze   while (Reg) {
166811b1736SZurab Tsinadze     if (State->contains<InvalidMemoryRegions>(Reg))
167811b1736SZurab Tsinadze       return Reg;
168811b1736SZurab Tsinadze     const auto *SymBase = Reg->getSymbolicBase();
169811b1736SZurab Tsinadze     if (!SymBase)
170811b1736SZurab Tsinadze       break;
171811b1736SZurab Tsinadze     const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());
172811b1736SZurab Tsinadze     if (!SRV)
173811b1736SZurab Tsinadze       break;
174811b1736SZurab Tsinadze     Reg = SRV->getRegion();
175811b1736SZurab Tsinadze     if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion()))
176811b1736SZurab Tsinadze       Reg = VarReg;
177811b1736SZurab Tsinadze   }
178811b1736SZurab Tsinadze   return nullptr;
179811b1736SZurab Tsinadze }
180811b1736SZurab Tsinadze 
181811b1736SZurab Tsinadze // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
182811b1736SZurab Tsinadze // pointer from 'main()' Also, check if invalidated region is passed to a
183811b1736SZurab Tsinadze // function call as an argument.
checkPostCall(const CallEvent & Call,CheckerContext & C) const184811b1736SZurab Tsinadze void InvalidPtrChecker::checkPostCall(const CallEvent &Call,
185811b1736SZurab Tsinadze                                       CheckerContext &C) const {
186811b1736SZurab Tsinadze   // Check if function invalidates 'envp' argument of 'main'
187811b1736SZurab Tsinadze   if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call))
188811b1736SZurab Tsinadze     (this->**Handler)(Call, C);
189811b1736SZurab Tsinadze 
190811b1736SZurab Tsinadze   // Check if function invalidates the result of previous call
191811b1736SZurab Tsinadze   if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))
192811b1736SZurab Tsinadze     (this->**Handler)(Call, C);
193811b1736SZurab Tsinadze 
194811b1736SZurab Tsinadze   // Check if one of the arguments of the function call is invalidated
195811b1736SZurab Tsinadze 
196811b1736SZurab Tsinadze   // If call was inlined, don't report invalidated argument
197811b1736SZurab Tsinadze   if (C.wasInlined)
198811b1736SZurab Tsinadze     return;
199811b1736SZurab Tsinadze 
200811b1736SZurab Tsinadze   ProgramStateRef State = C.getState();
201811b1736SZurab Tsinadze 
202811b1736SZurab Tsinadze   for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) {
203811b1736SZurab Tsinadze 
204811b1736SZurab Tsinadze     if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(
205811b1736SZurab Tsinadze             Call.getArgSVal(I).getAsRegion())) {
206811b1736SZurab Tsinadze       if (const MemRegion *InvalidatedSymbolicBase =
207811b1736SZurab Tsinadze               findInvalidatedSymbolicBase(State, SR)) {
208811b1736SZurab Tsinadze         ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
209811b1736SZurab Tsinadze         if (!ErrorNode)
210811b1736SZurab Tsinadze           return;
211811b1736SZurab Tsinadze 
212811b1736SZurab Tsinadze         SmallString<256> Msg;
213811b1736SZurab Tsinadze         llvm::raw_svector_ostream Out(Msg);
214811b1736SZurab Tsinadze         Out << "use of invalidated pointer '";
215811b1736SZurab Tsinadze         Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr,
216811b1736SZurab Tsinadze                                         C.getASTContext().getPrintingPolicy());
217811b1736SZurab Tsinadze         Out << "' in a function call";
218811b1736SZurab Tsinadze 
219811b1736SZurab Tsinadze         auto Report =
220811b1736SZurab Tsinadze             std::make_unique<PathSensitiveBugReport>(BT, Out.str(), ErrorNode);
221811b1736SZurab Tsinadze         Report->markInteresting(InvalidatedSymbolicBase);
222811b1736SZurab Tsinadze         Report->addRange(Call.getArgSourceRange(I));
223811b1736SZurab Tsinadze         C.emitReport(std::move(Report));
224811b1736SZurab Tsinadze       }
225811b1736SZurab Tsinadze     }
226811b1736SZurab Tsinadze   }
227811b1736SZurab Tsinadze }
228811b1736SZurab Tsinadze 
229811b1736SZurab Tsinadze // Obtain the environment pointer from 'main()', if present.
checkBeginFunction(CheckerContext & C) const230811b1736SZurab Tsinadze void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const {
231811b1736SZurab Tsinadze   if (!C.inTopFrame())
232811b1736SZurab Tsinadze     return;
233811b1736SZurab Tsinadze 
234811b1736SZurab Tsinadze   const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl());
235811b1736SZurab Tsinadze   if (!FD || FD->param_size() != 3 || !FD->isMain())
236811b1736SZurab Tsinadze     return;
237811b1736SZurab Tsinadze 
238811b1736SZurab Tsinadze   ProgramStateRef State = C.getState();
239811b1736SZurab Tsinadze   const MemRegion *EnvpReg =
240811b1736SZurab Tsinadze       State->getRegion(FD->parameters()[2], C.getLocationContext());
241811b1736SZurab Tsinadze 
242811b1736SZurab Tsinadze   // Save the memory region pointed by the environment pointer parameter of
243811b1736SZurab Tsinadze   // 'main'.
244*33ca5a44SBalazs Benics   C.addTransition(State->set<EnvPtrRegion>(EnvpReg));
245811b1736SZurab Tsinadze }
246811b1736SZurab Tsinadze 
247811b1736SZurab Tsinadze // Check if invalidated region is being dereferenced.
checkLocation(SVal Loc,bool isLoad,const Stmt * S,CheckerContext & C) const248811b1736SZurab Tsinadze void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S,
249811b1736SZurab Tsinadze                                       CheckerContext &C) const {
250811b1736SZurab Tsinadze   ProgramStateRef State = C.getState();
251811b1736SZurab Tsinadze 
252811b1736SZurab Tsinadze   // Ignore memory operations involving 'non-invalidated' locations.
253811b1736SZurab Tsinadze   const MemRegion *InvalidatedSymbolicBase =
254811b1736SZurab Tsinadze       findInvalidatedSymbolicBase(State, Loc.getAsRegion());
255811b1736SZurab Tsinadze   if (!InvalidatedSymbolicBase)
256811b1736SZurab Tsinadze     return;
257811b1736SZurab Tsinadze 
258811b1736SZurab Tsinadze   ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
259811b1736SZurab Tsinadze   if (!ErrorNode)
260811b1736SZurab Tsinadze     return;
261811b1736SZurab Tsinadze 
262811b1736SZurab Tsinadze   auto Report = std::make_unique<PathSensitiveBugReport>(
263811b1736SZurab Tsinadze       BT, "dereferencing an invalid pointer", ErrorNode);
264811b1736SZurab Tsinadze   Report->markInteresting(InvalidatedSymbolicBase);
265811b1736SZurab Tsinadze   C.emitReport(std::move(Report));
266811b1736SZurab Tsinadze }
267811b1736SZurab Tsinadze 
registerInvalidPtrChecker(CheckerManager & Mgr)268811b1736SZurab Tsinadze void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
269811b1736SZurab Tsinadze   Mgr.registerChecker<InvalidPtrChecker>();
270811b1736SZurab Tsinadze }
271811b1736SZurab Tsinadze 
shouldRegisterInvalidPtrChecker(const CheckerManager &)272811b1736SZurab Tsinadze bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
273811b1736SZurab Tsinadze   return true;
274811b1736SZurab Tsinadze }
275