clang 24.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 auto DescribeArgument = [ParamT, PointeeT,
325 &Call](bool ArgIsStruct) -> std::string {
326 if (PointeeT.isConstQualified())
327 return llvm::formatv(
328 "this argument is const{0} and {1} input data of the function",
329 ParamT->isPointerType() ? " pointer" : "",
330 ArgIsStruct ? "may contain" : "is likely");
331 else
332 return llvm::formatv(
333 "function '{0}' expects {1}this argument to be initialized",
334 cast<NamedDecl>(Call.getDecl())->getNameAsString(),
335 ParamT->isPointerType() ? "memory pointed to by " : "");
336 };
337
338 if (PointeeV.isUndef()) {
339 if (ExplodedNode *N = C.generateErrorNode()) {
340 std::string Msg = llvm::formatv(
341 "{0}{1} function call argument {2} an uninitialized value; {3}",
342 ArgumentNumber + 1, llvm::getOrdinalSuffix(ArgumentNumber + 1),
343 ParamT->isPointerType() ? "points to" : "is",
344 DescribeArgument(/*ArgIsStruct=*/false));
345 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
346 R->addRange(Call.getArgSourceRange(ArgumentNumber));
347 if (ArgEx)
349
350 C.emitReport(std::move(R));
351 }
352 return true;
353 }
354
355 if (auto LV = PointeeV.getAs<nonloc::LazyCompoundVal>()) {
356 const LazyCompoundValData *D = LV->getCVData();
357 FindUninitializedField F(C.getState()->getStateManager().getStoreManager(),
358 C.getSValBuilder().getRegionManager(),
359 D->getStore(), AllowPartialInitializedness);
360
361 if (F.Find(D->getRegion())) {
362 if (ExplodedNode *N = C.generateErrorNode()) {
363 std::string Msg = llvm::formatv(
364 "{0}{1} function call argument {2} an uninitialized value{3}; {4}",
365 (ArgumentNumber + 1), llvm::getOrdinalSuffix(ArgumentNumber + 1),
366 ParamT->isPointerType() ? "points to" : "is", F.FieldChain,
367 DescribeArgument(/*ArgIsStruct=*/true));
368 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
369 R->addRange(Call.getArgSourceRange(ArgumentNumber));
370 if (ArgEx)
372
373 C.emitReport(std::move(R));
374 }
375 return true;
376 }
377 }
378
379 return false;
380}
381
382bool CallAndMessageChecker::PreVisitProcessArg(
383 CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,
384 int ArgumentNumber, bool CheckUninitFields, const CallEvent &Call,
385 const BugType &BT, const ParmVarDecl *ParamDecl) const {
386 if (uninitRefOrPointer(C, V, Call, BT, ParamDecl, ArgumentNumber))
387 return true;
388
389 if (V.isUndef()) {
390 if (!ChecksEnabled[CK_ArgInitializedness]) {
391 C.addSink();
392 return true;
393 }
394 if (ExplodedNode *N = C.generateErrorNode()) {
395 // Generate a report for this bug.
396 SmallString<200> Buf;
397 llvm::raw_svector_ostream Os(Buf);
398 describeUninitializedArgumentInCall(Call, ArgumentNumber, Os);
399 auto R = std::make_unique<PathSensitiveBugReport>(BT, Os.str(), N);
400
401 R->addRange(ArgRange);
402 if (ArgEx)
404 C.emitReport(std::move(R));
405 }
406 return true;
407 }
408
409 if (!CheckUninitFields)
410 return false;
411
412 if (auto LV = V.getAs<nonloc::LazyCompoundVal>()) {
413 const LazyCompoundValData *D = LV->getCVData();
414 FindUninitializedField F(C.getState()->getStateManager().getStoreManager(),
415 C.getSValBuilder().getRegionManager(),
416 D->getStore());
417
418 if (F.Find(D->getRegion())) {
419 if (!ChecksEnabled[CK_ArgInitializedness]) {
420 C.addSink();
421 return true;
422 }
423 if (ExplodedNode *N = C.generateErrorNode()) {
424 std::string Msg = llvm::formatv(
425 "Passed-by-value struct argument contains uninitialized data{0}",
426 F.FieldChain);
427
428 // Generate a report for this bug.
429 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
430 R->addRange(ArgRange);
431
432 if (ArgEx)
434 // FIXME: enhance track back for uninitialized value for arbitrary
435 // memregions
436 C.emitReport(std::move(R));
437 }
438 return true;
439 }
440 }
441
442 return false;
443}
444
445ProgramStateRef CallAndMessageChecker::checkFunctionPointerCall(
446 const CallExpr *CE, CheckerContext &C, ProgramStateRef State) const {
447
448 const Expr *Callee = CE->getCallee()->IgnoreParens();
449 SVal L = State->getSVal(Callee, C.getStackFrame());
450
451 if (L.isUndef()) {
452 if (!ChecksEnabled[CK_FunctionPointer]) {
453 C.addSink(State);
454 return nullptr;
455 }
456 emitBadCall(CallUndefBug, C, Callee);
457 return nullptr;
458 }
459
460 ProgramStateRef StNonNull, StNull;
461 std::tie(StNonNull, StNull) = State->assume(L.castAs<DefinedOrUnknownSVal>());
462
463 if (StNull && !StNonNull) {
464 if (!ChecksEnabled[CK_FunctionPointer]) {
465 C.addSink(StNull);
466 return nullptr;
467 }
468 emitBadCall(CallNullBug, C, Callee);
469 return nullptr;
470 }
471
472 return StNonNull;
473}
474
475ProgramStateRef CallAndMessageChecker::checkParameterCount(
476 const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const {
477
478 // If we have a function or block declaration, we can make sure we pass
479 // enough parameters.
480 unsigned Params = Call.parameters().size();
481 if (Call.getNumArgs() >= Params)
482 return State;
483
484 if (!ChecksEnabled[CK_ParameterCount]) {
485 C.addSink(State);
486 return nullptr;
487 }
488
489 ExplodedNode *N = C.generateErrorNode();
490 if (!N)
491 return nullptr;
492
493 SmallString<512> Str;
494 llvm::raw_svector_ostream os(Str);
496 os << "Function ";
497 } else {
498 assert(isa<BlockCall>(Call));
499 os << "Block ";
500 }
501 os << "taking " << Params << " argument" << (Params == 1 ? "" : "s")
502 << " is called with fewer (" << Call.getNumArgs() << ")";
503
504 C.emitReport(
505 std::make_unique<PathSensitiveBugReport>(CallFewArgsBug, os.str(), N));
506 return nullptr;
507}
508
509ProgramStateRef CallAndMessageChecker::checkCXXMethodCall(
510 const CXXInstanceCall *CC, CheckerContext &C, ProgramStateRef State) const {
511
512 SVal V = CC->getCXXThisVal();
513 if (V.isUndef()) {
514 if (!ChecksEnabled[CK_CXXThisMethodCall]) {
515 C.addSink(State);
516 return nullptr;
517 }
518 emitBadCall(CXXCallUndefBug, C, CC->getCXXThisExpr());
519 return nullptr;
520 }
521
522 ProgramStateRef StNonNull, StNull;
523 std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
524
525 if (StNull && !StNonNull) {
526 if (!ChecksEnabled[CK_CXXThisMethodCall]) {
527 C.addSink(StNull);
528 return nullptr;
529 }
530 emitBadCall(CXXCallNullBug, C, CC->getCXXThisExpr());
531 return nullptr;
532 }
533
534 return StNonNull;
535}
536
538CallAndMessageChecker::checkCXXDeallocation(const CXXDeallocatorCall *DC,
539 CheckerContext &C,
540 ProgramStateRef State) const {
541 const CXXDeleteExpr *DE = DC->getOriginExpr();
542 assert(DE);
543 SVal Arg = C.getSVal(DE->getArgument());
544 if (!Arg.isUndef())
545 return State;
546
547 if (!ChecksEnabled[CK_CXXDeallocationArg]) {
548 C.addSink(State);
549 return nullptr;
550 }
551
552 StringRef Desc;
553 ExplodedNode *N = C.generateErrorNode();
554 if (!N)
555 return nullptr;
556 if (DE->isArrayFormAsWritten())
557 Desc = "Argument to 'delete[]' is uninitialized";
558 else
559 Desc = "Argument to 'delete' is uninitialized";
560 auto R = std::make_unique<PathSensitiveBugReport>(CXXDeleteUndefBug, Desc, N);
562 C.emitReport(std::move(R));
563 return nullptr;
564}
565
566ProgramStateRef CallAndMessageChecker::checkArgInitializedness(
567 const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const {
568
569 const Decl *D = Call.getDecl();
570
571 // Don't check for uninitialized field values in arguments if the
572 // caller has a body that is available and we have the chance to inline it.
573 // This is a hack, but is a reasonable compromise betweens sometimes warning
574 // and sometimes not depending on if we decide to inline a function.
575 const bool checkUninitFields =
576 !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody()));
577
578 const BugType &BT = isa<ObjCMethodCall>(Call) ? MsgArgBug : CallArgBug;
579
580 const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
581 for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) {
582 const ParmVarDecl *ParamDecl = nullptr;
583 if (FD && i < FD->getNumParams())
584 ParamDecl = FD->getParamDecl(i);
585 if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
586 Call.getArgExpr(i), i, checkUninitFields, Call, BT,
587 ParamDecl))
588 return nullptr;
589 }
590 return State;
591}
592
593void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
594 CheckerContext &C) const {
595 ProgramStateRef State = C.getState();
596
597 if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()))
598 State = checkFunctionPointerCall(CE, C, State);
599
600 if (!State)
601 return;
602
603 if (Call.getDecl())
604 State = checkParameterCount(Call, C, State);
605
606 if (!State)
607 return;
608
609 if (const auto *CC = dyn_cast<CXXInstanceCall>(&Call))
610 State = checkCXXMethodCall(CC, C, State);
611
612 if (!State)
613 return;
614
615 if (const auto *DC = dyn_cast<CXXDeallocatorCall>(&Call))
616 State = checkCXXDeallocation(DC, C, State);
617
618 if (!State)
619 return;
620
621 State = checkArgInitializedness(Call, C, State);
622
623 // If we make it here, record our assumptions about the callee.
624 C.addTransition(State);
625}
626
627void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
628 CheckerContext &C) const {
629 SVal recVal = msg.getReceiverSVal();
630 if (recVal.isUndef()) {
631 if (!ChecksEnabled[CK_UndefReceiver]) {
632 C.addSink();
633 return;
634 }
635 if (ExplodedNode *N = C.generateErrorNode()) {
636 const BugType *BT = nullptr;
637 switch (msg.getMessageKind()) {
638 case OCM_Message:
639 BT = &MsgUndefBug;
640 break;
642 BT = &ObjCPropUndefBug;
643 break;
644 case OCM_Subscript:
645 BT = &ObjCSubscriptUndefBug;
646 break;
647 }
648 assert(BT && "Unknown message kind.");
649
650 auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
651 const ObjCMessageExpr *ME = msg.getOriginExpr();
652 R->addRange(ME->getReceiverRange());
653
654 // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet.
655 if (const Expr *ReceiverE = ME->getInstanceReceiver())
656 bugreporter::trackExpressionValue(N, ReceiverE, *R);
657 C.emitReport(std::move(R));
658 }
659 return;
660 }
661}
662
663void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg,
664 CheckerContext &C) const {
665 HandleNilReceiver(C, C.getState(), msg);
666}
667
668void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
669 const ObjCMethodCall &msg,
670 ExplodedNode *N) const {
671 if (!ChecksEnabled[CK_NilReceiver]) {
672 C.addSink();
673 return;
674 }
675
676 const ObjCMessageExpr *ME = msg.getOriginExpr();
677
678 QualType ResTy = msg.getResultType();
679
680 SmallString<200> buf;
681 llvm::raw_svector_ostream os(buf);
682 os << "The receiver of message '";
683 ME->getSelector().print(os);
684 os << "' is nil";
685 if (ResTy->isReferenceType()) {
686 os << ", which results in forming a null reference";
687 } else {
688 os << " and returns a value of type '";
689 msg.getResultType().print(os, C.getLangOpts());
690 os << "' that will be garbage";
691 }
692
693 auto report =
694 std::make_unique<PathSensitiveBugReport>(MsgRetBug, os.str(), N);
695 report->addRange(ME->getReceiverRange());
696 // FIXME: This won't track "self" in messages to super.
697 if (const Expr *receiver = ME->getInstanceReceiver()) {
698 bugreporter::trackExpressionValue(N, receiver, *report);
699 }
700 C.emitReport(std::move(report));
701}
702
703static bool supportsNilWithFloatRet(const llvm::Triple &triple) {
704 return (triple.getVendor() == llvm::Triple::Apple &&
705 (triple.isiOS() || triple.isWatchOS() ||
706 !triple.isMacOSXVersionLT(10,5)));
707}
708
709void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
710 ProgramStateRef state,
711 const ObjCMethodCall &Msg) const {
712 ASTContext &Ctx = C.getASTContext();
713
714 // Check the return type of the message expression. A message to nil will
715 // return different values depending on the return type and the architecture.
716 QualType RetTy = Msg.getResultType();
717 CanQualType CanRetTy = Ctx.getCanonicalType(RetTy);
718 const StackFrame *SF = C.getStackFrame();
719
720 if (CanRetTy->isStructureOrClassType()) {
721 // Structure returns are safe since the compiler zeroes them out.
722 SVal V = C.getSValBuilder().makeZeroVal(RetTy);
723 C.addTransition(state->BindExpr(Msg.getOriginExpr(), SF, V));
724 return;
725 }
726
727 // Other cases: check if sizeof(return type) > sizeof(void*)
728 if (CanRetTy != Ctx.VoidTy &&
729 C.getStackFrame()->getParentMap().isConsumedExpr(Msg.getOriginExpr())) {
730 // Compute: sizeof(void *) and sizeof(return type)
731 const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);
732 const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy);
733
734 if (CanRetTy.getTypePtr()->isReferenceType()||
735 (voidPtrSize < returnTypeSize &&
737 (Ctx.FloatTy == CanRetTy ||
738 Ctx.DoubleTy == CanRetTy ||
739 Ctx.LongDoubleTy == CanRetTy ||
740 Ctx.LongLongTy == CanRetTy ||
741 Ctx.UnsignedLongLongTy == CanRetTy)))) {
742 if (ExplodedNode *N = C.generateErrorNode(state))
743 emitNilReceiverBug(C, Msg, N);
744 return;
745 }
746
747 // Handle the safe cases where the return value is 0 if the
748 // receiver is nil.
749 //
750 // FIXME: For now take the conservative approach that we only
751 // return null values if we *know* that the receiver is nil.
752 // This is because we can have surprises like:
753 //
754 // ... = [[NSScreens screens] objectAtIndex:0];
755 //
756 // What can happen is that [... screens] could return nil, but
757 // it most likely isn't nil. We should assume the semantics
758 // of this case unless we have *a lot* more knowledge.
759 //
760 SVal V = C.getSValBuilder().makeZeroVal(RetTy);
761 C.addTransition(state->BindExpr(Msg.getOriginExpr(), SF, V));
762 return;
763 }
764
765 C.addTransition(state);
766}
767
768void ento::registerCallAndMessageChecker(CheckerManager &Mgr) {
769 CallAndMessageChecker *Chk = Mgr.registerChecker<CallAndMessageChecker>();
770
771#define QUERY_CHECKER_OPTION(OPTION) \
772 Chk->ChecksEnabled[CallAndMessageChecker::CK_##OPTION] = \
773 Mgr.getAnalyzerOptions().getCheckerBooleanOption( \
774 Mgr.getCurrentCheckerName(), #OPTION);
775
776 QUERY_CHECKER_OPTION(FunctionPointer)
778 QUERY_CHECKER_OPTION(CXXThisMethodCall)
779 QUERY_CHECKER_OPTION(CXXDeallocationArg)
780 QUERY_CHECKER_OPTION(ArgInitializedness)
781 QUERY_CHECKER_OPTION(ArgPointeeInitializedness)
782 QUERY_CHECKER_OPTION(NilReceiver)
783 QUERY_CHECKER_OPTION(UndefReceiver)
784
785 Chk->ArgPointeeInitializednessComplete =
786 Mgr.getAnalyzerOptions().getCheckerBooleanOption(
787 Mgr.getCurrentCheckerName(), "ArgPointeeInitializednessComplete");
788}
789
790bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &) {
791 return true;
792}
#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 "<".
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:943
CanQualType LongLongTy
bool isArrayFormAsWritten() const
Definition ExprCXX.h:2657
Expr * getCallee()
Definition Expr.h:3110
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:1104
This represents one expression.
Definition Expr.h:113
bool isGLValue() const
Definition Expr.h:288
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition Expr.cpp:3097
Represents a member of a struct/union/class.
Definition Decl.h:3294
const ParmVarDecl * getParamDecl(unsigned i) const
Definition Decl.h:2927
Expr * getInstanceReceiver()
Returns the object expression (receiver) for an instance message, or null for a message that is not a...
Definition ExprObjC.h:1301
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:8575
field_range fields() const
Definition Decl.h:4662
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:9111
bool isPointerType() const
Definition TypeBase.h:8739
bool isReferenceType() const
Definition TypeBase.h:8763
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:8743
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:565
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:113
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:88
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition SVals.h:84
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.
Top level wrappers for InstallAPI frontend operations.
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)