1//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This defines CallAndMessageChecker, a builtin checker that checks for various
10// errors of call and objc message expressions.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/AST/ExprCXX.h"
15#include "clang/AST/ParentMap.h"
16#include "clang/Basic/TargetInfo.h"
17#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19#include "clang/StaticAnalyzer/Core/Checker.h"
20#include "clang/StaticAnalyzer/Core/CheckerManager.h"
21#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
22#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
23#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.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>(args: BT, args: BT.getDescription(), args&: N);
172 if (BadE) {
173 R->addRange(R: BadE->getSourceRange());
174 if (BadE->isGLValue())
175 BadE = bugreporter::getDerefExpr(S: BadE);
176 bugreporter::trackExpressionValue(N, E: BadE, R&: *R);
177 }
178 C.emitReport(R: std::move(R));
179}
180
181static void describeUninitializedArgumentInCall(const CallEvent &Call,
182 int ArgumentNumber,
183 llvm::raw_svector_ostream &Os) {
184 switch (Call.getKind()) {
185 case CE_ObjCMessage: {
186 const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Val: Call);
187 switch (Msg.getMessageKind()) {
188 case OCM_Message:
189 Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(Val: ArgumentNumber + 1)
190 << " argument in message expression is an uninitialized value";
191 return;
192 case OCM_PropertyAccess:
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(Val: ArgumentNumber + 1)
207 << " block call argument is an uninitialized value";
208 return;
209 default:
210 Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(Val: 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(FD: I, SuperRegion: R);
243 FieldChain.push_back(Elt: I);
244 T = I->getType();
245 if (T->isStructureType()) {
246 if (FindNotUninitialized ? !Find(R: FR) : Find(R: FR))
247 return !FindNotUninitialized;
248 } else {
249 SVal V = StoreMgr.getBinding(store, loc: 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 c: V, os&: Stream, each_fn: [&Stream](const FieldDecl *FD) { Stream << *FD; }, separator: ".");
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(R: SValMemRegion, T: PointeeT);
322 const Expr *ArgEx = Call.getArgExpr(Index: ArgumentNumber);
323
324 if (PointeeV.isUndef()) {
325 if (ExplodedNode *N = C.generateErrorNode()) {
326 std::string Msg = llvm::formatv(
327 Fmt: "{0}{1} function call argument is {2} uninitialized value",
328 Vals: ArgumentNumber + 1, Vals: llvm::getOrdinalSuffix(Val: ArgumentNumber + 1),
329 Vals: ParamT->isPointerType() ? "a pointer to" : "an");
330 auto R = std::make_unique<PathSensitiveBugReport>(args: BT, args&: Msg, args&: N);
331 R->addRange(R: Call.getArgSourceRange(Index: ArgumentNumber));
332 if (ArgEx)
333 bugreporter::trackExpressionValue(N, E: ArgEx, R&: *R);
334
335 C.emitReport(R: 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(R: D->getRegion())) {
347 if (ExplodedNode *N = C.generateErrorNode()) {
348 std::string Msg = llvm::formatv(
349 Fmt: "{0}{1} function call argument {2} an uninitialized value{3}",
350 Vals: (ArgumentNumber + 1), Vals: llvm::getOrdinalSuffix(Val: ArgumentNumber + 1),
351 Vals: ParamT->isPointerType() ? "points to" : "references", Vals&: F.FieldChain);
352 auto R = std::make_unique<PathSensitiveBugReport>(args: BT, args&: Msg, args&: N);
353 R->addRange(R: Call.getArgSourceRange(Index: ArgumentNumber));
354 if (ArgEx)
355 bugreporter::trackExpressionValue(N, E: ArgEx, R&: *R);
356
357 C.emitReport(R: 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>(args: BT, args: Os.str(), args&: N);
384
385 R->addRange(R: ArgRange);
386 if (ArgEx)
387 bugreporter::trackExpressionValue(N, E: ArgEx, R&: *R);
388 C.emitReport(R: 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(R: 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 Fmt: "Passed-by-value struct argument contains uninitialized data{0}",
410 Vals&: F.FieldChain);
411
412 // Generate a report for this bug.
413 auto R = std::make_unique<PathSensitiveBugReport>(args: BT, args&: Msg, args&: N);
414 R->addRange(R: ArgRange);
415
416 if (ArgEx)
417 bugreporter::trackExpressionValue(N, E: ArgEx, R&: *R);
418 // FIXME: enhance track back for uninitialized value for arbitrary
419 // memregions
420 C.emitReport(R: 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(E: Callee, SF: C.getStackFrame());
434
435 if (L.isUndef()) {
436 if (!ChecksEnabled[CK_FunctionPointer]) {
437 C.addSink(State);
438 return nullptr;
439 }
440 emitBadCall(BT: CallUndefBug, C, BadE: Callee);
441 return nullptr;
442 }
443
444 ProgramStateRef StNonNull, StNull;
445 std::tie(args&: StNonNull, args&: StNull) = State->assume(Cond: L.castAs<DefinedOrUnknownSVal>());
446
447 if (StNull && !StNonNull) {
448 if (!ChecksEnabled[CK_FunctionPointer]) {
449 C.addSink(State: StNull);
450 return nullptr;
451 }
452 emitBadCall(BT: CallNullBug, C, BadE: 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);
479 if (isa<AnyFunctionCall>(Val: Call)) {
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 R: std::make_unique<PathSensitiveBugReport>(args: CallFewArgsBug, args: os.str(), args&: 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(BT: CXXCallUndefBug, C, BadE: CC->getCXXThisExpr());
503 return nullptr;
504 }
505
506 ProgramStateRef StNonNull, StNull;
507 std::tie(args&: StNonNull, args&: StNull) = State->assume(Cond: V.castAs<DefinedOrUnknownSVal>());
508
509 if (StNull && !StNonNull) {
510 if (!ChecksEnabled[CK_CXXThisMethodCall]) {
511 C.addSink(State: StNull);
512 return nullptr;
513 }
514 emitBadCall(BT: CXXCallNullBug, C, BadE: CC->getCXXThisExpr());
515 return nullptr;
516 }
517
518 return StNonNull;
519}
520
521ProgramStateRef
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(E: 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>(args: CXXDeleteUndefBug, args&: Desc, args&: N);
545 bugreporter::trackExpressionValue(N, E: DE, R&: *R);
546 C.emitReport(R: 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>(Val: Call) ? MsgArgBug : CallArgBug;
563
564 const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Val: 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, V: Call.getArgSVal(Index: i), ArgRange: Call.getArgSourceRange(Index: i),
570 ArgEx: Call.getArgExpr(Index: i), ArgumentNumber: i, CheckUninitFields: 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>(Val: 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>(Val: &Call))
594 State = checkCXXMethodCall(CC, C, State);
595
596 if (!State)
597 return;
598
599 if (const auto *DC = dyn_cast<CXXDeallocatorCall>(Val: &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;
625 case OCM_PropertyAccess:
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>(args: *BT, args: BT->getDescription(), args&: N);
635 const ObjCMessageExpr *ME = msg.getOriginExpr();
636 R->addRange(R: ME->getReceiverRange());
637
638 // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet.
639 if (const Expr *ReceiverE = ME->getInstanceReceiver())
640 bugreporter::trackExpressionValue(N, E: ReceiverE, R&: *R);
641 C.emitReport(R: std::move(R));
642 }
643 return;
644 }
645}
646
647void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg,
648 CheckerContext &C) const {
649 HandleNilReceiver(C, state: 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&: 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&: os, Policy: C.getLangOpts());
674 os << "' that will be garbage";
675 }
676
677 auto report =
678 std::make_unique<PathSensitiveBugReport>(args: MsgRetBug, args: os.str(), args&: N);
679 report->addRange(R: ME->getReceiverRange());
680 // FIXME: This won't track "self" in messages to super.
681 if (const Expr *receiver = ME->getInstanceReceiver()) {
682 bugreporter::trackExpressionValue(N, E: receiver, R&: *report);
683 }
684 C.emitReport(R: 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(Major: 10,Minor: 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(T: 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(type: RetTy);
707 C.addTransition(State: state->BindExpr(E: 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(E: Msg.getOriginExpr())) {
714 // Compute: sizeof(void *) and sizeof(return type)
715 const uint64_t voidPtrSize = Ctx.getTypeSize(T: Ctx.VoidPtrTy);
716 const uint64_t returnTypeSize = Ctx.getTypeSize(T: CanRetTy);
717
718 if (CanRetTy.getTypePtr()->isReferenceType()||
719 (voidPtrSize < returnTypeSize &&
720 !(supportsNilWithFloatRet(triple: Ctx.getTargetInfo().getTriple()) &&
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: state))
727 emitNilReceiverBug(C, msg: 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(type: RetTy);
745 C.addTransition(State: state->BindExpr(E: Msg.getOriginExpr(), SF, V));
746 return;
747 }
748
749 C.addTransition(State: 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)
761 QUERY_CHECKER_OPTION(ParameterCount)
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 CheckerName: Mgr.getCurrentCheckerName(), OptionName: "ArgPointeeInitializednessComplete");
772}
773
774bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &) {
775 return true;
776}
777