clang 23.0.0git
CallAndMessageChecker.cpp
Go to the documentation of this file.
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"
24#include "llvm/ADT/STLExtras.h"
25#include "llvm/ADT/StringExtras.h"
26#include "llvm/Support/FormatVariadic.h"
27#include "llvm/Support/raw_ostream.h"
28
29using namespace clang;
30using namespace ento;
31
32namespace {
33
34class CallAndMessageChecker
35 : public Checker<check::PreObjCMessage, check::ObjCMessageNil,
36 check::PreCall> {
37 const BugType CallNullBug{
38 this, "Called function pointer is null (null dereference)"};
39 const BugType CallUndefBug{
40 this, "Called function pointer is an uninitialized pointer value"};
41 const BugType CXXCallNullBug{this, "Called C++ object pointer is null"};
42 const BugType CXXCallUndefBug{this,
43 "Called C++ object pointer is uninitialized"};
44 const BugType CallArgBug{this, "Uninitialized argument value"};
45 const BugType CXXDeleteUndefBug{this, "Uninitialized argument value"};
46 const BugType MsgUndefBug{
47 this, "Receiver in message expression is an uninitialized value"};
48 const BugType ObjCPropUndefBug{
49 this, "Property access on an uninitialized object pointer"};
50 const BugType ObjCSubscriptUndefBug{
51 this, "Subscript access on an uninitialized object pointer"};
52 const BugType MsgArgBug{this, "Uninitialized argument value"};
53 const BugType MsgRetBug{this, "Receiver in message expression is 'nil'"};
54 const BugType CallFewArgsBug{this, "Function call with too few arguments"};
55
56public:
57 // Like a checker family, CallAndMessageChecker can produce many kinds of
58 // warnings which can be separately enabled or disabled. However, for
59 // historical reasons these warning kinds are represented by checker options
60 // (and not separate checker frontends with their own names) because
61 // CallAndMessage is among the oldest checkers out there, and can
62 // be responsible for the majority of the reports on any given project. This
63 // is obviously not ideal, but changing checker name has the consequence of
64 // changing the issue hashes associated with the reports, and databases
65 // relying on this (CodeChecker, for instance) would suffer greatly.
66 // If we ever end up making changes to the issue hash generation algorithm, or
67 // the warning messages here, we should totally jump on the opportunity to
68 // convert these to actual checker frontends.
69 enum CheckKind {
70 CK_FunctionPointer,
71 CK_ParameterCount,
72 CK_CXXThisMethodCall,
73 CK_CXXDeallocationArg,
74 CK_ArgInitializedness,
75 CK_ArgPointeeInitializedness,
76 CK_NilReceiver,
77 CK_UndefReceiver,
78 CK_NumCheckKinds
79 };
80
81 bool ChecksEnabled[CK_NumCheckKinds] = {false};
82
83 /// When checking a struct value for uninitialized data and this setting is
84 /// true, all members should be completely uninitialized to get a checker
85 /// warning. When the value is false, the warning is emitted for partially
86 // initialized structures too.
87 bool ArgPointeeInitializednessComplete = true;
88
89 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
90
91 /// Fill in the return value that results from messaging nil based on the
92 /// return type and architecture and diagnose if the return value will be
93 /// garbage.
94 void checkObjCMessageNil(const ObjCMethodCall &msg, CheckerContext &C) const;
95
96 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
97
98 ProgramStateRef checkFunctionPointerCall(const CallExpr *CE,
99 CheckerContext &C,
100 ProgramStateRef State) const;
101
102 ProgramStateRef checkCXXMethodCall(const CXXInstanceCall *CC,
103 CheckerContext &C,
104 ProgramStateRef State) const;
105
106 ProgramStateRef checkParameterCount(const CallEvent &Call, CheckerContext &C,
107 ProgramStateRef State) const;
108
109 ProgramStateRef checkCXXDeallocation(const CXXDeallocatorCall *DC,
110 CheckerContext &C,
111 ProgramStateRef State) const;
112
113 ProgramStateRef checkArgInitializedness(const CallEvent &Call,
114 CheckerContext &C,
115 ProgramStateRef State) const;
116
117private:
118 bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange,
119 const Expr *ArgEx, int ArgumentNumber,
120 bool CheckUninitFields, const CallEvent &Call,
121 const BugType &BT,
122 const ParmVarDecl *ParamDecl) const;
123
124 static void emitBadCall(const BugType &BT, CheckerContext &C,
125 const Expr *BadE);
126 void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg,
127 ExplodedNode *N) const;
128
129 void HandleNilReceiver(CheckerContext &C,
130 ProgramStateRef state,
131 const ObjCMethodCall &msg) const;
132
133 bool uninitRefOrPointer(CheckerContext &C, SVal V, const CallEvent &Call,
134 const BugType &BT, const ParmVarDecl *ParamDecl,
135 int ArgumentNumber) const;
136
137 // C library functions which have a pointer-to-struct parameter that should be
138 // initialized (at least partially) before the call. The 'uninitRefOrPointer'
139 // check uses this data.
140 CallDescriptionMap<int> FunctionsWithInOutPtrParam = {
141 {{CDM::CLibrary, {"mbrlen"}, 3}, 2},
142 {{CDM::CLibrary, {"mbrtowc"}, 4}, 3},
143 {{CDM::CLibrary, {"wcrtomb"}, 3}, 2},
144 {{CDM::CLibrary, {"mbsrtowcs"}, 4}, 3},
145 {{CDM::CLibrary, {"wcsrtombs"}, 4}, 3},
146 {{CDM::CLibrary, {"mbsnrtowcs"}, 5}, 4},
147 {{CDM::CLibrary, {"wcsnrtombs"}, 5}, 4},
148 {{CDM::CLibrary, {"wcrtomb_s"}, 5}, 4},
149 {{CDM::CLibrary, {"mbsrtowcs_s"}, 6}, 5},
150 {{CDM::CLibrary, {"wcsrtombs_s"}, 6}, 5},
151
152 {{CDM::CLibrary, {"mbrtoc8"}, 4}, 3},
153 {{CDM::CLibrary, {"c8rtomb"}, 3}, 2},
154 {{CDM::CLibrary, {"mbrtoc16"}, 4}, 3},
155 {{CDM::CLibrary, {"c16rtomb"}, 3}, 2},
156 {{CDM::CLibrary, {"mbrtoc32"}, 4}, 3},
157 {{CDM::CLibrary, {"c32rtomb"}, 3}, 2},
158
159 {{CDM::CLibrary, {"mktime"}, 1}, 0},
160 {{CDM::CLibrary, {"timegm"}, 1}, 0},
161 };
162};
163} // end anonymous namespace
164
165void CallAndMessageChecker::emitBadCall(const BugType &BT, CheckerContext &C,
166 const Expr *BadE) {
167 ExplodedNode *N = C.generateErrorNode();
168 if (!N)
169 return;
170
171 auto R = std::make_unique<PathSensitiveBugReport>(BT, BT.getDescription(), N);
172 if (BadE) {
173 R->addRange(BadE->getSourceRange());
174 if (BadE->isGLValue())
175 BadE = bugreporter::getDerefExpr(BadE);
177 }
178 C.emitReport(std::move(R));
179}
180
182 int ArgumentNumber,
183 llvm::raw_svector_ostream &Os) {
184 switch (Call.getKind()) {
185 case CE_ObjCMessage: {
187 switch (Msg.getMessageKind()) {
188 case OCM_Message:
189 Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
190 << " argument in message expression is an uninitialized value";
191 return;
193 assert(Msg.isSetter() && "Getters have no args");
194 Os << "Argument for property setter is an uninitialized value";
195 return;
196 case OCM_Subscript:
197 if (Msg.isSetter() && (ArgumentNumber == 0))
198 Os << "Argument for subscript setter is an uninitialized value";
199 else
200 Os << "Subscript index is an uninitialized value";
201 return;
202 }
203 llvm_unreachable("Unknown message kind.");
204 }
205 case CE_Block:
206 Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
207 << " block call argument is an uninitialized value";
208 return;
209 default:
210 Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
211 << " function call argument is an uninitialized value";
212 return;
213 }
214}
215
216namespace {
217class FindUninitializedField {
218public:
219 using FieldChainTy = SmallVector<const FieldDecl *, 10>;
220 FieldChainTy FieldChain;
221
222private:
223 StoreManager &StoreMgr;
224 MemRegionManager &MrMgr;
225 Store store;
226 bool FindNotUninitialized;
227
228public:
229 FindUninitializedField(StoreManager &storeMgr, MemRegionManager &mrMgr,
230 Store s, bool FindNotUninitialized = false)
231 : StoreMgr(storeMgr), MrMgr(mrMgr), store(s),
232 FindNotUninitialized(FindNotUninitialized) {}
233
234 bool Find(const TypedValueRegion *R) {
235 QualType T = R->getValueType();
236 if (const RecordType *RT = T->getAsStructureType()) {
237 const RecordDecl *RD = RT->getDecl()->getDefinition();
238 assert(RD && "Referred record has no definition");
239 for (const auto *I : RD->fields()) {
240 if (I->isUnnamedBitField())
241 continue;
242 const FieldRegion *FR = MrMgr.getFieldRegion(I, R);
243 FieldChain.push_back(I);
244 T = I->getType();
245 if (T->isStructureType()) {
246 if (FindNotUninitialized ? !Find(FR) : Find(FR))
247 return !FindNotUninitialized;
248 } else {
249 SVal V = StoreMgr.getBinding(store, loc::MemRegionVal(FR));
250 if (FindNotUninitialized ? !V.isUndef() : V.isUndef())
251 return !FindNotUninitialized;
252 }
253 FieldChain.pop_back();
254 }
255 }
256
257 return FindNotUninitialized;
258 }
259};
260} // namespace
261
262namespace llvm {
263template <> struct format_provider<FindUninitializedField::FieldChainTy> {
264 static void format(const FindUninitializedField::FieldChainTy &V,
265 raw_ostream &Stream, StringRef Style) {
266 if (V.size() == 0)
267 return;
268 else if (V.size() == 1)
269 Stream << " (e.g., field: '" << *V[0] << "')";
270 else {
271 Stream << " (e.g., via the field chain: '";
272 interleave(
273 V, Stream, [&Stream](const FieldDecl *FD) { Stream << *FD; }, ".");
274 Stream << "')";
275 }
276 }
277};
278} // namespace llvm
279
280bool CallAndMessageChecker::uninitRefOrPointer(CheckerContext &C, SVal V,
281 const CallEvent &Call,
282 const BugType &BT,
283 const ParmVarDecl *ParamDecl,
284 int ArgumentNumber) const {
285
286 if (!ChecksEnabled[CK_ArgPointeeInitializedness])
287 return false;
288
289 // No parameter declaration available, i.e. variadic function argument.
290 if (!ParamDecl)
291 return false;
292
293 QualType ParamT = ParamDecl->getType();
294 if (!ParamT->isPointerOrReferenceType())
295 return false;
296
297 bool AllowPartialInitializedness = ArgPointeeInitializednessComplete;
298 QualType PointeeT = ParamT->getPointeeType();
299 if (!PointeeT.isConstQualified()) {
300 if (const int *PI = FunctionsWithInOutPtrParam.lookup(Call)) {
301 if (*PI != ArgumentNumber)
302 return false;
303 // At these functions always allow partial argument initializedness.
304 AllowPartialInitializedness = true;
305 } else {
306 return false;
307 }
308 }
309
310 const MemRegion *SValMemRegion = V.getAsRegion();
311 if (!SValMemRegion)
312 return false;
313
314 // If parameter is declared as pointer to const in function declaration,
315 // then check if corresponding argument in function call is
316 // pointing to undefined symbol value (uninitialized memory).
317
318 const ProgramStateRef State = C.getState();
319 if (PointeeT->isVoidType())
320 PointeeT = C.getASTContext().CharTy;
321 const SVal PointeeV = State->getSVal(SValMemRegion, PointeeT);
322 const Expr *ArgEx = Call.getArgExpr(ArgumentNumber);
323
324 if (PointeeV.isUndef()) {
325 if (ExplodedNode *N = C.generateErrorNode()) {
326 std::string Msg = llvm::formatv(
327 "{0}{1} function call argument is {2} uninitialized value",
328 ArgumentNumber + 1, llvm::getOrdinalSuffix(ArgumentNumber + 1),
329 ParamT->isPointerType() ? "a pointer to" : "an");
330 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
331 R->addRange(Call.getArgSourceRange(ArgumentNumber));
332 if (ArgEx)
334
335 C.emitReport(std::move(R));
336 }
337 return true;
338 }
339
340 if (auto LV = PointeeV.getAs<nonloc::LazyCompoundVal>()) {
341 const LazyCompoundValData *D = LV->getCVData();
342 FindUninitializedField F(C.getState()->getStateManager().getStoreManager(),
343 C.getSValBuilder().getRegionManager(),
344 D->getStore(), AllowPartialInitializedness);
345
346 if (F.Find(D->getRegion())) {
347 if (ExplodedNode *N = C.generateErrorNode()) {
348 std::string Msg = llvm::formatv(
349 "{0}{1} function call argument {2} an uninitialized value{3}",
350 (ArgumentNumber + 1), llvm::getOrdinalSuffix(ArgumentNumber + 1),
351 ParamT->isPointerType() ? "points to" : "references", F.FieldChain);
352 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
353 R->addRange(Call.getArgSourceRange(ArgumentNumber));
354 if (ArgEx)
356
357 C.emitReport(std::move(R));
358 }
359 return true;
360 }
361 }
362
363 return false;
364}
365
366bool CallAndMessageChecker::PreVisitProcessArg(
367 CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,
368 int ArgumentNumber, bool CheckUninitFields, const CallEvent &Call,
369 const BugType &BT, const ParmVarDecl *ParamDecl) const {
370 if (uninitRefOrPointer(C, V, Call, BT, ParamDecl, ArgumentNumber))
371 return true;
372
373 if (V.isUndef()) {
374 if (!ChecksEnabled[CK_ArgInitializedness]) {
375 C.addSink();
376 return true;
377 }
378 if (ExplodedNode *N = C.generateErrorNode()) {
379 // Generate a report for this bug.
380 SmallString<200> Buf;
381 llvm::raw_svector_ostream Os(Buf);
382 describeUninitializedArgumentInCall(Call, ArgumentNumber, Os);
383 auto R = std::make_unique<PathSensitiveBugReport>(BT, Os.str(), N);
384
385 R->addRange(ArgRange);
386 if (ArgEx)
388 C.emitReport(std::move(R));
389 }
390 return true;
391 }
392
393 if (!CheckUninitFields)
394 return false;
395
396 if (auto LV = V.getAs<nonloc::LazyCompoundVal>()) {
397 const LazyCompoundValData *D = LV->getCVData();
398 FindUninitializedField F(C.getState()->getStateManager().getStoreManager(),
399 C.getSValBuilder().getRegionManager(),
400 D->getStore());
401
402 if (F.Find(D->getRegion())) {
403 if (!ChecksEnabled[CK_ArgInitializedness]) {
404 C.addSink();
405 return true;
406 }
407 if (ExplodedNode *N = C.generateErrorNode()) {
408 std::string Msg = llvm::formatv(
409 "Passed-by-value struct argument contains uninitialized data{0}",
410 F.FieldChain);
411
412 // Generate a report for this bug.
413 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
414 R->addRange(ArgRange);
415
416 if (ArgEx)
418 // FIXME: enhance track back for uninitialized value for arbitrary
419 // memregions
420 C.emitReport(std::move(R));
421 }
422 return true;
423 }
424 }
425
426 return false;
427}
428
429ProgramStateRef CallAndMessageChecker::checkFunctionPointerCall(
430 const CallExpr *CE, CheckerContext &C, ProgramStateRef State) const {
431
432 const Expr *Callee = CE->getCallee()->IgnoreParens();
433 SVal L = State->getSVal(Callee, C.getStackFrame());
434
435 if (L.isUndef()) {
436 if (!ChecksEnabled[CK_FunctionPointer]) {
437 C.addSink(State);
438 return nullptr;
439 }
440 emitBadCall(CallUndefBug, C, Callee);
441 return nullptr;
442 }
443
444 ProgramStateRef StNonNull, StNull;
445 std::tie(StNonNull, StNull) = State->assume(L.castAs<DefinedOrUnknownSVal>());
446
447 if (StNull && !StNonNull) {
448 if (!ChecksEnabled[CK_FunctionPointer]) {
449 C.addSink(StNull);
450 return nullptr;
451 }
452 emitBadCall(CallNullBug, C, Callee);
453 return nullptr;
454 }
455
456 return StNonNull;
457}
458
459ProgramStateRef CallAndMessageChecker::checkParameterCount(
460 const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const {
461
462 // If we have a function or block declaration, we can make sure we pass
463 // enough parameters.
464 unsigned Params = Call.parameters().size();
465 if (Call.getNumArgs() >= Params)
466 return State;
467
468 if (!ChecksEnabled[CK_ParameterCount]) {
469 C.addSink(State);
470 return nullptr;
471 }
472
473 ExplodedNode *N = C.generateErrorNode();
474 if (!N)
475 return nullptr;
476
477 SmallString<512> Str;
478 llvm::raw_svector_ostream os(Str);
480 os << "Function ";
481 } else {
482 assert(isa<BlockCall>(Call));
483 os << "Block ";
484 }
485 os << "taking " << Params << " argument" << (Params == 1 ? "" : "s")
486 << " is called with fewer (" << Call.getNumArgs() << ")";
487
488 C.emitReport(
489 std::make_unique<PathSensitiveBugReport>(CallFewArgsBug, os.str(), N));
490 return nullptr;
491}
492
493ProgramStateRef CallAndMessageChecker::checkCXXMethodCall(
494 const CXXInstanceCall *CC, CheckerContext &C, ProgramStateRef State) const {
495
496 SVal V = CC->getCXXThisVal();
497 if (V.isUndef()) {
498 if (!ChecksEnabled[CK_CXXThisMethodCall]) {
499 C.addSink(State);
500 return nullptr;
501 }
502 emitBadCall(CXXCallUndefBug, C, CC->getCXXThisExpr());
503 return nullptr;
504 }
505
506 ProgramStateRef StNonNull, StNull;
507 std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
508
509 if (StNull && !StNonNull) {
510 if (!ChecksEnabled[CK_CXXThisMethodCall]) {
511 C.addSink(StNull);
512 return nullptr;
513 }
514 emitBadCall(CXXCallNullBug, C, CC->getCXXThisExpr());
515 return nullptr;
516 }
517
518 return StNonNull;
519}
520
522CallAndMessageChecker::checkCXXDeallocation(const CXXDeallocatorCall *DC,
523 CheckerContext &C,
524 ProgramStateRef State) const {
525 const CXXDeleteExpr *DE = DC->getOriginExpr();
526 assert(DE);
527 SVal Arg = C.getSVal(DE->getArgument());
528 if (!Arg.isUndef())
529 return State;
530
531 if (!ChecksEnabled[CK_CXXDeallocationArg]) {
532 C.addSink(State);
533 return nullptr;
534 }
535
536 StringRef Desc;
537 ExplodedNode *N = C.generateErrorNode();
538 if (!N)
539 return nullptr;
540 if (DE->isArrayFormAsWritten())
541 Desc = "Argument to 'delete[]' is uninitialized";
542 else
543 Desc = "Argument to 'delete' is uninitialized";
544 auto R = std::make_unique<PathSensitiveBugReport>(CXXDeleteUndefBug, Desc, N);
546 C.emitReport(std::move(R));
547 return nullptr;
548}
549
550ProgramStateRef CallAndMessageChecker::checkArgInitializedness(
551 const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const {
552
553 const Decl *D = Call.getDecl();
554
555 // Don't check for uninitialized field values in arguments if the
556 // caller has a body that is available and we have the chance to inline it.
557 // This is a hack, but is a reasonable compromise betweens sometimes warning
558 // and sometimes not depending on if we decide to inline a function.
559 const bool checkUninitFields =
560 !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody()));
561
562 const BugType &BT = isa<ObjCMethodCall>(Call) ? MsgArgBug : CallArgBug;
563
564 const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
565 for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) {
566 const ParmVarDecl *ParamDecl = nullptr;
567 if (FD && i < FD->getNumParams())
568 ParamDecl = FD->getParamDecl(i);
569 if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
570 Call.getArgExpr(i), i, checkUninitFields, Call, BT,
571 ParamDecl))
572 return nullptr;
573 }
574 return State;
575}
576
577void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
578 CheckerContext &C) const {
579 ProgramStateRef State = C.getState();
580
581 if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()))
582 State = checkFunctionPointerCall(CE, C, State);
583
584 if (!State)
585 return;
586
587 if (Call.getDecl())
588 State = checkParameterCount(Call, C, State);
589
590 if (!State)
591 return;
592
593 if (const auto *CC = dyn_cast<CXXInstanceCall>(&Call))
594 State = checkCXXMethodCall(CC, C, State);
595
596 if (!State)
597 return;
598
599 if (const auto *DC = dyn_cast<CXXDeallocatorCall>(&Call))
600 State = checkCXXDeallocation(DC, C, State);
601
602 if (!State)
603 return;
604
605 State = checkArgInitializedness(Call, C, State);
606
607 // If we make it here, record our assumptions about the callee.
608 C.addTransition(State);
609}
610
611void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
612 CheckerContext &C) const {
613 SVal recVal = msg.getReceiverSVal();
614 if (recVal.isUndef()) {
615 if (!ChecksEnabled[CK_UndefReceiver]) {
616 C.addSink();
617 return;
618 }
619 if (ExplodedNode *N = C.generateErrorNode()) {
620 const BugType *BT = nullptr;
621 switch (msg.getMessageKind()) {
622 case OCM_Message:
623 BT = &MsgUndefBug;
624 break;
626 BT = &ObjCPropUndefBug;
627 break;
628 case OCM_Subscript:
629 BT = &ObjCSubscriptUndefBug;
630 break;
631 }
632 assert(BT && "Unknown message kind.");
633
634 auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
635 const ObjCMessageExpr *ME = msg.getOriginExpr();
636 R->addRange(ME->getReceiverRange());
637
638 // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet.
639 if (const Expr *ReceiverE = ME->getInstanceReceiver())
640 bugreporter::trackExpressionValue(N, ReceiverE, *R);
641 C.emitReport(std::move(R));
642 }
643 return;
644 }
645}
646
647void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg,
648 CheckerContext &C) const {
649 HandleNilReceiver(C, C.getState(), msg);
650}
651
652void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
653 const ObjCMethodCall &msg,
654 ExplodedNode *N) const {
655 if (!ChecksEnabled[CK_NilReceiver]) {
656 C.addSink();
657 return;
658 }
659
660 const ObjCMessageExpr *ME = msg.getOriginExpr();
661
662 QualType ResTy = msg.getResultType();
663
664 SmallString<200> buf;
665 llvm::raw_svector_ostream os(buf);
666 os << "The receiver of message '";
667 ME->getSelector().print(os);
668 os << "' is nil";
669 if (ResTy->isReferenceType()) {
670 os << ", which results in forming a null reference";
671 } else {
672 os << " and returns a value of type '";
673 msg.getResultType().print(os, C.getLangOpts());
674 os << "' that will be garbage";
675 }
676
677 auto report =
678 std::make_unique<PathSensitiveBugReport>(MsgRetBug, os.str(), N);
679 report->addRange(ME->getReceiverRange());
680 // FIXME: This won't track "self" in messages to super.
681 if (const Expr *receiver = ME->getInstanceReceiver()) {
682 bugreporter::trackExpressionValue(N, receiver, *report);
683 }
684 C.emitReport(std::move(report));
685}
686
687static bool supportsNilWithFloatRet(const llvm::Triple &triple) {
688 return (triple.getVendor() == llvm::Triple::Apple &&
689 (triple.isiOS() || triple.isWatchOS() ||
690 !triple.isMacOSXVersionLT(10,5)));
691}
692
693void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
694 ProgramStateRef state,
695 const ObjCMethodCall &Msg) const {
696 ASTContext &Ctx = C.getASTContext();
697
698 // Check the return type of the message expression. A message to nil will
699 // return different values depending on the return type and the architecture.
700 QualType RetTy = Msg.getResultType();
701 CanQualType CanRetTy = Ctx.getCanonicalType(RetTy);
702 const StackFrame *SF = C.getStackFrame();
703
704 if (CanRetTy->isStructureOrClassType()) {
705 // Structure returns are safe since the compiler zeroes them out.
706 SVal V = C.getSValBuilder().makeZeroVal(RetTy);
707 C.addTransition(state->BindExpr(Msg.getOriginExpr(), SF, V));
708 return;
709 }
710
711 // Other cases: check if sizeof(return type) > sizeof(void*)
712 if (CanRetTy != Ctx.VoidTy &&
713 C.getStackFrame()->getParentMap().isConsumedExpr(Msg.getOriginExpr())) {
714 // Compute: sizeof(void *) and sizeof(return type)
715 const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);
716 const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy);
717
718 if (CanRetTy.getTypePtr()->isReferenceType()||
719 (voidPtrSize < returnTypeSize &&
721 (Ctx.FloatTy == CanRetTy ||
722 Ctx.DoubleTy == CanRetTy ||
723 Ctx.LongDoubleTy == CanRetTy ||
724 Ctx.LongLongTy == CanRetTy ||
725 Ctx.UnsignedLongLongTy == CanRetTy)))) {
726 if (ExplodedNode *N = C.generateErrorNode(state))
727 emitNilReceiverBug(C, Msg, N);
728 return;
729 }
730
731 // Handle the safe cases where the return value is 0 if the
732 // receiver is nil.
733 //
734 // FIXME: For now take the conservative approach that we only
735 // return null values if we *know* that the receiver is nil.
736 // This is because we can have surprises like:
737 //
738 // ... = [[NSScreens screens] objectAtIndex:0];
739 //
740 // What can happen is that [... screens] could return nil, but
741 // it most likely isn't nil. We should assume the semantics
742 // of this case unless we have *a lot* more knowledge.
743 //
744 SVal V = C.getSValBuilder().makeZeroVal(RetTy);
745 C.addTransition(state->BindExpr(Msg.getOriginExpr(), SF, V));
746 return;
747 }
748
749 C.addTransition(state);
750}
751
752void ento::registerCallAndMessageChecker(CheckerManager &Mgr) {
753 CallAndMessageChecker *Chk = Mgr.registerChecker<CallAndMessageChecker>();
754
755#define QUERY_CHECKER_OPTION(OPTION) \
756 Chk->ChecksEnabled[CallAndMessageChecker::CK_##OPTION] = \
757 Mgr.getAnalyzerOptions().getCheckerBooleanOption( \
758 Mgr.getCurrentCheckerName(), #OPTION);
759
760 QUERY_CHECKER_OPTION(FunctionPointer)
762 QUERY_CHECKER_OPTION(CXXThisMethodCall)
763 QUERY_CHECKER_OPTION(CXXDeallocationArg)
764 QUERY_CHECKER_OPTION(ArgInitializedness)
765 QUERY_CHECKER_OPTION(ArgPointeeInitializedness)
766 QUERY_CHECKER_OPTION(NilReceiver)
767 QUERY_CHECKER_OPTION(UndefReceiver)
768
769 Chk->ArgPointeeInitializednessComplete =
770 Mgr.getAnalyzerOptions().getCheckerBooleanOption(
771 Mgr.getCurrentCheckerName(), "ArgPointeeInitializednessComplete");
772}
773
774bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &) {
775 return true;
776}
#define V(N, I)
#define QUERY_CHECKER_OPTION(OPTION)
static bool supportsNilWithFloatRet(const llvm::Triple &triple)
static void describeUninitializedArgumentInCall(const CallEvent &Call, int ArgumentNumber, llvm::raw_svector_ostream &Os)
Defines the clang::Expr interface and subclasses for C++ expressions.
unsigned ParameterCount
Number of parameters, if this is "(", "[" or "<".
__device__ __2f16 float __ockl_bool s
static CanQualType getCanonicalType(QualType T)
Return the canonical (structural) type corresponding to the specified potentially non-canonical type ...
CanQualType FloatTy
CanQualType DoubleTy
CanQualType LongDoubleTy
CanQualType VoidPtrTy
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
CanQualType VoidTy
CanQualType UnsignedLongLongTy
const TargetInfo & getTargetInfo() const
Definition ASTContext.h:926
CanQualType LongLongTy
bool isArrayFormAsWritten() const
Definition ExprCXX.h:2657
Expr * getCallee()
Definition Expr.h:3093
const T * getTypePtr() const
Retrieve the underlying type pointer, which refers to a canonical type.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
Definition DeclBase.h:1100
This represents one expression.
Definition Expr.h:112
bool isGLValue() const
Definition Expr.h:287
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition Expr.cpp:3093
Represents a member of a struct/union/class.
Definition Decl.h:3182
const ParmVarDecl * getParamDecl(unsigned i) const
Definition Decl.h:2815
Expr * getInstanceReceiver()
Returns the object expression (receiver) for an instance message, or null for a message that is not a...
Definition ExprObjC.h:1299
Selector getSelector() const
Definition ExprObjC.cpp:301
SourceRange getReceiverRange() const
Source range of the receiver.
Definition ExprObjC.cpp:285
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const
bool isConstQualified() const
Determine whether this type is const-qualified.
Definition TypeBase.h:8518
field_range fields() const
Definition Decl.h:4550
void print(llvm::raw_ostream &OS) const
Prints the full selector name (e.g. "foo:bar:").
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:343
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
bool isStructureType() const
Definition Type.cpp:715
bool isVoidType() const
Definition TypeBase.h:9048
bool isPointerType() const
Definition TypeBase.h:8682
bool isReferenceType() const
Definition TypeBase.h:8706
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:789
const RecordType * getAsStructureType() const
Definition Type.cpp:805
bool isPointerOrReferenceType() const
Definition TypeBase.h:8686
QualType getType() const
Definition Decl.h:723
StringRef getDescription() const
Definition BugType.h:58
const CXXDeleteExpr * getOriginExpr() const override
Returns the expression whose value will be the result of this call.
Definition CallEvent.h:1219
virtual SVal getCXXThisVal() const
Returns the value of the implicit 'this' object.
virtual const Expr * getCXXThisExpr() const
Returns the expression representing the implicit 'this' object.
Definition CallEvent.h:707
Represents an abstract call to a function or method along a particular path.
Definition CallEvent.h:152
QualType getResultType() const
Returns the result type, adjusted for references.
Definition CallEvent.cpp:70
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
CheckerNameRef getCurrentCheckerName() const
Simple checker classes that implement one frontend (i.e.
Definition Checker.h:550
const void * getStore() const
It might return null.
LLVM_ATTRIBUTE_RETURNS_NONNULL const TypedValueRegion * getRegion() const
const FieldRegion * getFieldRegion(const FieldDecl *FD, const SubRegion *SuperRegion)
getFieldRegion - Retrieve or create the memory region associated with a specified FieldDecl.
Represents any expression that calls an Objective-C method.
Definition CallEvent.h:1251
ObjCMessageKind getMessageKind() const
Returns how the message was written in the source (property access, subscript, or explicit message se...
bool isSetter() const
Returns true if this property access or subscript is a setter (has the form of an assignment).
Definition CallEvent.h:1322
const ObjCMessageExpr * getOriginExpr() const override
Returns the expression whose value will be the result of this call.
Definition CallEvent.h:1276
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
bool isUndef() const
Definition SVals.h:107
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition SVals.h:87
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition SVals.h:83
virtual SVal getBinding(Store store, Loc loc, QualType T=QualType())=0
Return the value bound to specified location in a given state.
Defines the clang::TargetInfo interface.
const Expr * getDerefExpr(const Stmt *S)
Given that expression S represents a pointer that would be dereferenced, try to find a sub-expression...
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const void * Store
Store - This opaque type encapsulates an immutable mapping from locations to values.
Definition StoreRef.h:27
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
The JSON file list parser is used to communicate input to InstallAPI.
CanQual< Type > CanQualType
Represents a canonical, potentially-qualified type.
bool isa(CodeGen::Address addr)
Definition Address.h:330
U cast(CodeGen::Address addr)
Definition Address.h:327
unsigned long uint64_t
static void format(const FindUninitializedField::FieldChainTy &V, raw_ostream &Stream, StringRef Style)