/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
Line | Count | Source (jump to first uncovered line) |
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 |
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 | 5 | CheckerContext &C) const { |
96 | 5 | StringRef FunctionName = Call.getCalleeIdentifier()->getName(); |
97 | 5 | ProgramStateRef State = C.getState(); |
98 | 5 | const MemRegion *SymbolicEnvPtrRegion = State->get<EnvPtrRegion>(); |
99 | 5 | if (!SymbolicEnvPtrRegion) |
100 | 0 | return; |
101 | | |
102 | 5 | State = State->add<InvalidMemoryRegions>(SymbolicEnvPtrRegion); |
103 | | |
104 | 5 | const NoteTag *Note = |
105 | 5 | C.getNoteTag([SymbolicEnvPtrRegion, FunctionName]( |
106 | 25 | PathSensitiveBugReport &BR, llvm::raw_ostream &Out) { |
107 | 25 | if (!BR.isInteresting(SymbolicEnvPtrRegion)) |
108 | 0 | return; |
109 | 25 | Out << '\'' << FunctionName |
110 | 25 | << "' call may invalidate the environment parameter of 'main'"; |
111 | 25 | }); |
112 | | |
113 | 5 | C.addTransition(State, Note); |
114 | 5 | } |
115 | | |
116 | | void InvalidPtrChecker::postPreviousReturnInvalidatingCall( |
117 | 54 | const CallEvent &Call, CheckerContext &C) const { |
118 | 54 | ProgramStateRef State = C.getState(); |
119 | | |
120 | 54 | const NoteTag *Note = nullptr; |
121 | 54 | 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 | 54 | if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) { |
125 | 32 | const MemRegion *PrevReg = *Reg; |
126 | 32 | State = State->add<InvalidMemoryRegions>(PrevReg); |
127 | 32 | Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR, |
128 | 32 | llvm::raw_ostream &Out) { |
129 | 29 | if (!BR.isInteresting(PrevReg)) |
130 | 11 | return; |
131 | 18 | Out << '\''; |
132 | 18 | FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true); |
133 | 18 | Out << "' call may invalidate the result of the previous " << '\''; |
134 | 18 | FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true); |
135 | 18 | Out << '\''; |
136 | 18 | }); |
137 | 32 | } |
138 | | |
139 | 54 | const LocationContext *LCtx = C.getLocationContext(); |
140 | 54 | const auto *CE = cast<CallExpr>(Call.getOriginExpr()); |
141 | | |
142 | | // Function call will return a pointer to the new symbolic region. |
143 | 54 | DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal( |
144 | 54 | CE, LCtx, CE->getType(), C.blockCount()); |
145 | 54 | State = State->BindExpr(CE, LCtx, RetVal); |
146 | | |
147 | | // Remember to this region. |
148 | 54 | const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion()); |
149 | 54 | const MemRegion *MR = |
150 | 54 | const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion()); |
151 | 54 | State = State->set<PreviousCallResultMap>(FD, MR); |
152 | | |
153 | 54 | ExplodedNode *Node = C.addTransition(State, Note); |
154 | 54 | const NoteTag *PreviousCallNote = |
155 | 54 | C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) { |
156 | 47 | if (!BR.isInteresting(MR)) |
157 | 29 | return; |
158 | 18 | Out << '\'' << "'previous function call was here" << '\''; |
159 | 18 | }); |
160 | | |
161 | 54 | C.addTransition(State, Node, PreviousCallNote); |
162 | 54 | } |
163 | | |
164 | | // TODO: This seems really ugly. Simplify this. |
165 | | static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State, |
166 | 265 | const MemRegion *Reg) { |
167 | 300 | while (Reg) { |
168 | 300 | if (State->contains<InvalidMemoryRegions>(Reg)) |
169 | 43 | return Reg; |
170 | 257 | const auto *SymBase = Reg->getSymbolicBase(); |
171 | 257 | if (!SymBase) |
172 | 187 | break; |
173 | 70 | const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol()); |
174 | 70 | if (!SRV) |
175 | 35 | break; |
176 | 35 | Reg = SRV->getRegion(); |
177 | 35 | if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion())) |
178 | 35 | Reg = VarReg; |
179 | 35 | } |
180 | 222 | return nullptr; |
181 | 265 | } |
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. |
186 | | void InvalidPtrChecker::checkPostCall(const CallEvent &Call, |
187 | 97 | CheckerContext &C) const { |
188 | | // Check if function invalidates 'envp' argument of 'main' |
189 | 97 | if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call)) |
190 | 5 | (this->**Handler)(Call, C); |
191 | | |
192 | | // Check if function invalidates the result of previous call |
193 | 97 | if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call)) |
194 | 54 | (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 | 97 | if (C.wasInlined) |
200 | 11 | return; |
201 | | |
202 | 86 | ProgramStateRef State = C.getState(); |
203 | | |
204 | 183 | for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I97 ) { |
205 | | |
206 | 97 | if (const auto *SR = dyn_cast_or_null<SymbolicRegion>( |
207 | 97 | Call.getArgSVal(I).getAsRegion())) { |
208 | 20 | if (const MemRegion *InvalidatedSymbolicBase = |
209 | 20 | findInvalidatedSymbolicBase(State, SR)) { |
210 | 8 | ExplodedNode *ErrorNode = C.generateNonFatalErrorNode(); |
211 | 8 | if (!ErrorNode) |
212 | 0 | return; |
213 | | |
214 | 8 | SmallString<256> Msg; |
215 | 8 | llvm::raw_svector_ostream Out(Msg); |
216 | 8 | Out << "use of invalidated pointer '"; |
217 | 8 | Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr, |
218 | 8 | C.getASTContext().getPrintingPolicy()); |
219 | 8 | Out << "' in a function call"; |
220 | | |
221 | 8 | auto Report = |
222 | 8 | std::make_unique<PathSensitiveBugReport>(BT, Out.str(), ErrorNode); |
223 | 8 | Report->markInteresting(InvalidatedSymbolicBase); |
224 | 8 | Report->addRange(Call.getArgSourceRange(I)); |
225 | 8 | C.emitReport(std::move(Report)); |
226 | 8 | } |
227 | 20 | } |
228 | 97 | } |
229 | 86 | } |
230 | | |
231 | | // Obtain the environment pointer from 'main()', if present. |
232 | 38 | void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const { |
233 | 38 | if (!C.inTopFrame()) |
234 | 11 | return; |
235 | | |
236 | 27 | const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl()); |
237 | 27 | if (!FD || FD->param_size() != 3 || !FD->isMain()5 ) |
238 | 22 | return; |
239 | | |
240 | 5 | ProgramStateRef State = C.getState(); |
241 | 5 | const MemRegion *EnvpReg = |
242 | 5 | State->getRegion(FD->parameters()[2], C.getLocationContext()); |
243 | | |
244 | | // Save the memory region pointed by the environment pointer parameter of |
245 | | // 'main'. |
246 | 5 | C.addTransition(State->set<EnvPtrRegion>(EnvpReg)); |
247 | 5 | } |
248 | | |
249 | | // Check if invalidated region is being dereferenced. |
250 | | void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S, |
251 | 245 | CheckerContext &C) const { |
252 | 245 | ProgramStateRef State = C.getState(); |
253 | | |
254 | | // Ignore memory operations involving 'non-invalidated' locations. |
255 | 245 | const MemRegion *InvalidatedSymbolicBase = |
256 | 245 | findInvalidatedSymbolicBase(State, Loc.getAsRegion()); |
257 | 245 | if (!InvalidatedSymbolicBase) |
258 | 210 | return; |
259 | | |
260 | 35 | ExplodedNode *ErrorNode = C.generateNonFatalErrorNode(); |
261 | 35 | if (!ErrorNode) |
262 | 0 | return; |
263 | | |
264 | 35 | auto Report = std::make_unique<PathSensitiveBugReport>( |
265 | 35 | BT, "dereferencing an invalid pointer", ErrorNode); |
266 | 35 | Report->markInteresting(InvalidatedSymbolicBase); |
267 | 35 | C.emitReport(std::move(Report)); |
268 | 35 | } |
269 | | |
270 | 7 | void ento::registerInvalidPtrChecker(CheckerManager &Mgr) { |
271 | 7 | Mgr.registerChecker<InvalidPtrChecker>(); |
272 | 7 | } |
273 | | |
274 | 14 | bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) { |
275 | 14 | return true; |
276 | 14 | } |