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 auto DescribeArgument = [ParamT, PointeeT,
325 &Call](bool ArgIsStruct) -> std::string {
326 if (PointeeT.isConstQualified())
327 return llvm::formatv(
328 Fmt: "this argument is const{0} and {1} input data of the function",
329 Vals: ParamT->isPointerType() ? " pointer" : "",
330 Vals: ArgIsStruct ? "may contain" : "is likely");
331 else
332 return llvm::formatv(
333 Fmt: "function '{0}' expects {1}this argument to be initialized",
334 Vals: cast<NamedDecl>(Val: Call.getDecl())->getNameAsString(),
335 Vals: 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 Fmt: "{0}{1} function call argument {2} an uninitialized value; {3}",
342 Vals: ArgumentNumber + 1, Vals: llvm::getOrdinalSuffix(Val: ArgumentNumber + 1),
343 Vals: ParamT->isPointerType() ? "points to" : "is",
344 Vals: DescribeArgument(/*ArgIsStruct=*/false));
345 auto R = std::make_unique<PathSensitiveBugReport>(args: BT, args&: Msg, args&: N);
346 R->addRange(R: Call.getArgSourceRange(Index: ArgumentNumber));
347 if (ArgEx)
348 bugreporter::trackExpressionValue(N, E: ArgEx, R&: *R);
349
350 C.emitReport(R: 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(R: D->getRegion())) {
362 if (ExplodedNode *N = C.generateErrorNode()) {
363 std::string Msg = llvm::formatv(
364 Fmt: "{0}{1} function call argument {2} an uninitialized value{3}; {4}",
365 Vals: (ArgumentNumber + 1), Vals: llvm::getOrdinalSuffix(Val: ArgumentNumber + 1),
366 Vals: ParamT->isPointerType() ? "points to" : "is", Vals&: F.FieldChain,
367 Vals: DescribeArgument(/*ArgIsStruct=*/true));
368 auto R = std::make_unique<PathSensitiveBugReport>(args: BT, args&: Msg, args&: N);
369 R->addRange(R: Call.getArgSourceRange(Index: ArgumentNumber));
370 if (ArgEx)
371 bugreporter::trackExpressionValue(N, E: ArgEx, R&: *R);
372
373 C.emitReport(R: 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>(args: BT, args: Os.str(), args&: N);
400
401 R->addRange(R: ArgRange);
402 if (ArgEx)
403 bugreporter::trackExpressionValue(N, E: ArgEx, R&: *R);
404 C.emitReport(R: 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(R: 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 Fmt: "Passed-by-value struct argument contains uninitialized data{0}",
426 Vals&: F.FieldChain);
427
428 // Generate a report for this bug.
429 auto R = std::make_unique<PathSensitiveBugReport>(args: BT, args&: Msg, args&: N);
430 R->addRange(R: ArgRange);
431
432 if (ArgEx)
433 bugreporter::trackExpressionValue(N, E: ArgEx, R&: *R);
434 // FIXME: enhance track back for uninitialized value for arbitrary
435 // memregions
436 C.emitReport(R: 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(E: Callee, SF: C.getStackFrame());
450
451 if (L.isUndef()) {
452 if (!ChecksEnabled[CK_FunctionPointer]) {
453 C.addSink(State);
454 return nullptr;
455 }
456 emitBadCall(BT: CallUndefBug, C, BadE: Callee);
457 return nullptr;
458 }
459
460 ProgramStateRef StNonNull, StNull;
461 std::tie(args&: StNonNull, args&: StNull) = State->assume(Cond: L.castAs<DefinedOrUnknownSVal>());
462
463 if (StNull && !StNonNull) {
464 if (!ChecksEnabled[CK_FunctionPointer]) {
465 C.addSink(State: StNull);
466 return nullptr;
467 }
468 emitBadCall(BT: CallNullBug, C, BadE: 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);
495 if (isa<AnyFunctionCall>(Val: Call)) {
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 R: std::make_unique<PathSensitiveBugReport>(args: CallFewArgsBug, args: os.str(), args&: 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(BT: CXXCallUndefBug, C, BadE: CC->getCXXThisExpr());
519 return nullptr;
520 }
521
522 ProgramStateRef StNonNull, StNull;
523 std::tie(args&: StNonNull, args&: StNull) = State->assume(Cond: V.castAs<DefinedOrUnknownSVal>());
524
525 if (StNull && !StNonNull) {
526 if (!ChecksEnabled[CK_CXXThisMethodCall]) {
527 C.addSink(State: StNull);
528 return nullptr;
529 }
530 emitBadCall(BT: CXXCallNullBug, C, BadE: CC->getCXXThisExpr());
531 return nullptr;
532 }
533
534 return StNonNull;
535}
536
537ProgramStateRef
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(E: 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>(args: CXXDeleteUndefBug, args&: Desc, args&: N);
561 bugreporter::trackExpressionValue(N, E: DE, R&: *R);
562 C.emitReport(R: 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>(Val: Call) ? MsgArgBug : CallArgBug;
579
580 const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Val: 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, V: Call.getArgSVal(Index: i), ArgRange: Call.getArgSourceRange(Index: i),
586 ArgEx: Call.getArgExpr(Index: i), ArgumentNumber: i, CheckUninitFields: 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>(Val: 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>(Val: &Call))
610 State = checkCXXMethodCall(CC, C, State);
611
612 if (!State)
613 return;
614
615 if (const auto *DC = dyn_cast<CXXDeallocatorCall>(Val: &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;
641 case OCM_PropertyAccess:
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>(args: *BT, args: BT->getDescription(), args&: N);
651 const ObjCMessageExpr *ME = msg.getOriginExpr();
652 R->addRange(R: ME->getReceiverRange());
653
654 // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet.
655 if (const Expr *ReceiverE = ME->getInstanceReceiver())
656 bugreporter::trackExpressionValue(N, E: ReceiverE, R&: *R);
657 C.emitReport(R: std::move(R));
658 }
659 return;
660 }
661}
662
663void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg,
664 CheckerContext &C) const {
665 HandleNilReceiver(C, state: 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&: 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&: os, Policy: C.getLangOpts());
690 os << "' that will be garbage";
691 }
692
693 auto report =
694 std::make_unique<PathSensitiveBugReport>(args: MsgRetBug, args: os.str(), args&: N);
695 report->addRange(R: ME->getReceiverRange());
696 // FIXME: This won't track "self" in messages to super.
697 if (const Expr *receiver = ME->getInstanceReceiver()) {
698 bugreporter::trackExpressionValue(N, E: receiver, R&: *report);
699 }
700 C.emitReport(R: 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(Major: 10,Minor: 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(T: 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(type: RetTy);
723 C.addTransition(State: state->BindExpr(E: 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(E: Msg.getOriginExpr())) {
730 // Compute: sizeof(void *) and sizeof(return type)
731 const uint64_t voidPtrSize = Ctx.getTypeSize(T: Ctx.VoidPtrTy);
732 const uint64_t returnTypeSize = Ctx.getTypeSize(T: CanRetTy);
733
734 if (CanRetTy.getTypePtr()->isReferenceType()||
735 (voidPtrSize < returnTypeSize &&
736 !(supportsNilWithFloatRet(triple: Ctx.getTargetInfo().getTriple()) &&
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: state))
743 emitNilReceiverBug(C, msg: 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(type: RetTy);
761 C.addTransition(State: state->BindExpr(E: Msg.getOriginExpr(), SF, V));
762 return;
763 }
764
765 C.addTransition(State: 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)
777 QUERY_CHECKER_OPTION(ParameterCount)
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 CheckerName: Mgr.getCurrentCheckerName(), OptionName: "ArgPointeeInitializednessComplete");
788}
789
790bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &) {
791 return true;
792}
793