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 const LocationContext *LCtx = C.getLocationContext();
434 SVal L = State->getSVal(Ex: Callee, LCtx);
435
436 if (L.isUndef()) {
437 if (!ChecksEnabled[CK_FunctionPointer]) {
438 C.addSink(State);
439 return nullptr;
440 }
441 emitBadCall(BT: CallUndefBug, C, BadE: Callee);
442 return nullptr;
443 }
444
445 ProgramStateRef StNonNull, StNull;
446 std::tie(args&: StNonNull, args&: StNull) = State->assume(Cond: L.castAs<DefinedOrUnknownSVal>());
447
448 if (StNull && !StNonNull) {
449 if (!ChecksEnabled[CK_FunctionPointer]) {
450 C.addSink(State: StNull);
451 return nullptr;
452 }
453 emitBadCall(BT: CallNullBug, C, BadE: 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);
480 if (isa<AnyFunctionCall>(Val: Call)) {
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 R: std::make_unique<PathSensitiveBugReport>(args: CallFewArgsBug, args: os.str(), args&: 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(BT: CXXCallUndefBug, C, BadE: CC->getCXXThisExpr());
504 return nullptr;
505 }
506
507 ProgramStateRef StNonNull, StNull;
508 std::tie(args&: StNonNull, args&: StNull) = State->assume(Cond: V.castAs<DefinedOrUnknownSVal>());
509
510 if (StNull && !StNonNull) {
511 if (!ChecksEnabled[CK_CXXThisMethodCall]) {
512 C.addSink(State: StNull);
513 return nullptr;
514 }
515 emitBadCall(BT: CXXCallNullBug, C, BadE: CC->getCXXThisExpr());
516 return nullptr;
517 }
518
519 return StNonNull;
520}
521
522ProgramStateRef
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(S: 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>(args: CXXDeleteUndefBug, args&: Desc, args&: N);
546 bugreporter::trackExpressionValue(N, E: DE, R&: *R);
547 C.emitReport(R: 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>(Val: Call) ? MsgArgBug : CallArgBug;
564
565 const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Val: 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, V: Call.getArgSVal(Index: i), ArgRange: Call.getArgSourceRange(Index: i),
571 ArgEx: Call.getArgExpr(Index: i), ArgumentNumber: i, CheckUninitFields: 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>(Val: 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>(Val: &Call))
595 State = checkCXXMethodCall(CC, C, State);
596
597 if (!State)
598 return;
599
600 if (const auto *DC = dyn_cast<CXXDeallocatorCall>(Val: &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;
626 case OCM_PropertyAccess:
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>(args: *BT, args: BT->getDescription(), args&: N);
636 const ObjCMessageExpr *ME = msg.getOriginExpr();
637 R->addRange(R: ME->getReceiverRange());
638
639 // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet.
640 if (const Expr *ReceiverE = ME->getInstanceReceiver())
641 bugreporter::trackExpressionValue(N, E: ReceiverE, R&: *R);
642 C.emitReport(R: std::move(R));
643 }
644 return;
645 }
646}
647
648void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg,
649 CheckerContext &C) const {
650 HandleNilReceiver(C, state: 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&: 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&: os, Policy: C.getLangOpts());
675 os << "' that will be garbage";
676 }
677
678 auto report =
679 std::make_unique<PathSensitiveBugReport>(args: MsgRetBug, args: os.str(), args&: N);
680 report->addRange(R: ME->getReceiverRange());
681 // FIXME: This won't track "self" in messages to super.
682 if (const Expr *receiver = ME->getInstanceReceiver()) {
683 bugreporter::trackExpressionValue(N, E: receiver, R&: *report);
684 }
685 C.emitReport(R: 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(Major: 10,Minor: 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(T: 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(type: RetTy);
708 C.addTransition(State: state->BindExpr(S: 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(E: Msg.getOriginExpr())) {
715 // Compute: sizeof(void *) and sizeof(return type)
716 const uint64_t voidPtrSize = Ctx.getTypeSize(T: Ctx.VoidPtrTy);
717 const uint64_t returnTypeSize = Ctx.getTypeSize(T: CanRetTy);
718
719 if (CanRetTy.getTypePtr()->isReferenceType()||
720 (voidPtrSize < returnTypeSize &&
721 !(supportsNilWithFloatRet(triple: Ctx.getTargetInfo().getTriple()) &&
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: state))
728 emitNilReceiverBug(C, msg: 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(type: RetTy);
746 C.addTransition(State: state->BindExpr(S: Msg.getOriginExpr(), LCtx, V));
747 return;
748 }
749
750 C.addTransition(State: 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)
762 QUERY_CHECKER_OPTION(ParameterCount)
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 CheckerName: Mgr.getCurrentCheckerName(), OptionName: "ArgPointeeInitializednessComplete");
773}
774
775bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &) {
776 return true;
777}
778