clang 22.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 const LocationContext *LCtx = C.getLocationContext();
434 SVal L = State->getSVal(Callee, LCtx);
435
436 if (L.isUndef()) {
437 if (!ChecksEnabled[CK_FunctionPointer]) {
438 C.addSink(State);
439 return nullptr;
440 }
441 emitBadCall(CallUndefBug, C, Callee);
442 return nullptr;
443 }
444
445 ProgramStateRef StNonNull, StNull;
446 std::tie(StNonNull, StNull) = State->assume(L.castAs<DefinedOrUnknownSVal>());
447
448 if (StNull && !StNonNull) {
449 if (!ChecksEnabled[CK_FunctionPointer]) {
450 C.addSink(StNull);
451 return nullptr;
452 }
453 emitBadCall(CallNullBug, C, Callee);
454 return nullptr;
455 }
456
457 return StNonNull;
458}
459
460ProgramStateRef CallAndMessageChecker::checkParameterCount(
461 const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const {
462
463 // If we have a function or block declaration, we can make sure we pass
464 // enough parameters.
465 unsigned Params = Call.parameters().size();
466 if (Call.getNumArgs() >= Params)
467 return State;
468
469 if (!ChecksEnabled[CK_ParameterCount]) {
470 C.addSink(State);
471 return nullptr;
472 }
473
474 ExplodedNode *N = C.generateErrorNode();
475 if (!N)
476 return nullptr;
477
478 SmallString<512> Str;
479 llvm::raw_svector_ostream os(Str);
481 os << "Function ";
482 } else {
483 assert(isa<BlockCall>(Call));
484 os << "Block ";
485 }
486 os << "taking " << Params << " argument" << (Params == 1 ? "" : "s")
487 << " is called with fewer (" << Call.getNumArgs() << ")";
488
489 C.emitReport(
490 std::make_unique<PathSensitiveBugReport>(CallFewArgsBug, os.str(), N));
491 return nullptr;
492}
493
494ProgramStateRef CallAndMessageChecker::checkCXXMethodCall(
495 const CXXInstanceCall *CC, CheckerContext &C, ProgramStateRef State) const {
496
497 SVal V = CC->getCXXThisVal();
498 if (V.isUndef()) {
499 if (!ChecksEnabled[CK_CXXThisMethodCall]) {
500 C.addSink(State);
501 return nullptr;
502 }
503 emitBadCall(CXXCallUndefBug, C, CC->getCXXThisExpr());
504 return nullptr;
505 }
506
507 ProgramStateRef StNonNull, StNull;
508 std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
509
510 if (StNull && !StNonNull) {
511 if (!ChecksEnabled[CK_CXXThisMethodCall]) {
512 C.addSink(StNull);
513 return nullptr;
514 }
515 emitBadCall(CXXCallNullBug, C, CC->getCXXThisExpr());
516 return nullptr;
517 }
518
519 return StNonNull;
520}
521
523CallAndMessageChecker::checkCXXDeallocation(const CXXDeallocatorCall *DC,
524 CheckerContext &C,
525 ProgramStateRef State) const {
526 const CXXDeleteExpr *DE = DC->getOriginExpr();
527 assert(DE);
528 SVal Arg = C.getSVal(DE->getArgument());
529 if (!Arg.isUndef())
530 return State;
531
532 if (!ChecksEnabled[CK_CXXDeallocationArg]) {
533 C.addSink(State);
534 return nullptr;
535 }
536
537 StringRef Desc;
538 ExplodedNode *N = C.generateErrorNode();
539 if (!N)
540 return nullptr;
541 if (DE->isArrayFormAsWritten())
542 Desc = "Argument to 'delete[]' is uninitialized";
543 else
544 Desc = "Argument to 'delete' is uninitialized";
545 auto R = std::make_unique<PathSensitiveBugReport>(CXXDeleteUndefBug, Desc, N);
547 C.emitReport(std::move(R));
548 return nullptr;
549}
550
551ProgramStateRef CallAndMessageChecker::checkArgInitializedness(
552 const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const {
553
554 const Decl *D = Call.getDecl();
555
556 // Don't check for uninitialized field values in arguments if the
557 // caller has a body that is available and we have the chance to inline it.
558 // This is a hack, but is a reasonable compromise betweens sometimes warning
559 // and sometimes not depending on if we decide to inline a function.
560 const bool checkUninitFields =
561 !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody()));
562
563 const BugType &BT = isa<ObjCMethodCall>(Call) ? MsgArgBug : CallArgBug;
564
565 const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
566 for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) {
567 const ParmVarDecl *ParamDecl = nullptr;
568 if (FD && i < FD->getNumParams())
569 ParamDecl = FD->getParamDecl(i);
570 if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
571 Call.getArgExpr(i), i, checkUninitFields, Call, BT,
572 ParamDecl))
573 return nullptr;
574 }
575 return State;
576}
577
578void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
579 CheckerContext &C) const {
580 ProgramStateRef State = C.getState();
581
582 if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()))
583 State = checkFunctionPointerCall(CE, C, State);
584
585 if (!State)
586 return;
587
588 if (Call.getDecl())
589 State = checkParameterCount(Call, C, State);
590
591 if (!State)
592 return;
593
594 if (const auto *CC = dyn_cast<CXXInstanceCall>(&Call))
595 State = checkCXXMethodCall(CC, C, State);
596
597 if (!State)
598 return;
599
600 if (const auto *DC = dyn_cast<CXXDeallocatorCall>(&Call))
601 State = checkCXXDeallocation(DC, C, State);
602
603 if (!State)
604 return;
605
606 State = checkArgInitializedness(Call, C, State);
607
608 // If we make it here, record our assumptions about the callee.
609 C.addTransition(State);
610}
611
612void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
613 CheckerContext &C) const {
614 SVal recVal = msg.getReceiverSVal();
615 if (recVal.isUndef()) {
616 if (!ChecksEnabled[CK_UndefReceiver]) {
617 C.addSink();
618 return;
619 }
620 if (ExplodedNode *N = C.generateErrorNode()) {
621 const BugType *BT = nullptr;
622 switch (msg.getMessageKind()) {
623 case OCM_Message:
624 BT = &MsgUndefBug;
625 break;
627 BT = &ObjCPropUndefBug;
628 break;
629 case OCM_Subscript:
630 BT = &ObjCSubscriptUndefBug;
631 break;
632 }
633 assert(BT && "Unknown message kind.");
634
635 auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
636 const ObjCMessageExpr *ME = msg.getOriginExpr();
637 R->addRange(ME->getReceiverRange());
638
639 // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet.
640 if (const Expr *ReceiverE = ME->getInstanceReceiver())
641 bugreporter::trackExpressionValue(N, ReceiverE, *R);
642 C.emitReport(std::move(R));
643 }
644 return;
645 }
646}
647
648void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg,
649 CheckerContext &C) const {
650 HandleNilReceiver(C, C.getState(), msg);
651}
652
653void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
654 const ObjCMethodCall &msg,
655 ExplodedNode *N) const {
656 if (!ChecksEnabled[CK_NilReceiver]) {
657 C.addSink();
658 return;
659 }
660
661 const ObjCMessageExpr *ME = msg.getOriginExpr();
662
663 QualType ResTy = msg.getResultType();
664
665 SmallString<200> buf;
666 llvm::raw_svector_ostream os(buf);
667 os << "The receiver of message '";
668 ME->getSelector().print(os);
669 os << "' is nil";
670 if (ResTy->isReferenceType()) {
671 os << ", which results in forming a null reference";
672 } else {
673 os << " and returns a value of type '";
674 msg.getResultType().print(os, C.getLangOpts());
675 os << "' that will be garbage";
676 }
677
678 auto report =
679 std::make_unique<PathSensitiveBugReport>(MsgRetBug, os.str(), N);
680 report->addRange(ME->getReceiverRange());
681 // FIXME: This won't track "self" in messages to super.
682 if (const Expr *receiver = ME->getInstanceReceiver()) {
683 bugreporter::trackExpressionValue(N, receiver, *report);
684 }
685 C.emitReport(std::move(report));
686}
687
688static bool supportsNilWithFloatRet(const llvm::Triple &triple) {
689 return (triple.getVendor() == llvm::Triple::Apple &&
690 (triple.isiOS() || triple.isWatchOS() ||
691 !triple.isMacOSXVersionLT(10,5)));
692}
693
694void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
695 ProgramStateRef state,
696 const ObjCMethodCall &Msg) const {
697 ASTContext &Ctx = C.getASTContext();
698
699 // Check the return type of the message expression. A message to nil will
700 // return different values depending on the return type and the architecture.
701 QualType RetTy = Msg.getResultType();
702 CanQualType CanRetTy = Ctx.getCanonicalType(RetTy);
703 const LocationContext *LCtx = C.getLocationContext();
704
705 if (CanRetTy->isStructureOrClassType()) {
706 // Structure returns are safe since the compiler zeroes them out.
707 SVal V = C.getSValBuilder().makeZeroVal(RetTy);
708 C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V));
709 return;
710 }
711
712 // Other cases: check if sizeof(return type) > sizeof(void*)
713 if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap()
714 .isConsumedExpr(Msg.getOriginExpr())) {
715 // Compute: sizeof(void *) and sizeof(return type)
716 const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);
717 const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy);
718
719 if (CanRetTy.getTypePtr()->isReferenceType()||
720 (voidPtrSize < returnTypeSize &&
722 (Ctx.FloatTy == CanRetTy ||
723 Ctx.DoubleTy == CanRetTy ||
724 Ctx.LongDoubleTy == CanRetTy ||
725 Ctx.LongLongTy == CanRetTy ||
726 Ctx.UnsignedLongLongTy == CanRetTy)))) {
727 if (ExplodedNode *N = C.generateErrorNode(state))
728 emitNilReceiverBug(C, Msg, N);
729 return;
730 }
731
732 // Handle the safe cases where the return value is 0 if the
733 // receiver is nil.
734 //
735 // FIXME: For now take the conservative approach that we only
736 // return null values if we *know* that the receiver is nil.
737 // This is because we can have surprises like:
738 //
739 // ... = [[NSScreens screens] objectAtIndex:0];
740 //
741 // What can happen is that [... screens] could return nil, but
742 // it most likely isn't nil. We should assume the semantics
743 // of this case unless we have *a lot* more knowledge.
744 //
745 SVal V = C.getSValBuilder().makeZeroVal(RetTy);
746 C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V));
747 return;
748 }
749
750 C.addTransition(state);
751}
752
753void ento::registerCallAndMessageChecker(CheckerManager &Mgr) {
754 CallAndMessageChecker *Chk = Mgr.registerChecker<CallAndMessageChecker>();
755
756#define QUERY_CHECKER_OPTION(OPTION) \
757 Chk->ChecksEnabled[CallAndMessageChecker::CK_##OPTION] = \
758 Mgr.getAnalyzerOptions().getCheckerBooleanOption( \
759 Mgr.getCurrentCheckerName(), #OPTION);
760
761 QUERY_CHECKER_OPTION(FunctionPointer)
763 QUERY_CHECKER_OPTION(CXXThisMethodCall)
764 QUERY_CHECKER_OPTION(CXXDeallocationArg)
765 QUERY_CHECKER_OPTION(ArgInitializedness)
766 QUERY_CHECKER_OPTION(ArgPointeeInitializedness)
767 QUERY_CHECKER_OPTION(NilReceiver)
768 QUERY_CHECKER_OPTION(UndefReceiver)
769
770 Chk->ArgPointeeInitializednessComplete =
771 Mgr.getAnalyzerOptions().getCheckerBooleanOption(
772 Mgr.getCurrentCheckerName(), "ArgPointeeInitializednessComplete");
773}
774
775bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &) {
776 return true;
777}
#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:909
CanQualType LongLongTy
bool isArrayFormAsWritten() const
Definition ExprCXX.h:2653
Expr * getCallee()
Definition Expr.h:3090
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:1087
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:3085
Represents a member of a struct/union/class.
Definition Decl.h:3160
const ParmVarDecl * getParamDecl(unsigned i) const
Definition Decl.h:2797
Expr * getInstanceReceiver()
Returns the object expression (receiver) for an instance message, or null for a message that is not a...
Definition ExprObjC.h:1265
Selector getSelector() const
Definition ExprObjC.cpp:289
SourceRange getReceiverRange() const
Source range of the receiver.
Definition ExprObjC.cpp:273
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:8365
field_range fields() const
Definition Decl.h:4527
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:338
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
bool isStructureType() const
Definition Type.cpp:679
bool isVoidType() const
Definition TypeBase.h:8891
bool isPointerType() const
Definition TypeBase.h:8529
bool isReferenceType() const
Definition TypeBase.h:8553
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:753
const RecordType * getAsStructureType() const
Definition Type.cpp:769
bool isPointerOrReferenceType() const
Definition TypeBase.h:8533
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:1229
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:713
Represents an abstract call to a function or method along a particular path.
Definition CallEvent.h:153
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:553
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:1261
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:1333
const ObjCMessageExpr * getOriginExpr() const override
Returns the expression whose value will be the result of this call.
Definition CallEvent.h:1287
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.
virtual QualType getValueType() const =0
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
const FunctionProtoType * T
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)