/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- CallAndMessageChecker.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 defines CallAndMessageChecker, a builtin checker that checks for various |
10 | | // errors of call and objc message expressions. |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | |
14 | | #include "clang/AST/ExprCXX.h" |
15 | | #include "clang/AST/ParentMap.h" |
16 | | #include "clang/Basic/TargetInfo.h" |
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 | | #include "llvm/ADT/SmallString.h" |
24 | | #include "llvm/ADT/StringExtras.h" |
25 | | #include "llvm/Support/Casting.h" |
26 | | #include "llvm/Support/raw_ostream.h" |
27 | | |
28 | | using namespace clang; |
29 | | using namespace ento; |
30 | | |
31 | | namespace { |
32 | | |
33 | | class CallAndMessageChecker |
34 | | : public Checker<check::PreObjCMessage, check::ObjCMessageNil, |
35 | | check::PreCall> { |
36 | | mutable std::unique_ptr<BugType> BT_call_null; |
37 | | mutable std::unique_ptr<BugType> BT_call_undef; |
38 | | mutable std::unique_ptr<BugType> BT_cxx_call_null; |
39 | | mutable std::unique_ptr<BugType> BT_cxx_call_undef; |
40 | | mutable std::unique_ptr<BugType> BT_call_arg; |
41 | | mutable std::unique_ptr<BugType> BT_cxx_delete_undef; |
42 | | mutable std::unique_ptr<BugType> BT_msg_undef; |
43 | | mutable std::unique_ptr<BugType> BT_objc_prop_undef; |
44 | | mutable std::unique_ptr<BugType> BT_objc_subscript_undef; |
45 | | mutable std::unique_ptr<BugType> BT_msg_arg; |
46 | | mutable std::unique_ptr<BugType> BT_msg_ret; |
47 | | mutable std::unique_ptr<BugType> BT_call_few_args; |
48 | | |
49 | | public: |
50 | | // These correspond with the checker options. Looking at other checkers such |
51 | | // as MallocChecker and CStringChecker, this is similar as to how they pull |
52 | | // off having a modeling class, but emitting diagnostics under a smaller |
53 | | // checker's name that can be safely disabled without disturbing the |
54 | | // underlaying modeling engine. |
55 | | // The reason behind having *checker options* rather then actual *checkers* |
56 | | // here is that CallAndMessage is among the oldest checkers out there, and can |
57 | | // be responsible for the majority of the reports on any given project. This |
58 | | // is obviously not ideal, but changing checker name has the consequence of |
59 | | // changing the issue hashes associated with the reports, and databases |
60 | | // relying on this (CodeChecker, for instance) would suffer greatly. |
61 | | // If we ever end up making changes to the issue hash generation algorithm, or |
62 | | // the warning messages here, we should totally jump on the opportunity to |
63 | | // convert these to actual checkers. |
64 | | enum CheckKind { |
65 | | CK_FunctionPointer, |
66 | | CK_ParameterCount, |
67 | | CK_CXXThisMethodCall, |
68 | | CK_CXXDeallocationArg, |
69 | | CK_ArgInitializedness, |
70 | | CK_ArgPointeeInitializedness, |
71 | | CK_NilReceiver, |
72 | | CK_UndefReceiver, |
73 | | CK_NumCheckKinds |
74 | | }; |
75 | | |
76 | | bool ChecksEnabled[CK_NumCheckKinds] = {false}; |
77 | | // The original core.CallAndMessage checker name. This should rather be an |
78 | | // array, as seen in MallocChecker and CStringChecker. |
79 | | CheckerNameRef OriginalName; |
80 | | |
81 | | void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; |
82 | | |
83 | | /// Fill in the return value that results from messaging nil based on the |
84 | | /// return type and architecture and diagnose if the return value will be |
85 | | /// garbage. |
86 | | void checkObjCMessageNil(const ObjCMethodCall &msg, CheckerContext &C) const; |
87 | | |
88 | | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; |
89 | | |
90 | | ProgramStateRef checkFunctionPointerCall(const CallExpr *CE, |
91 | | CheckerContext &C, |
92 | | ProgramStateRef State) const; |
93 | | |
94 | | ProgramStateRef checkCXXMethodCall(const CXXInstanceCall *CC, |
95 | | CheckerContext &C, |
96 | | ProgramStateRef State) const; |
97 | | |
98 | | ProgramStateRef checkParameterCount(const CallEvent &Call, CheckerContext &C, |
99 | | ProgramStateRef State) const; |
100 | | |
101 | | ProgramStateRef checkCXXDeallocation(const CXXDeallocatorCall *DC, |
102 | | CheckerContext &C, |
103 | | ProgramStateRef State) const; |
104 | | |
105 | | ProgramStateRef checkArgInitializedness(const CallEvent &Call, |
106 | | CheckerContext &C, |
107 | | ProgramStateRef State) const; |
108 | | |
109 | | private: |
110 | | bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange, |
111 | | const Expr *ArgEx, int ArgumentNumber, |
112 | | bool CheckUninitFields, const CallEvent &Call, |
113 | | std::unique_ptr<BugType> &BT, |
114 | | const ParmVarDecl *ParamDecl) const; |
115 | | |
116 | | static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE); |
117 | | void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg, |
118 | | ExplodedNode *N) const; |
119 | | |
120 | | void HandleNilReceiver(CheckerContext &C, |
121 | | ProgramStateRef state, |
122 | | const ObjCMethodCall &msg) const; |
123 | | |
124 | 103 | void LazyInit_BT(const char *desc, std::unique_ptr<BugType> &BT) const { |
125 | 103 | if (!BT) |
126 | 61 | BT.reset(new BugType(OriginalName, desc)); |
127 | 103 | } |
128 | | bool uninitRefOrPointer(CheckerContext &C, const SVal &V, |
129 | | SourceRange ArgRange, const Expr *ArgEx, |
130 | | std::unique_ptr<BugType> &BT, |
131 | | const ParmVarDecl *ParamDecl, const char *BD, |
132 | | int ArgumentNumber) const; |
133 | | }; |
134 | | } // end anonymous namespace |
135 | | |
136 | | void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C, |
137 | 85 | const Expr *BadE) { |
138 | 85 | ExplodedNode *N = C.generateErrorNode(); |
139 | 85 | if (!N) |
140 | 0 | return; |
141 | | |
142 | 85 | auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); |
143 | 85 | if (BadE) { |
144 | 85 | R->addRange(BadE->getSourceRange()); |
145 | 85 | if (BadE->isGLValue()) |
146 | 11 | BadE = bugreporter::getDerefExpr(BadE); |
147 | 85 | bugreporter::trackExpressionValue(N, BadE, *R); |
148 | 85 | } |
149 | 85 | C.emitReport(std::move(R)); |
150 | 85 | } |
151 | | |
152 | | static void describeUninitializedArgumentInCall(const CallEvent &Call, |
153 | | int ArgumentNumber, |
154 | 63 | llvm::raw_svector_ostream &Os) { |
155 | 63 | switch (Call.getKind()) { |
156 | 9 | case CE_ObjCMessage: { |
157 | 9 | const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call); |
158 | 9 | switch (Msg.getMessageKind()) { |
159 | 2 | case OCM_Message: |
160 | 2 | Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1) |
161 | 2 | << " argument in message expression is an uninitialized value"; |
162 | 2 | return; |
163 | 1 | case OCM_PropertyAccess: |
164 | 1 | assert(Msg.isSetter() && "Getters have no args"); |
165 | 1 | Os << "Argument for property setter is an uninitialized value"; |
166 | 1 | return; |
167 | 6 | case OCM_Subscript: |
168 | 6 | if (Msg.isSetter() && (ArgumentNumber == 0)4 ) |
169 | 2 | Os << "Argument for subscript setter is an uninitialized value"; |
170 | 4 | else |
171 | 4 | Os << "Subscript index is an uninitialized value"; |
172 | 6 | return; |
173 | 9 | } |
174 | 0 | llvm_unreachable("Unknown message kind."); |
175 | 0 | } |
176 | 2 | case CE_Block: |
177 | 2 | Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1) |
178 | 2 | << " block call argument is an uninitialized value"; |
179 | 2 | return; |
180 | 52 | default: |
181 | 52 | Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1) |
182 | 52 | << " function call argument is an uninitialized value"; |
183 | 52 | return; |
184 | 63 | } |
185 | 63 | } |
186 | | |
187 | | bool CallAndMessageChecker::uninitRefOrPointer( |
188 | | CheckerContext &C, const SVal &V, SourceRange ArgRange, const Expr *ArgEx, |
189 | | std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD, |
190 | 90.0k | int ArgumentNumber) const { |
191 | | |
192 | | // The pointee being uninitialized is a sign of code smell, not a bug, no need |
193 | | // to sink here. |
194 | 90.0k | if (!ChecksEnabled[CK_ArgPointeeInitializedness]) |
195 | 89.9k | return false; |
196 | | |
197 | | // No parameter declaration available, i.e. variadic function argument. |
198 | 77 | if(!ParamDecl) |
199 | 4 | return false; |
200 | | |
201 | | // If parameter is declared as pointer to const in function declaration, |
202 | | // then check if corresponding argument in function call is |
203 | | // pointing to undefined symbol value (uninitialized memory). |
204 | 73 | SmallString<200> Buf; |
205 | 73 | llvm::raw_svector_ostream Os(Buf); |
206 | | |
207 | 73 | if (ParamDecl->getType()->isPointerType()) { |
208 | 47 | Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1) |
209 | 47 | << " function call argument is a pointer to uninitialized value"; |
210 | 47 | } else if (26 ParamDecl->getType()->isReferenceType()26 ) { |
211 | 13 | Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1) |
212 | 13 | << " function call argument is an uninitialized value"; |
213 | 13 | } else |
214 | 13 | return false; |
215 | | |
216 | 60 | if(!ParamDecl->getType()->getPointeeType().isConstQualified()) |
217 | 14 | return false; |
218 | | |
219 | 46 | if (const MemRegion *SValMemRegion = V.getAsRegion()) { |
220 | 46 | const ProgramStateRef State = C.getState(); |
221 | 46 | const SVal PSV = State->getSVal(SValMemRegion, C.getASTContext().CharTy); |
222 | 46 | if (PSV.isUndef()) { |
223 | 26 | if (ExplodedNode *N = C.generateErrorNode()) { |
224 | 26 | LazyInit_BT(BD, BT); |
225 | 26 | auto R = std::make_unique<PathSensitiveBugReport>(*BT, Os.str(), N); |
226 | 26 | R->addRange(ArgRange); |
227 | 26 | if (ArgEx) |
228 | 26 | bugreporter::trackExpressionValue(N, ArgEx, *R); |
229 | | |
230 | 26 | C.emitReport(std::move(R)); |
231 | 26 | } |
232 | 26 | return true; |
233 | 26 | } |
234 | 46 | } |
235 | 20 | return false; |
236 | 46 | } |
237 | | |
238 | | namespace { |
239 | | class FindUninitializedField { |
240 | | public: |
241 | | SmallVector<const FieldDecl *, 10> FieldChain; |
242 | | |
243 | | private: |
244 | | StoreManager &StoreMgr; |
245 | | MemRegionManager &MrMgr; |
246 | | Store store; |
247 | | |
248 | | public: |
249 | | FindUninitializedField(StoreManager &storeMgr, MemRegionManager &mrMgr, |
250 | | Store s) |
251 | 9.81k | : StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} |
252 | | |
253 | 9.82k | bool Find(const TypedValueRegion *R) { |
254 | 9.82k | QualType T = R->getValueType(); |
255 | 9.82k | if (const RecordType *RT = T->getAsStructureType()) { |
256 | 9.78k | const RecordDecl *RD = RT->getDecl()->getDefinition(); |
257 | 9.78k | assert(RD && "Referred record has no definition"); |
258 | 9.82k | for (const auto *I : RD->fields())9.78k { |
259 | 9.82k | const FieldRegion *FR = MrMgr.getFieldRegion(I, R); |
260 | 9.82k | FieldChain.push_back(I); |
261 | 9.82k | T = I->getType(); |
262 | 9.82k | if (T->getAsStructureType()) { |
263 | 14 | if (Find(FR)) |
264 | 0 | return true; |
265 | 9.80k | } else { |
266 | 9.80k | const SVal &V = StoreMgr.getBinding(store, loc::MemRegionVal(FR)); |
267 | 9.80k | if (V.isUndef()) |
268 | 11 | return true; |
269 | 9.80k | } |
270 | 9.80k | FieldChain.pop_back(); |
271 | 9.80k | } |
272 | 9.78k | } |
273 | | |
274 | 9.81k | return false; |
275 | 9.82k | } |
276 | | }; |
277 | | } // namespace |
278 | | |
279 | | bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, |
280 | | SVal V, |
281 | | SourceRange ArgRange, |
282 | | const Expr *ArgEx, |
283 | | int ArgumentNumber, |
284 | | bool CheckUninitFields, |
285 | | const CallEvent &Call, |
286 | | std::unique_ptr<BugType> &BT, |
287 | | const ParmVarDecl *ParamDecl |
288 | 90.0k | ) const { |
289 | 90.0k | const char *BD = "Uninitialized argument value"; |
290 | | |
291 | 90.0k | if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, BD, |
292 | 90.0k | ArgumentNumber)) |
293 | 26 | return true; |
294 | | |
295 | 90.0k | if (V.isUndef()) { |
296 | 68 | if (!ChecksEnabled[CK_ArgInitializedness]) { |
297 | 5 | C.addSink(); |
298 | 5 | return true; |
299 | 5 | } |
300 | 63 | if (ExplodedNode *N = C.generateErrorNode()) { |
301 | 63 | LazyInit_BT(BD, BT); |
302 | | // Generate a report for this bug. |
303 | 63 | SmallString<200> Buf; |
304 | 63 | llvm::raw_svector_ostream Os(Buf); |
305 | 63 | describeUninitializedArgumentInCall(Call, ArgumentNumber, Os); |
306 | 63 | auto R = std::make_unique<PathSensitiveBugReport>(*BT, Os.str(), N); |
307 | | |
308 | 63 | R->addRange(ArgRange); |
309 | 63 | if (ArgEx) |
310 | 63 | bugreporter::trackExpressionValue(N, ArgEx, *R); |
311 | 63 | C.emitReport(std::move(R)); |
312 | 63 | } |
313 | 63 | return true; |
314 | 68 | } |
315 | | |
316 | 89.9k | if (!CheckUninitFields) |
317 | 33.1k | return false; |
318 | | |
319 | 56.7k | if (auto LV = V.getAs<nonloc::LazyCompoundVal>()) { |
320 | 9.81k | const LazyCompoundValData *D = LV->getCVData(); |
321 | 9.81k | FindUninitializedField F(C.getState()->getStateManager().getStoreManager(), |
322 | 9.81k | C.getSValBuilder().getRegionManager(), |
323 | 9.81k | D->getStore()); |
324 | | |
325 | 9.81k | if (F.Find(D->getRegion())) { |
326 | 11 | if (!ChecksEnabled[CK_ArgInitializedness]) { |
327 | 4 | C.addSink(); |
328 | 4 | return true; |
329 | 4 | } |
330 | 7 | if (ExplodedNode *N = C.generateErrorNode()) { |
331 | 7 | LazyInit_BT(BD, BT); |
332 | 7 | SmallString<512> Str; |
333 | 7 | llvm::raw_svector_ostream os(Str); |
334 | 7 | os << "Passed-by-value struct argument contains uninitialized data"; |
335 | | |
336 | 7 | if (F.FieldChain.size() == 1) |
337 | 7 | os << " (e.g., field: '" << *F.FieldChain[0] << "')"; |
338 | 0 | else { |
339 | 0 | os << " (e.g., via the field chain: '"; |
340 | 0 | bool first = true; |
341 | 0 | for (SmallVectorImpl<const FieldDecl *>::iterator |
342 | 0 | DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){ |
343 | 0 | if (first) |
344 | 0 | first = false; |
345 | 0 | else |
346 | 0 | os << '.'; |
347 | 0 | os << **DI; |
348 | 0 | } |
349 | 0 | os << "')"; |
350 | 0 | } |
351 | | |
352 | | // Generate a report for this bug. |
353 | 7 | auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); |
354 | 7 | R->addRange(ArgRange); |
355 | | |
356 | 7 | if (ArgEx) |
357 | 7 | bugreporter::trackExpressionValue(N, ArgEx, *R); |
358 | | // FIXME: enhance track back for uninitialized value for arbitrary |
359 | | // memregions |
360 | 7 | C.emitReport(std::move(R)); |
361 | 7 | } |
362 | 7 | return true; |
363 | 11 | } |
364 | 9.81k | } |
365 | | |
366 | 56.7k | return false; |
367 | 56.7k | } |
368 | | |
369 | | ProgramStateRef CallAndMessageChecker::checkFunctionPointerCall( |
370 | 72.3k | const CallExpr *CE, CheckerContext &C, ProgramStateRef State) const { |
371 | | |
372 | 72.3k | const Expr *Callee = CE->getCallee()->IgnoreParens(); |
373 | 72.3k | const LocationContext *LCtx = C.getLocationContext(); |
374 | 72.3k | SVal L = State->getSVal(Callee, LCtx); |
375 | | |
376 | 72.3k | if (L.isUndef()) { |
377 | 8 | if (!ChecksEnabled[CK_FunctionPointer]) { |
378 | 4 | C.addSink(State); |
379 | 4 | return nullptr; |
380 | 4 | } |
381 | 4 | if (!BT_call_undef) |
382 | 4 | BT_call_undef.reset(new BugType( |
383 | 4 | OriginalName, |
384 | 4 | "Called function pointer is an uninitialized pointer value")); |
385 | 4 | emitBadCall(BT_call_undef.get(), C, Callee); |
386 | 4 | return nullptr; |
387 | 8 | } |
388 | | |
389 | 72.3k | ProgramStateRef StNonNull, StNull; |
390 | 72.3k | std::tie(StNonNull, StNull) = State->assume(L.castAs<DefinedOrUnknownSVal>()); |
391 | | |
392 | 72.3k | if (StNull && !StNonNull81 ) { |
393 | 8 | if (!ChecksEnabled[CK_FunctionPointer]) { |
394 | 4 | C.addSink(StNull); |
395 | 4 | return nullptr; |
396 | 4 | } |
397 | 4 | if (!BT_call_null) |
398 | 3 | BT_call_null.reset(new BugType( |
399 | 3 | OriginalName, "Called function pointer is null (null dereference)")); |
400 | 4 | emitBadCall(BT_call_null.get(), C, Callee); |
401 | 4 | return nullptr; |
402 | 8 | } |
403 | | |
404 | 72.3k | return StNonNull; |
405 | 72.3k | } |
406 | | |
407 | | ProgramStateRef CallAndMessageChecker::checkParameterCount( |
408 | 102k | const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const { |
409 | | |
410 | | // If we have a function or block declaration, we can make sure we pass |
411 | | // enough parameters. |
412 | 102k | unsigned Params = Call.parameters().size(); |
413 | 102k | if (Call.getNumArgs() >= Params) |
414 | 102k | return State; |
415 | | |
416 | 11 | if (!ChecksEnabled[CK_ParameterCount]) { |
417 | 4 | C.addSink(State); |
418 | 4 | return nullptr; |
419 | 4 | } |
420 | | |
421 | 7 | ExplodedNode *N = C.generateErrorNode(); |
422 | 7 | if (!N) |
423 | 0 | return nullptr; |
424 | | |
425 | 7 | LazyInit_BT("Function call with too few arguments", BT_call_few_args); |
426 | | |
427 | 7 | SmallString<512> Str; |
428 | 7 | llvm::raw_svector_ostream os(Str); |
429 | 7 | if (isa<AnyFunctionCall>(Call)) { |
430 | 5 | os << "Function "; |
431 | 5 | } else { |
432 | 2 | assert(isa<BlockCall>(Call)); |
433 | 2 | os << "Block "; |
434 | 2 | } |
435 | 7 | os << "taking " << Params << " argument" << (Params == 1 ? ""3 : "s"4 ) |
436 | 7 | << " is called with fewer (" << Call.getNumArgs() << ")"; |
437 | | |
438 | 7 | C.emitReport( |
439 | 7 | std::make_unique<PathSensitiveBugReport>(*BT_call_few_args, os.str(), N)); |
440 | 7 | return nullptr; |
441 | 7 | } |
442 | | |
443 | | ProgramStateRef CallAndMessageChecker::checkCXXMethodCall( |
444 | 13.4k | const CXXInstanceCall *CC, CheckerContext &C, ProgramStateRef State) const { |
445 | | |
446 | 13.4k | SVal V = CC->getCXXThisVal(); |
447 | 13.4k | if (V.isUndef()) { |
448 | 13 | if (!ChecksEnabled[CK_CXXThisMethodCall]) { |
449 | 4 | C.addSink(State); |
450 | 4 | return nullptr; |
451 | 4 | } |
452 | 9 | if (!BT_cxx_call_undef) |
453 | 9 | BT_cxx_call_undef.reset(new BugType( |
454 | 9 | OriginalName, "Called C++ object pointer is uninitialized")); |
455 | 9 | emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr()); |
456 | 9 | return nullptr; |
457 | 13 | } |
458 | | |
459 | 13.4k | ProgramStateRef StNonNull, StNull; |
460 | 13.4k | std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>()); |
461 | | |
462 | 13.4k | if (StNull && !StNonNull311 ) { |
463 | 72 | if (!ChecksEnabled[CK_CXXThisMethodCall]) { |
464 | 4 | C.addSink(StNull); |
465 | 4 | return nullptr; |
466 | 4 | } |
467 | 68 | if (!BT_cxx_call_null) |
468 | 29 | BT_cxx_call_null.reset( |
469 | 29 | new BugType(OriginalName, "Called C++ object pointer is null")); |
470 | 68 | emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr()); |
471 | 68 | return nullptr; |
472 | 72 | } |
473 | | |
474 | 13.4k | return StNonNull; |
475 | 13.4k | } |
476 | | |
477 | | ProgramStateRef |
478 | | CallAndMessageChecker::checkCXXDeallocation(const CXXDeallocatorCall *DC, |
479 | | CheckerContext &C, |
480 | 475 | ProgramStateRef State) const { |
481 | 475 | const CXXDeleteExpr *DE = DC->getOriginExpr(); |
482 | 475 | assert(DE); |
483 | 475 | SVal Arg = C.getSVal(DE->getArgument()); |
484 | 475 | if (!Arg.isUndef()) |
485 | 445 | return State; |
486 | | |
487 | 30 | if (!ChecksEnabled[CK_CXXDeallocationArg]) { |
488 | 4 | C.addSink(State); |
489 | 4 | return nullptr; |
490 | 4 | } |
491 | | |
492 | 26 | StringRef Desc; |
493 | 26 | ExplodedNode *N = C.generateErrorNode(); |
494 | 26 | if (!N) |
495 | 0 | return nullptr; |
496 | 26 | if (!BT_cxx_delete_undef) |
497 | 8 | BT_cxx_delete_undef.reset( |
498 | 8 | new BugType(OriginalName, "Uninitialized argument value")); |
499 | 26 | if (DE->isArrayFormAsWritten()) |
500 | 12 | Desc = "Argument to 'delete[]' is uninitialized"; |
501 | 14 | else |
502 | 14 | Desc = "Argument to 'delete' is uninitialized"; |
503 | 26 | auto R = |
504 | 26 | std::make_unique<PathSensitiveBugReport>(*BT_cxx_delete_undef, Desc, N); |
505 | 26 | bugreporter::trackExpressionValue(N, DE, *R); |
506 | 26 | C.emitReport(std::move(R)); |
507 | 26 | return nullptr; |
508 | 26 | } |
509 | | |
510 | | ProgramStateRef CallAndMessageChecker::checkArgInitializedness( |
511 | 102k | const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const { |
512 | | |
513 | 102k | const Decl *D = Call.getDecl(); |
514 | | |
515 | | // Don't check for uninitialized field values in arguments if the |
516 | | // caller has a body that is available and we have the chance to inline it. |
517 | | // This is a hack, but is a reasonable compromise betweens sometimes warning |
518 | | // and sometimes not depending on if we decide to inline a function. |
519 | 102k | const bool checkUninitFields = |
520 | 102k | !(C.getAnalysisManager().shouldInlineCall() && (102k D102k && D->getBody()102k )); |
521 | | |
522 | 102k | std::unique_ptr<BugType> *BT; |
523 | 102k | if (isa<ObjCMethodCall>(Call)) |
524 | 3.67k | BT = &BT_msg_arg; |
525 | 98.9k | else |
526 | 98.9k | BT = &BT_call_arg; |
527 | | |
528 | 102k | const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D); |
529 | 192k | for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i89.9k ) { |
530 | 90.0k | const ParmVarDecl *ParamDecl = nullptr; |
531 | 90.0k | if (FD && i < FD->getNumParams()88.9k ) |
532 | 88.3k | ParamDecl = FD->getParamDecl(i); |
533 | 90.0k | if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i), |
534 | 90.0k | Call.getArgExpr(i), i, checkUninitFields, Call, *BT, |
535 | 90.0k | ParamDecl)) |
536 | 105 | return nullptr; |
537 | 90.0k | } |
538 | 102k | return State; |
539 | 102k | } |
540 | | |
541 | | void CallAndMessageChecker::checkPreCall(const CallEvent &Call, |
542 | 102k | CheckerContext &C) const { |
543 | 102k | ProgramStateRef State = C.getState(); |
544 | | |
545 | 102k | if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr())) |
546 | 72.3k | State = checkFunctionPointerCall(CE, C, State); |
547 | | |
548 | 102k | if (!State) |
549 | 16 | return; |
550 | | |
551 | 102k | if (Call.getDecl()) |
552 | 102k | State = checkParameterCount(Call, C, State); |
553 | | |
554 | 102k | if (!State) |
555 | 11 | return; |
556 | | |
557 | 102k | if (const auto *CC = dyn_cast<CXXInstanceCall>(&Call)) |
558 | 13.4k | State = checkCXXMethodCall(CC, C, State); |
559 | | |
560 | 102k | if (!State) |
561 | 85 | return; |
562 | | |
563 | 102k | if (const auto *DC = dyn_cast<CXXDeallocatorCall>(&Call)) |
564 | 475 | State = checkCXXDeallocation(DC, C, State); |
565 | | |
566 | 102k | if (!State) |
567 | 30 | return; |
568 | | |
569 | 102k | State = checkArgInitializedness(Call, C, State); |
570 | | |
571 | | // If we make it here, record our assumptions about the callee. |
572 | 102k | C.addTransition(State); |
573 | 102k | } |
574 | | |
575 | | void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg, |
576 | 3.73k | CheckerContext &C) const { |
577 | 3.73k | SVal recVal = msg.getReceiverSVal(); |
578 | 3.73k | if (recVal.isUndef()) { |
579 | 8 | if (!ChecksEnabled[CK_UndefReceiver]) { |
580 | 0 | C.addSink(); |
581 | 0 | return; |
582 | 0 | } |
583 | 8 | if (ExplodedNode *N = C.generateErrorNode()) { |
584 | 8 | BugType *BT = nullptr; |
585 | 8 | switch (msg.getMessageKind()) { |
586 | 2 | case OCM_Message: |
587 | 2 | if (!BT_msg_undef) |
588 | 2 | BT_msg_undef.reset(new BugType(OriginalName, |
589 | 2 | "Receiver in message expression " |
590 | 2 | "is an uninitialized value")); |
591 | 2 | BT = BT_msg_undef.get(); |
592 | 2 | break; |
593 | 2 | case OCM_PropertyAccess: |
594 | 2 | if (!BT_objc_prop_undef) |
595 | 2 | BT_objc_prop_undef.reset(new BugType( |
596 | 2 | OriginalName, |
597 | 2 | "Property access on an uninitialized object pointer")); |
598 | 2 | BT = BT_objc_prop_undef.get(); |
599 | 2 | break; |
600 | 4 | case OCM_Subscript: |
601 | 4 | if (!BT_objc_subscript_undef) |
602 | 2 | BT_objc_subscript_undef.reset(new BugType( |
603 | 2 | OriginalName, |
604 | 2 | "Subscript access on an uninitialized object pointer")); |
605 | 4 | BT = BT_objc_subscript_undef.get(); |
606 | 4 | break; |
607 | 8 | } |
608 | 8 | assert(BT && "Unknown message kind."); |
609 | | |
610 | 8 | auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); |
611 | 8 | const ObjCMessageExpr *ME = msg.getOriginExpr(); |
612 | 8 | R->addRange(ME->getReceiverRange()); |
613 | | |
614 | | // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet. |
615 | 8 | if (const Expr *ReceiverE = ME->getInstanceReceiver()) |
616 | 8 | bugreporter::trackExpressionValue(N, ReceiverE, *R); |
617 | 8 | C.emitReport(std::move(R)); |
618 | 8 | } |
619 | 8 | return; |
620 | 8 | } |
621 | 3.73k | } |
622 | | |
623 | | void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg, |
624 | 96 | CheckerContext &C) const { |
625 | 96 | HandleNilReceiver(C, C.getState(), msg); |
626 | 96 | } |
627 | | |
628 | | void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, |
629 | | const ObjCMethodCall &msg, |
630 | 7 | ExplodedNode *N) const { |
631 | 7 | if (!ChecksEnabled[CK_NilReceiver]) { |
632 | 0 | C.addSink(); |
633 | 0 | return; |
634 | 0 | } |
635 | | |
636 | 7 | if (!BT_msg_ret) |
637 | 2 | BT_msg_ret.reset( |
638 | 2 | new BugType(OriginalName, "Receiver in message expression is 'nil'")); |
639 | | |
640 | 7 | const ObjCMessageExpr *ME = msg.getOriginExpr(); |
641 | | |
642 | 7 | QualType ResTy = msg.getResultType(); |
643 | | |
644 | 7 | SmallString<200> buf; |
645 | 7 | llvm::raw_svector_ostream os(buf); |
646 | 7 | os << "The receiver of message '"; |
647 | 7 | ME->getSelector().print(os); |
648 | 7 | os << "' is nil"; |
649 | 7 | if (ResTy->isReferenceType()) { |
650 | 2 | os << ", which results in forming a null reference"; |
651 | 5 | } else { |
652 | 5 | os << " and returns a value of type '"; |
653 | 5 | msg.getResultType().print(os, C.getLangOpts()); |
654 | 5 | os << "' that will be garbage"; |
655 | 5 | } |
656 | | |
657 | 7 | auto report = |
658 | 7 | std::make_unique<PathSensitiveBugReport>(*BT_msg_ret, os.str(), N); |
659 | 7 | report->addRange(ME->getReceiverRange()); |
660 | | // FIXME: This won't track "self" in messages to super. |
661 | 7 | if (const Expr *receiver = ME->getInstanceReceiver()) { |
662 | 7 | bugreporter::trackExpressionValue(N, receiver, *report); |
663 | 7 | } |
664 | 7 | C.emitReport(std::move(report)); |
665 | 7 | } |
666 | | |
667 | 15 | static bool supportsNilWithFloatRet(const llvm::Triple &triple) { |
668 | 15 | return (triple.getVendor() == llvm::Triple::Apple && |
669 | 15 | (triple.isiOS() || triple.isWatchOS()10 || |
670 | 15 | !triple.isMacOSXVersionLT(10,5)10 )); |
671 | 15 | } |
672 | | |
673 | | void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, |
674 | | ProgramStateRef state, |
675 | 96 | const ObjCMethodCall &Msg) const { |
676 | 96 | ASTContext &Ctx = C.getASTContext(); |
677 | 96 | static CheckerProgramPointTag Tag(this, "NilReceiver"); |
678 | | |
679 | | // Check the return type of the message expression. A message to nil will |
680 | | // return different values depending on the return type and the architecture. |
681 | 96 | QualType RetTy = Msg.getResultType(); |
682 | 96 | CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); |
683 | 96 | const LocationContext *LCtx = C.getLocationContext(); |
684 | | |
685 | 96 | if (CanRetTy->isStructureOrClassType()) { |
686 | | // Structure returns are safe since the compiler zeroes them out. |
687 | 4 | SVal V = C.getSValBuilder().makeZeroVal(RetTy); |
688 | 4 | C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V), &Tag); |
689 | 4 | return; |
690 | 4 | } |
691 | | |
692 | | // Other cases: check if sizeof(return type) > sizeof(void*) |
693 | 92 | if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap() |
694 | 79 | .isConsumedExpr(Msg.getOriginExpr())) { |
695 | | // Compute: sizeof(void *) and sizeof(return type) |
696 | 74 | const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); |
697 | 74 | const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); |
698 | | |
699 | 74 | if (CanRetTy.getTypePtr()->isReferenceType()|| |
700 | 74 | (72 voidPtrSize < returnTypeSize72 && |
701 | 72 | !(15 supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple())15 && |
702 | 15 | (10 Ctx.FloatTy == CanRetTy10 || |
703 | 10 | Ctx.DoubleTy == CanRetTy || |
704 | 10 | Ctx.LongDoubleTy == CanRetTy8 || |
705 | 10 | Ctx.LongLongTy == CanRetTy6 || |
706 | 10 | Ctx.UnsignedLongLongTy == CanRetTy2 )))) { |
707 | 7 | if (ExplodedNode *N = C.generateErrorNode(state, &Tag)) |
708 | 7 | emitNilReceiverBug(C, Msg, N); |
709 | 7 | return; |
710 | 7 | } |
711 | | |
712 | | // Handle the safe cases where the return value is 0 if the |
713 | | // receiver is nil. |
714 | | // |
715 | | // FIXME: For now take the conservative approach that we only |
716 | | // return null values if we *know* that the receiver is nil. |
717 | | // This is because we can have surprises like: |
718 | | // |
719 | | // ... = [[NSScreens screens] objectAtIndex:0]; |
720 | | // |
721 | | // What can happen is that [... screens] could return nil, but |
722 | | // it most likely isn't nil. We should assume the semantics |
723 | | // of this case unless we have *a lot* more knowledge. |
724 | | // |
725 | 67 | SVal V = C.getSValBuilder().makeZeroVal(RetTy); |
726 | 67 | C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V), &Tag); |
727 | 67 | return; |
728 | 74 | } |
729 | | |
730 | 18 | C.addTransition(state); |
731 | 18 | } |
732 | | |
733 | 1.27k | void ento::registerCallAndMessageModeling(CheckerManager &mgr) { |
734 | 1.27k | mgr.registerChecker<CallAndMessageChecker>(); |
735 | 1.27k | } |
736 | | |
737 | 5.10k | bool ento::shouldRegisterCallAndMessageModeling(const CheckerManager &mgr) { |
738 | 5.10k | return true; |
739 | 5.10k | } |
740 | | |
741 | 1.27k | void ento::registerCallAndMessageChecker(CheckerManager &mgr) { |
742 | 1.27k | CallAndMessageChecker *checker = mgr.getChecker<CallAndMessageChecker>(); |
743 | | |
744 | 1.27k | checker->OriginalName = mgr.getCurrentCheckerName(); |
745 | | |
746 | 1.27k | #define QUERY_CHECKER_OPTION(OPTION) \ |
747 | 10.1k | checker->ChecksEnabled[CallAndMessageChecker::CK_##OPTION] = \ |
748 | 10.1k | mgr.getAnalyzerOptions().getCheckerBooleanOption( \ |
749 | 10.1k | mgr.getCurrentCheckerName(), #OPTION); |
750 | | |
751 | 1.27k | QUERY_CHECKER_OPTION(FunctionPointer) |
752 | 1.27k | QUERY_CHECKER_OPTION(ParameterCount) |
753 | 1.27k | QUERY_CHECKER_OPTION(CXXThisMethodCall) |
754 | 1.27k | QUERY_CHECKER_OPTION(CXXDeallocationArg) |
755 | 1.27k | QUERY_CHECKER_OPTION(ArgInitializedness) |
756 | 1.27k | QUERY_CHECKER_OPTION(ArgPointeeInitializedness) |
757 | 1.27k | QUERY_CHECKER_OPTION(NilReceiver) |
758 | 1.27k | QUERY_CHECKER_OPTION(UndefReceiver) |
759 | 1.27k | } |
760 | | |
761 | 2.55k | bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &mgr) { |
762 | 2.55k | return true; |
763 | 2.55k | } |