clang 18.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 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
65public:
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
84REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)
85
86// Stores the region of the environment pointer of 'main' (if present).
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
91REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *,
92 const MemRegion *)
93
94void 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
116void 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.
165static 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.
186void 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
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.
232void 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.
250void 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
270void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
271 Mgr.registerChecker<InvalidPtrChecker>();
272}
273
274bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
275 return true;
276}
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:761
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:429
Represents a function declaration or definition.
Definition: Decl.h:1919
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:3054
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
Stmt - This represents one statement.
Definition: Stmt.h:72
An immutable map from CallDescriptions to arbitrary data.
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:153
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:1393
The tag upon which the TagVisitor reacts.
Definition: BugReporter.h:764
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:73
const MemRegion * getAsRegion() const
Definition: SVals.cpp:120
bool Call(InterpState &S, CodePtr OpPC, const Function *Func)
Definition: Interp.h:1723
@ C
Languages that the frontend can parse and compile.