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
26using namespace clang;
27using namespace ento;
28
29namespace {
30
31class 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
47public:
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
107private:
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
133void 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
149static 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
184bool 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
235namespace {
236class FindUninitializedField {
237public:
238 SmallVector<const FieldDecl *, 10> FieldChain;
239
240private:
241 StoreManager &StoreMgr;
242 MemRegionManager &MrMgr;
243 Store store;
244
245public:
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
276bool 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
366ProgramStateRef 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
404ProgramStateRef 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
440ProgramStateRef 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
474ProgramStateRef
475CallAndMessageChecker::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
507ProgramStateRef 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
538void 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
572void 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
620void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg,
621 CheckerContext &C) const {
622 HandleNilReceiver(C, state: C.getState(), msg);
623}
624
625void 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
664static 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
670void 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
729void ento::registerCallAndMessageModeling(CheckerManager &mgr) {
730 mgr.registerChecker<CallAndMessageChecker>();
731}
732
733bool ento::shouldRegisterCallAndMessageModeling(const CheckerManager &mgr) {
734 return true;
735}
736
737void 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
757bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &mgr) {
758 return true;
759}
760

source code of clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp