1 //== InvalidPtrChecker.cpp ------------------------------------- -*- C++ -*--=//
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
7 //===----------------------------------------------------------------------===//
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 //===----------------------------------------------------------------------===//
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"
25 using namespace clang
;
30 class InvalidPtrChecker
31 : public Checker
<check::Location
, check::BeginFunction
, check::PostCall
> {
33 // For accurate emission of NoteTags, the BugType of this checker should have
35 BugType InvalidPtrBugType
{this, "Use of invalidated pointer",
36 categories::MemoryError
};
38 void EnvpInvalidatingCall(const CallEvent
&Call
, CheckerContext
&C
) const;
40 using HandlerFn
= void (InvalidPtrChecker::*)(const CallEvent
&Call
,
41 CheckerContext
&C
) const;
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;
50 // GetEnv can be treated invalidating and non-invalidating as well.
51 const CallDescription GetEnvCall
{{"getenv"}, 1};
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
},
61 void postPreviousReturnInvalidatingCall(const CallEvent
&Call
,
62 CheckerContext
&C
) const;
65 const CallDescriptionMap
<HandlerFn
> PreviousCallInvalidatingFunctions
= {
67 &InvalidPtrChecker::postPreviousReturnInvalidatingCall
},
69 &InvalidPtrChecker::postPreviousReturnInvalidatingCall
},
71 &InvalidPtrChecker::postPreviousReturnInvalidatingCall
},
73 &InvalidPtrChecker::postPreviousReturnInvalidatingCall
},
76 // The private members of this checker corresponding to commandline options
77 // are set in this function.
78 friend void ento::registerInvalidPtrChecker(CheckerManager
&);
81 // Obtain the environment pointer from 'main()' (if present).
82 void checkBeginFunction(CheckerContext
&C
) const;
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;
91 // Check if invalidated region is being dereferenced.
92 void checkLocation(SVal l
, bool isLoad
, const Stmt
*S
,
93 CheckerContext
&C
) const;
96 const NoteTag
*createEnvInvalidationNote(CheckerContext
&C
,
97 ProgramStateRef State
,
98 StringRef FunctionName
) const;
103 // Set of memory regions that were invalidated
104 REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions
, const MemRegion
*)
106 // Stores the region of the environment pointer of 'main' (if present).
107 REGISTER_TRAIT_WITH_PROGRAMSTATE(MainEnvPtrRegion
, const MemRegion
*)
109 // Stores the regions of environments returned by getenv calls.
110 REGISTER_SET_WITH_PROGRAMSTATE(GetenvEnvPtrRegions
, const MemRegion
*)
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
114 REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap
, const FunctionDecl
*,
117 const NoteTag
*InvalidPtrChecker::createEnvInvalidationNote(
118 CheckerContext
&C
, ProgramStateRef State
, StringRef FunctionName
) const {
120 const MemRegion
*MainRegion
= State
->get
<MainEnvPtrRegion
>();
121 const auto GetenvRegions
= State
->get
<GetenvEnvPtrRegions
>();
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
)
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
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'");
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'");
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];
160 void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent
&Call
,
161 CheckerContext
&C
) const {
162 // This callevent invalidates all previously generated pointers to the
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
);
170 StringRef FunctionName
= Call
.getCalleeIdentifier()->getName();
171 const NoteTag
*InvalidationNote
=
172 createEnvInvalidationNote(C
, State
, FunctionName
);
174 C
.addTransition(State
, InvalidationNote
);
177 void InvalidPtrChecker::postPreviousReturnInvalidatingCall(
178 const CallEvent
&Call
, CheckerContext
&C
) const {
179 ProgramStateRef State
= C
.getState();
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
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
)
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);
200 const LocationContext
*LCtx
= C
.getLocationContext();
201 const auto *CE
= cast
<CallExpr
>(Call
.getOriginExpr());
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
);
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
);
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
)
218 Out
<< "previous function call was here";
221 C
.addTransition(State
, Node
, PreviousCallNote
);
224 // TODO: This seems really ugly. Simplify this.
225 static const MemRegion
*findInvalidatedSymbolicBase(ProgramStateRef State
,
226 const MemRegion
*Reg
) {
228 if (State
->contains
<InvalidMemoryRegions
>(Reg
))
230 const auto *SymBase
= Reg
->getSymbolicBase();
233 const auto *SRV
= dyn_cast
<SymbolRegionValue
>(SymBase
->getSymbol());
236 Reg
= SRV
->getRegion();
237 if (const auto *VarReg
= dyn_cast
<VarRegion
>(SRV
->getRegion()))
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.
246 void InvalidPtrChecker::checkPostCall(const CallEvent
&Call
,
247 CheckerContext
&C
) const {
249 ProgramStateRef State
= C
.getState();
251 // Model 'getenv' calls
252 if (GetEnvCall
.matches(Call
)) {
253 const MemRegion
*Region
= Call
.getReturnValue().getAsRegion();
255 State
= State
->add
<GetenvEnvPtrRegions
>(Region
);
256 C
.addTransition(State
);
260 // Check if function invalidates 'envp' argument of 'main'
261 if (const auto *Handler
= EnvpInvalidatingFunctions
.lookup(Call
))
262 (this->**Handler
)(Call
, C
);
264 // Check if function invalidates the result of previous call
265 if (const auto *Handler
= PreviousCallInvalidatingFunctions
.lookup(Call
))
266 (this->**Handler
)(Call
, C
);
268 // If pedantic mode is on, regard 'getenv' calls invalidating as well
269 if (InvalidatingGetEnv
&& GetEnvCall
.matches(Call
))
270 postPreviousReturnInvalidatingCall(Call
, C
);
272 // Check if one of the arguments of the function call is invalidated
274 // If call was inlined, don't report invalidated argument
278 for (unsigned I
= 0, NumArgs
= Call
.getNumArgs(); I
< NumArgs
; ++I
) {
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();
288 SmallString
<256> Msg
;
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";
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
));
305 // Obtain the environment pointer from 'main()', if present.
306 void InvalidPtrChecker::checkBeginFunction(CheckerContext
&C
) const {
310 const auto *FD
= dyn_cast
<FunctionDecl
>(C
.getLocationContext()->getDecl());
311 if (!FD
|| FD
->param_size() != 3 || !FD
->isMain())
314 ProgramStateRef State
= C
.getState();
315 const MemRegion
*EnvpReg
=
316 State
->getRegion(FD
->parameters()[2], C
.getLocationContext());
318 // Save the memory region pointed by the environment pointer parameter of
320 C
.addTransition(State
->set
<MainEnvPtrRegion
>(EnvpReg
));
323 // Check if invalidated region is being dereferenced.
324 void InvalidPtrChecker::checkLocation(SVal Loc
, bool isLoad
, const Stmt
*S
,
325 CheckerContext
&C
) const {
326 ProgramStateRef State
= C
.getState();
328 // Ignore memory operations involving 'non-invalidated' locations.
329 const MemRegion
*InvalidatedSymbolicBase
=
330 findInvalidatedSymbolicBase(State
, Loc
.getAsRegion());
331 if (!InvalidatedSymbolicBase
)
334 ExplodedNode
*ErrorNode
= C
.generateNonFatalErrorNode();
338 auto Report
= std::make_unique
<PathSensitiveBugReport
>(
339 InvalidPtrBugType
, "dereferencing an invalid pointer", ErrorNode
);
340 Report
->markInteresting(InvalidatedSymbolicBase
);
341 C
.emitReport(std::move(Report
));
344 void ento::registerInvalidPtrChecker(CheckerManager
&Mgr
) {
345 auto *Checker
= Mgr
.registerChecker
<InvalidPtrChecker
>();
346 Checker
->InvalidatingGetEnv
=
347 Mgr
.getAnalyzerOptions().getCheckerBooleanOption(Checker
,
348 "InvalidatingGetEnv");
351 bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager
&) {