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