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