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