clang 19.0.0git
InvalidPtrChecker.cpp
Go to the documentation of this file.
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
24
25using namespace clang;
26using namespace ento;
27
28namespace {
29
30class InvalidPtrChecker
31 : public Checker<check::Location, check::BeginFunction, check::PostCall> {
32private:
33 // For accurate emission of NoteTags, the BugType of this checker should have
34 // a unique address.
35 BugType InvalidPtrBugType{this, "Use of invalidated pointer",
37
38 void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const;
39
40 using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call,
41 CheckerContext &C) const;
42
43 // SEI CERT ENV31-C
44
45 // If set to true, consider getenv calls as invalidating operations on the
46 // environment variable buffer. This is implied in the standard, but in
47 // practice does not cause problems (in the commonly used environments).
48 bool InvalidatingGetEnv = false;
49
50 // GetEnv can be treated invalidating and non-invalidating as well.
51 const CallDescription GetEnvCall{{"getenv"}, 1};
52
53 const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = {
54 {{{"setenv"}, 3}, &InvalidPtrChecker::EnvpInvalidatingCall},
55 {{{"unsetenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
56 {{{"putenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
57 {{{"_putenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
58 {{{"_wputenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
59 };
60
61 void postPreviousReturnInvalidatingCall(const CallEvent &Call,
62 CheckerContext &C) const;
63
64 // SEI CERT ENV34-C
65 const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = {
66 {{{"setlocale"}, 2},
67 &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
68 {{{"strerror"}, 1},
69 &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
70 {{{"localeconv"}, 0},
71 &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
72 {{{"asctime"}, 1},
73 &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
74 };
75
76 // The private members of this checker corresponding to commandline options
77 // are set in this function.
78 friend void ento::registerInvalidPtrChecker(CheckerManager &);
79
80public:
81 // Obtain the environment pointer from 'main()' (if present).
82 void checkBeginFunction(CheckerContext &C) const;
83
84 // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
85 // pointer from 'main()'
86 // Handle functions in PreviousCallInvalidatingFunctions.
87 // Also, check if invalidated region is passed to a
88 // conservatively evaluated function call as an argument.
89 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
90
91 // Check if invalidated region is being dereferenced.
92 void checkLocation(SVal l, bool isLoad, const Stmt *S,
93 CheckerContext &C) const;
94
95private:
96 const NoteTag *createEnvInvalidationNote(CheckerContext &C,
97 ProgramStateRef State,
98 StringRef FunctionName) const;
99};
100
101} // namespace
102
103// Set of memory regions that were invalidated
104REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)
105
106// Stores the region of the environment pointer of 'main' (if present).
107REGISTER_TRAIT_WITH_PROGRAMSTATE(MainEnvPtrRegion, const MemRegion *)
108
109// Stores the regions of environments returned by getenv calls.
110REGISTER_SET_WITH_PROGRAMSTATE(GetenvEnvPtrRegions, const MemRegion *)
111
112// Stores key-value pairs, where key is function declaration and value is
113// pointer to memory region returned by previous call of this function
114REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *,
115 const MemRegion *)
116
117const NoteTag *InvalidPtrChecker::createEnvInvalidationNote(
118 CheckerContext &C, ProgramStateRef State, StringRef FunctionName) const {
119
120 const MemRegion *MainRegion = State->get<MainEnvPtrRegion>();
121 const auto GetenvRegions = State->get<GetenvEnvPtrRegions>();
122
123 return C.getNoteTag([this, MainRegion, GetenvRegions,
124 FunctionName = std::string{FunctionName}](
125 PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
126 // Only handle the BugType of this checker.
127 if (&BR.getBugType() != &InvalidPtrBugType)
128 return;
129
130 // Mark all regions that were interesting before as NOT interesting now
131 // to avoid extra notes coming from invalidation points higher up the
132 // bugpath. This ensures that only the last invalidation point is marked
133 // with a note tag.
134 llvm::SmallVector<std::string, 2> InvalidLocationNames;
135 if (BR.isInteresting(MainRegion)) {
136 BR.markNotInteresting(MainRegion);
137 InvalidLocationNames.push_back("the environment parameter of 'main'");
138 }
139 bool InterestingGetenvFound = false;
140 for (const MemRegion *MR : GetenvRegions) {
141 if (BR.isInteresting(MR)) {
142 BR.markNotInteresting(MR);
143 if (!InterestingGetenvFound) {
144 InterestingGetenvFound = true;
145 InvalidLocationNames.push_back(
146 "the environment returned by 'getenv'");
147 }
148 }
149 }
150
151 // Emit note tag message.
152 if (InvalidLocationNames.size() >= 1)
153 Out << '\'' << FunctionName << "' call may invalidate "
154 << InvalidLocationNames[0];
155 if (InvalidLocationNames.size() == 2)
156 Out << ", and " << InvalidLocationNames[1];
157 });
158}
159
160void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call,
161 CheckerContext &C) const {
162 // This callevent invalidates all previously generated pointers to the
163 // environment.
164 ProgramStateRef State = C.getState();
165 if (const MemRegion *MainEnvPtr = State->get<MainEnvPtrRegion>())
166 State = State->add<InvalidMemoryRegions>(MainEnvPtr);
167 for (const MemRegion *EnvPtr : State->get<GetenvEnvPtrRegions>())
168 State = State->add<InvalidMemoryRegions>(EnvPtr);
169
170 StringRef FunctionName = Call.getCalleeIdentifier()->getName();
171 const NoteTag *InvalidationNote =
172 createEnvInvalidationNote(C, State, FunctionName);
173
174 C.addTransition(State, InvalidationNote);
175}
176
177void InvalidPtrChecker::postPreviousReturnInvalidatingCall(
178 const CallEvent &Call, CheckerContext &C) const {
179 ProgramStateRef State = C.getState();
180
181 const NoteTag *Note = nullptr;
182 const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
183 // Invalidate the region of the previously returned pointer - if there was
184 // one.
185 if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {
186 const MemRegion *PrevReg = *Reg;
187 State = State->add<InvalidMemoryRegions>(PrevReg);
188 Note = C.getNoteTag([this, PrevReg, FD](PathSensitiveBugReport &BR,
189 llvm::raw_ostream &Out) {
190 if (!BR.isInteresting(PrevReg) || &BR.getBugType() != &InvalidPtrBugType)
191 return;
192 Out << '\'';
193 FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
194 Out << "' call may invalidate the result of the previous " << '\'';
195 FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
196 Out << '\'';
197 });
198 }
199
200 const LocationContext *LCtx = C.getLocationContext();
201 const auto *CE = cast<CallExpr>(Call.getOriginExpr());
202
203 // Function call will return a pointer to the new symbolic region.
204 DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal(
205 CE, LCtx, CE->getType(), C.blockCount());
206 State = State->BindExpr(CE, LCtx, RetVal);
207
208 // Remember to this region.
209 const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion());
210 const MemRegion *MR = SymRegOfRetVal->getBaseRegion();
211 State = State->set<PreviousCallResultMap>(FD, MR);
212
213 ExplodedNode *Node = C.addTransition(State, Note);
214 const NoteTag *PreviousCallNote = C.getNoteTag(
215 [this, MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
216 if (!BR.isInteresting(MR) || &BR.getBugType() != &InvalidPtrBugType)
217 return;
218 Out << "previous function call was here";
219 });
220
221 C.addTransition(State, Node, PreviousCallNote);
222}
223
224// TODO: This seems really ugly. Simplify this.
225static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State,
226 const MemRegion *Reg) {
227 while (Reg) {
228 if (State->contains<InvalidMemoryRegions>(Reg))
229 return Reg;
230 const auto *SymBase = Reg->getSymbolicBase();
231 if (!SymBase)
232 break;
233 const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());
234 if (!SRV)
235 break;
236 Reg = SRV->getRegion();
237 if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion()))
238 Reg = VarReg;
239 }
240 return nullptr;
241}
242
243// Handle functions in EnvpInvalidatingFunctions, that invalidate environment
244// pointer from 'main()' Also, check if invalidated region is passed to a
245// function call as an argument.
246void InvalidPtrChecker::checkPostCall(const CallEvent &Call,
247 CheckerContext &C) const {
248
249 ProgramStateRef State = C.getState();
250
251 // Model 'getenv' calls
252 if (GetEnvCall.matches(Call)) {
253 const MemRegion *Region = Call.getReturnValue().getAsRegion();
254 if (Region) {
255 State = State->add<GetenvEnvPtrRegions>(Region);
256 C.addTransition(State);
257 }
258 }
259
260 // Check if function invalidates 'envp' argument of 'main'
261 if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call))
262 (this->**Handler)(Call, C);
263
264 // Check if function invalidates the result of previous call
265 if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))
266 (this->**Handler)(Call, C);
267
268 // If pedantic mode is on, regard 'getenv' calls invalidating as well
269 if (InvalidatingGetEnv && GetEnvCall.matches(Call))
270 postPreviousReturnInvalidatingCall(Call, C);
271
272 // Check if one of the arguments of the function call is invalidated
273
274 // If call was inlined, don't report invalidated argument
275 if (C.wasInlined)
276 return;
277
278 for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) {
279
280 if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(
281 Call.getArgSVal(I).getAsRegion())) {
282 if (const MemRegion *InvalidatedSymbolicBase =
283 findInvalidatedSymbolicBase(State, SR)) {
284 ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
285 if (!ErrorNode)
286 return;
287
289 llvm::raw_svector_ostream Out(Msg);
290 Out << "use of invalidated pointer '";
291 Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr,
292 C.getASTContext().getPrintingPolicy());
293 Out << "' in a function call";
294
295 auto Report = std::make_unique<PathSensitiveBugReport>(
296 InvalidPtrBugType, Out.str(), ErrorNode);
297 Report->markInteresting(InvalidatedSymbolicBase);
298 Report->addRange(Call.getArgSourceRange(I));
299 C.emitReport(std::move(Report));
300 }
301 }
302 }
303}
304
305// Obtain the environment pointer from 'main()', if present.
306void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const {
307 if (!C.inTopFrame())
308 return;
309
310 const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl());
311 if (!FD || FD->param_size() != 3 || !FD->isMain())
312 return;
313
314 ProgramStateRef State = C.getState();
315 const MemRegion *EnvpReg =
316 State->getRegion(FD->parameters()[2], C.getLocationContext());
317
318 // Save the memory region pointed by the environment pointer parameter of
319 // 'main'.
320 C.addTransition(State->set<MainEnvPtrRegion>(EnvpReg));
321}
322
323// Check if invalidated region is being dereferenced.
324void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S,
325 CheckerContext &C) const {
326 ProgramStateRef State = C.getState();
327
328 // Ignore memory operations involving 'non-invalidated' locations.
329 const MemRegion *InvalidatedSymbolicBase =
330 findInvalidatedSymbolicBase(State, Loc.getAsRegion());
331 if (!InvalidatedSymbolicBase)
332 return;
333
334 ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
335 if (!ErrorNode)
336 return;
337
338 auto Report = std::make_unique<PathSensitiveBugReport>(
339 InvalidPtrBugType, "dereferencing an invalid pointer", ErrorNode);
340 Report->markInteresting(InvalidatedSymbolicBase);
341 C.emitReport(std::move(Report));
342}
343
344void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
345 auto *Checker = Mgr.registerChecker<InvalidPtrChecker>();
346 Checker->InvalidatingGetEnv =
348 "InvalidatingGetEnv");
349}
350
351bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
352 return true;
353}
DynTypedNode Node
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
#define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable set of type NameTy, suitable for placement into the ProgramState.
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a type named NameTy.
const LangOptions & getLangOpts() const
Definition: ASTContext.h:772
bool getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Interprets an option's string value as a boolean.
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:501
Represents a function declaration or definition.
Definition: Decl.h:1959
void getNameForDiagnostic(raw_ostream &OS, const PrintingPolicy &Policy, bool Qualified) const override
Appends a human-readable name for this declaration into the given stream.
Definition: Decl.cpp:3081
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
Stmt - This represents one statement.
Definition: Stmt.h:84
const BugType & getBugType() const
Definition: BugReporter.h:149
An immutable map from CallDescriptions to arbitrary data.
A CallDescription is a pattern that can be used to match calls based on the qualified name and the ar...
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:153
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:96
const SymbolicRegion * getSymbolicBase() const
If this is a symbolic region, returns the region.
Definition: MemRegion.cpp:1401
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getBaseRegion() const
Definition: MemRegion.cpp:1343
The tag upon which the TagVisitor reacts.
Definition: BugReporter.h:779
bool isInteresting(SymbolRef sym) const
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition: SVals.h:55
const MemRegion * getAsRegion() const
Definition: SVals.cpp:120
The JSON file list parser is used to communicate input to InstallAPI.