1 //== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- 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 file defines BasicObjCFoundationChecks, a class that encapsulates
10 // a set of simple checks to run on Objective-C code using Apple's Foundation
11 // classes.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include "clang/AST/ASTContext.h"
16 #include "clang/AST/DeclObjC.h"
17 #include "clang/AST/Expr.h"
18 #include "clang/AST/ExprObjC.h"
19 #include "clang/AST/StmtObjC.h"
20 #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
21 #include "clang/Analysis/SelectorExtras.h"
22 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
23 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
24 #include "clang/StaticAnalyzer/Core/Checker.h"
25 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
27 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
28 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
29 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
30 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
31 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
32 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
33 #include "llvm/ADT/STLExtras.h"
34 #include "llvm/ADT/SmallString.h"
35 #include "llvm/ADT/StringMap.h"
36 #include "llvm/Support/raw_ostream.h"
37 #include <optional>
38
39 using namespace clang;
40 using namespace ento;
41 using namespace llvm;
42
43 namespace {
44 class APIMisuse : public BugType {
45 public:
APIMisuse(const CheckerBase * checker,const char * name)46 APIMisuse(const CheckerBase *checker, const char *name)
47 : BugType(checker, name, categories::AppleAPIMisuse) {}
48 };
49 } // end anonymous namespace
50
51 //===----------------------------------------------------------------------===//
52 // Utility functions.
53 //===----------------------------------------------------------------------===//
54
GetReceiverInterfaceName(const ObjCMethodCall & msg)55 static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) {
56 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
57 return ID->getIdentifier()->getName();
58 return StringRef();
59 }
60
61 enum FoundationClass {
62 FC_None,
63 FC_NSArray,
64 FC_NSDictionary,
65 FC_NSEnumerator,
66 FC_NSNull,
67 FC_NSOrderedSet,
68 FC_NSSet,
69 FC_NSString
70 };
71
findKnownClass(const ObjCInterfaceDecl * ID,bool IncludeSuperclasses=true)72 static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID,
73 bool IncludeSuperclasses = true) {
74 static llvm::StringMap<FoundationClass> Classes;
75 if (Classes.empty()) {
76 Classes["NSArray"] = FC_NSArray;
77 Classes["NSDictionary"] = FC_NSDictionary;
78 Classes["NSEnumerator"] = FC_NSEnumerator;
79 Classes["NSNull"] = FC_NSNull;
80 Classes["NSOrderedSet"] = FC_NSOrderedSet;
81 Classes["NSSet"] = FC_NSSet;
82 Classes["NSString"] = FC_NSString;
83 }
84
85 // FIXME: Should we cache this at all?
86 FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
87 if (result == FC_None && IncludeSuperclasses)
88 if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
89 return findKnownClass(Super);
90
91 return result;
92 }
93
94 //===----------------------------------------------------------------------===//
95 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
96 //===----------------------------------------------------------------------===//
97
98 namespace {
99 class NilArgChecker : public Checker<check::PreObjCMessage,
100 check::PostStmt<ObjCDictionaryLiteral>,
101 check::PostStmt<ObjCArrayLiteral>,
102 EventDispatcher<ImplicitNullDerefEvent>> {
103 mutable std::unique_ptr<APIMisuse> BT;
104
105 mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
106 mutable Selector ArrayWithObjectSel;
107 mutable Selector AddObjectSel;
108 mutable Selector InsertObjectAtIndexSel;
109 mutable Selector ReplaceObjectAtIndexWithObjectSel;
110 mutable Selector SetObjectAtIndexedSubscriptSel;
111 mutable Selector ArrayByAddingObjectSel;
112 mutable Selector DictionaryWithObjectForKeySel;
113 mutable Selector SetObjectForKeySel;
114 mutable Selector SetObjectForKeyedSubscriptSel;
115 mutable Selector RemoveObjectForKeySel;
116
117 void warnIfNilExpr(const Expr *E, const char *Msg, CheckerContext &C) const;
118
119 void warnIfNilArg(CheckerContext &C, const ObjCMethodCall &msg, unsigned Arg,
120 FoundationClass Class, bool CanBeSubscript = false) const;
121
122 void generateBugReport(ExplodedNode *N, StringRef Msg, SourceRange Range,
123 const Expr *Expr, CheckerContext &C) const;
124
125 public:
126 void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
127 void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const;
128 void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const;
129 };
130 } // end anonymous namespace
131
warnIfNilExpr(const Expr * E,const char * Msg,CheckerContext & C) const132 void NilArgChecker::warnIfNilExpr(const Expr *E,
133 const char *Msg,
134 CheckerContext &C) const {
135 auto Location = C.getSVal(E).getAs<Loc>();
136 if (!Location)
137 return;
138
139 auto [NonNull, Null] = C.getState()->assume(*Location);
140
141 // If it's known to be null.
142 if (!NonNull && Null) {
143 if (ExplodedNode *N = C.generateErrorNode()) {
144 generateBugReport(N, Msg, E->getSourceRange(), E, C);
145 return;
146 }
147 }
148
149 // If it might be null, assume that it cannot after this operation.
150 if (Null) {
151 // One needs to make sure the pointer is non-null to be used here.
152 if (ExplodedNode *N = C.generateSink(Null, C.getPredecessor())) {
153 dispatchEvent({*Location, /*IsLoad=*/false, N, &C.getBugReporter(),
154 /*IsDirectDereference=*/false});
155 }
156 C.addTransition(NonNull);
157 }
158 }
159
warnIfNilArg(CheckerContext & C,const ObjCMethodCall & msg,unsigned int Arg,FoundationClass Class,bool CanBeSubscript) const160 void NilArgChecker::warnIfNilArg(CheckerContext &C,
161 const ObjCMethodCall &msg,
162 unsigned int Arg,
163 FoundationClass Class,
164 bool CanBeSubscript) const {
165 // Check if the argument is nil.
166 ProgramStateRef State = C.getState();
167 if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
168 return;
169
170 // NOTE: We cannot throw non-fatal errors from warnIfNilExpr,
171 // because it's called multiple times from some callers, so it'd cause
172 // an unwanted state split if two or more non-fatal errors are thrown
173 // within the same checker callback. For now we don't want to, but
174 // it'll need to be fixed if we ever want to.
175 if (ExplodedNode *N = C.generateErrorNode()) {
176 SmallString<128> sbuf;
177 llvm::raw_svector_ostream os(sbuf);
178
179 if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) {
180
181 if (Class == FC_NSArray) {
182 os << "Array element cannot be nil";
183 } else if (Class == FC_NSDictionary) {
184 if (Arg == 0) {
185 os << "Value stored into '";
186 os << GetReceiverInterfaceName(msg) << "' cannot be nil";
187 } else {
188 assert(Arg == 1);
189 os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil";
190 }
191 } else
192 llvm_unreachable("Missing foundation class for the subscript expr");
193
194 } else {
195 if (Class == FC_NSDictionary) {
196 if (Arg == 0)
197 os << "Value argument ";
198 else {
199 assert(Arg == 1);
200 os << "Key argument ";
201 }
202 os << "to '";
203 msg.getSelector().print(os);
204 os << "' cannot be nil";
205 } else {
206 os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '";
207 msg.getSelector().print(os);
208 os << "' cannot be nil";
209 }
210 }
211
212 generateBugReport(N, os.str(), msg.getArgSourceRange(Arg),
213 msg.getArgExpr(Arg), C);
214 }
215 }
216
generateBugReport(ExplodedNode * N,StringRef Msg,SourceRange Range,const Expr * E,CheckerContext & C) const217 void NilArgChecker::generateBugReport(ExplodedNode *N,
218 StringRef Msg,
219 SourceRange Range,
220 const Expr *E,
221 CheckerContext &C) const {
222 if (!BT)
223 BT.reset(new APIMisuse(this, "nil argument"));
224
225 auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
226 R->addRange(Range);
227 bugreporter::trackExpressionValue(N, E, *R);
228 C.emitReport(std::move(R));
229 }
230
checkPreObjCMessage(const ObjCMethodCall & msg,CheckerContext & C) const231 void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
232 CheckerContext &C) const {
233 const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
234 if (!ID)
235 return;
236
237 FoundationClass Class = findKnownClass(ID);
238
239 static const unsigned InvalidArgIndex = UINT_MAX;
240 unsigned Arg = InvalidArgIndex;
241 bool CanBeSubscript = false;
242
243 if (Class == FC_NSString) {
244 Selector S = msg.getSelector();
245
246 if (S.isUnarySelector())
247 return;
248
249 if (StringSelectors.empty()) {
250 ASTContext &Ctx = C.getASTContext();
251 Selector Sels[] = {
252 getKeywordSelector(Ctx, "caseInsensitiveCompare"),
253 getKeywordSelector(Ctx, "compare"),
254 getKeywordSelector(Ctx, "compare", "options"),
255 getKeywordSelector(Ctx, "compare", "options", "range"),
256 getKeywordSelector(Ctx, "compare", "options", "range", "locale"),
257 getKeywordSelector(Ctx, "componentsSeparatedByCharactersInSet"),
258 getKeywordSelector(Ctx, "initWithFormat"),
259 getKeywordSelector(Ctx, "localizedCaseInsensitiveCompare"),
260 getKeywordSelector(Ctx, "localizedCompare"),
261 getKeywordSelector(Ctx, "localizedStandardCompare"),
262 };
263 for (Selector KnownSel : Sels)
264 StringSelectors[KnownSel] = 0;
265 }
266 auto I = StringSelectors.find(S);
267 if (I == StringSelectors.end())
268 return;
269 Arg = I->second;
270 } else if (Class == FC_NSArray) {
271 Selector S = msg.getSelector();
272
273 if (S.isUnarySelector())
274 return;
275
276 if (ArrayWithObjectSel.isNull()) {
277 ASTContext &Ctx = C.getASTContext();
278 ArrayWithObjectSel = getKeywordSelector(Ctx, "arrayWithObject");
279 AddObjectSel = getKeywordSelector(Ctx, "addObject");
280 InsertObjectAtIndexSel =
281 getKeywordSelector(Ctx, "insertObject", "atIndex");
282 ReplaceObjectAtIndexWithObjectSel =
283 getKeywordSelector(Ctx, "replaceObjectAtIndex", "withObject");
284 SetObjectAtIndexedSubscriptSel =
285 getKeywordSelector(Ctx, "setObject", "atIndexedSubscript");
286 ArrayByAddingObjectSel = getKeywordSelector(Ctx, "arrayByAddingObject");
287 }
288
289 if (S == ArrayWithObjectSel || S == AddObjectSel ||
290 S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) {
291 Arg = 0;
292 } else if (S == SetObjectAtIndexedSubscriptSel) {
293 Arg = 0;
294 CanBeSubscript = true;
295 } else if (S == ReplaceObjectAtIndexWithObjectSel) {
296 Arg = 1;
297 }
298 } else if (Class == FC_NSDictionary) {
299 Selector S = msg.getSelector();
300
301 if (S.isUnarySelector())
302 return;
303
304 if (DictionaryWithObjectForKeySel.isNull()) {
305 ASTContext &Ctx = C.getASTContext();
306 DictionaryWithObjectForKeySel =
307 getKeywordSelector(Ctx, "dictionaryWithObject", "forKey");
308 SetObjectForKeySel = getKeywordSelector(Ctx, "setObject", "forKey");
309 SetObjectForKeyedSubscriptSel =
310 getKeywordSelector(Ctx, "setObject", "forKeyedSubscript");
311 RemoveObjectForKeySel = getKeywordSelector(Ctx, "removeObjectForKey");
312 }
313
314 if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) {
315 Arg = 0;
316 warnIfNilArg(C, msg, /* Arg */1, Class);
317 } else if (S == SetObjectForKeyedSubscriptSel) {
318 CanBeSubscript = true;
319 Arg = 1;
320 } else if (S == RemoveObjectForKeySel) {
321 Arg = 0;
322 }
323 }
324
325 // If argument is '0', report a warning.
326 if ((Arg != InvalidArgIndex))
327 warnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
328 }
329
checkPostStmt(const ObjCArrayLiteral * AL,CheckerContext & C) const330 void NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL,
331 CheckerContext &C) const {
332 unsigned NumOfElements = AL->getNumElements();
333 for (unsigned i = 0; i < NumOfElements; ++i) {
334 warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C);
335 }
336 }
337
checkPostStmt(const ObjCDictionaryLiteral * DL,CheckerContext & C) const338 void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
339 CheckerContext &C) const {
340 unsigned NumOfElements = DL->getNumElements();
341 for (unsigned i = 0; i < NumOfElements; ++i) {
342 ObjCDictionaryElement Element = DL->getKeyValueElement(i);
343 warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C);
344 warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C);
345 }
346 }
347
348 //===----------------------------------------------------------------------===//
349 // Checking for mismatched types passed to CFNumberCreate/CFNumberGetValue.
350 //===----------------------------------------------------------------------===//
351
352 namespace {
353 class CFNumberChecker : public Checker< check::PreStmt<CallExpr> > {
354 mutable std::unique_ptr<APIMisuse> BT;
355 mutable IdentifierInfo *ICreate = nullptr, *IGetValue = nullptr;
356 public:
357 CFNumberChecker() = default;
358
359 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
360 };
361 } // end anonymous namespace
362
363 enum CFNumberType {
364 kCFNumberSInt8Type = 1,
365 kCFNumberSInt16Type = 2,
366 kCFNumberSInt32Type = 3,
367 kCFNumberSInt64Type = 4,
368 kCFNumberFloat32Type = 5,
369 kCFNumberFloat64Type = 6,
370 kCFNumberCharType = 7,
371 kCFNumberShortType = 8,
372 kCFNumberIntType = 9,
373 kCFNumberLongType = 10,
374 kCFNumberLongLongType = 11,
375 kCFNumberFloatType = 12,
376 kCFNumberDoubleType = 13,
377 kCFNumberCFIndexType = 14,
378 kCFNumberNSIntegerType = 15,
379 kCFNumberCGFloatType = 16
380 };
381
GetCFNumberSize(ASTContext & Ctx,uint64_t i)382 static std::optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
383 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
384
385 if (i < kCFNumberCharType)
386 return FixedSize[i-1];
387
388 QualType T;
389
390 switch (i) {
391 case kCFNumberCharType: T = Ctx.CharTy; break;
392 case kCFNumberShortType: T = Ctx.ShortTy; break;
393 case kCFNumberIntType: T = Ctx.IntTy; break;
394 case kCFNumberLongType: T = Ctx.LongTy; break;
395 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
396 case kCFNumberFloatType: T = Ctx.FloatTy; break;
397 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
398 case kCFNumberCFIndexType:
399 case kCFNumberNSIntegerType:
400 case kCFNumberCGFloatType:
401 // FIXME: We need a way to map from names to Type*.
402 default:
403 return std::nullopt;
404 }
405
406 return Ctx.getTypeSize(T);
407 }
408
409 #if 0
410 static const char* GetCFNumberTypeStr(uint64_t i) {
411 static const char* Names[] = {
412 "kCFNumberSInt8Type",
413 "kCFNumberSInt16Type",
414 "kCFNumberSInt32Type",
415 "kCFNumberSInt64Type",
416 "kCFNumberFloat32Type",
417 "kCFNumberFloat64Type",
418 "kCFNumberCharType",
419 "kCFNumberShortType",
420 "kCFNumberIntType",
421 "kCFNumberLongType",
422 "kCFNumberLongLongType",
423 "kCFNumberFloatType",
424 "kCFNumberDoubleType",
425 "kCFNumberCFIndexType",
426 "kCFNumberNSIntegerType",
427 "kCFNumberCGFloatType"
428 };
429
430 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
431 }
432 #endif
433
checkPreStmt(const CallExpr * CE,CheckerContext & C) const434 void CFNumberChecker::checkPreStmt(const CallExpr *CE,
435 CheckerContext &C) const {
436 ProgramStateRef state = C.getState();
437 const FunctionDecl *FD = C.getCalleeDecl(CE);
438 if (!FD)
439 return;
440
441 ASTContext &Ctx = C.getASTContext();
442 if (!ICreate) {
443 ICreate = &Ctx.Idents.get("CFNumberCreate");
444 IGetValue = &Ctx.Idents.get("CFNumberGetValue");
445 }
446 if (!(FD->getIdentifier() == ICreate || FD->getIdentifier() == IGetValue) ||
447 CE->getNumArgs() != 3)
448 return;
449
450 // Get the value of the "theType" argument.
451 SVal TheTypeVal = C.getSVal(CE->getArg(1));
452
453 // FIXME: We really should allow ranges of valid theType values, and
454 // bifurcate the state appropriately.
455 std::optional<nonloc::ConcreteInt> V =
456 dyn_cast<nonloc::ConcreteInt>(TheTypeVal);
457 if (!V)
458 return;
459
460 uint64_t NumberKind = V->getValue().getLimitedValue();
461 std::optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind);
462
463 // FIXME: In some cases we can emit an error.
464 if (!OptCFNumberSize)
465 return;
466
467 uint64_t CFNumberSize = *OptCFNumberSize;
468
469 // Look at the value of the integer being passed by reference. Essentially
470 // we want to catch cases where the value passed in is not equal to the
471 // size of the type being created.
472 SVal TheValueExpr = C.getSVal(CE->getArg(2));
473
474 // FIXME: Eventually we should handle arbitrary locations. We can do this
475 // by having an enhanced memory model that does low-level typing.
476 std::optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
477 if (!LV)
478 return;
479
480 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
481 if (!R)
482 return;
483
484 QualType T = Ctx.getCanonicalType(R->getValueType());
485
486 // FIXME: If the pointee isn't an integer type, should we flag a warning?
487 // People can do weird stuff with pointers.
488
489 if (!T->isIntegralOrEnumerationType())
490 return;
491
492 uint64_t PrimitiveTypeSize = Ctx.getTypeSize(T);
493
494 if (PrimitiveTypeSize == CFNumberSize)
495 return;
496
497 // FIXME: We can actually create an abstract "CFNumber" object that has
498 // the bits initialized to the provided values.
499 ExplodedNode *N = C.generateNonFatalErrorNode();
500 if (N) {
501 SmallString<128> sbuf;
502 llvm::raw_svector_ostream os(sbuf);
503 bool isCreate = (FD->getIdentifier() == ICreate);
504
505 if (isCreate) {
506 os << (PrimitiveTypeSize == 8 ? "An " : "A ")
507 << PrimitiveTypeSize << "-bit integer is used to initialize a "
508 << "CFNumber object that represents "
509 << (CFNumberSize == 8 ? "an " : "a ")
510 << CFNumberSize << "-bit integer; ";
511 } else {
512 os << "A CFNumber object that represents "
513 << (CFNumberSize == 8 ? "an " : "a ")
514 << CFNumberSize << "-bit integer is used to initialize "
515 << (PrimitiveTypeSize == 8 ? "an " : "a ")
516 << PrimitiveTypeSize << "-bit integer; ";
517 }
518
519 if (PrimitiveTypeSize < CFNumberSize)
520 os << (CFNumberSize - PrimitiveTypeSize)
521 << " bits of the CFNumber value will "
522 << (isCreate ? "be garbage." : "overwrite adjacent storage.");
523 else
524 os << (PrimitiveTypeSize - CFNumberSize)
525 << " bits of the integer value will be "
526 << (isCreate ? "lost." : "garbage.");
527
528 if (!BT)
529 BT.reset(new APIMisuse(this, "Bad use of CFNumber APIs"));
530
531 auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
532 report->addRange(CE->getArg(2)->getSourceRange());
533 C.emitReport(std::move(report));
534 }
535 }
536
537 //===----------------------------------------------------------------------===//
538 // CFRetain/CFRelease/CFMakeCollectable/CFAutorelease checking for null arguments.
539 //===----------------------------------------------------------------------===//
540
541 namespace {
542 class CFRetainReleaseChecker : public Checker<check::PreCall> {
543 mutable APIMisuse BT{this, "null passed to CF memory management function"};
544 const CallDescriptionSet ModelledCalls = {
545 {CDM::CLibrary, {"CFRetain"}, 1},
546 {CDM::CLibrary, {"CFRelease"}, 1},
547 {CDM::CLibrary, {"CFMakeCollectable"}, 1},
548 {CDM::CLibrary, {"CFAutorelease"}, 1},
549 };
550
551 public:
552 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
553 };
554 } // end anonymous namespace
555
checkPreCall(const CallEvent & Call,CheckerContext & C) const556 void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call,
557 CheckerContext &C) const {
558 // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease.
559 if (!ModelledCalls.contains(Call))
560 return;
561
562 // Get the argument's value.
563 SVal ArgVal = Call.getArgSVal(0);
564 std::optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
565 if (!DefArgVal)
566 return;
567
568 // Is it null?
569 ProgramStateRef state = C.getState();
570 ProgramStateRef stateNonNull, stateNull;
571 std::tie(stateNonNull, stateNull) = state->assume(*DefArgVal);
572
573 if (!stateNonNull) {
574 ExplodedNode *N = C.generateErrorNode(stateNull);
575 if (!N)
576 return;
577
578 SmallString<64> Str;
579 raw_svector_ostream OS(Str);
580 OS << "Null pointer argument in call to "
581 << cast<FunctionDecl>(Call.getDecl())->getName();
582
583 auto report = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N);
584 report->addRange(Call.getArgSourceRange(0));
585 bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *report);
586 C.emitReport(std::move(report));
587 return;
588 }
589
590 // From here on, we know the argument is non-null.
591 C.addTransition(stateNonNull);
592 }
593
594 //===----------------------------------------------------------------------===//
595 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
596 //===----------------------------------------------------------------------===//
597
598 namespace {
599 class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
600 mutable Selector releaseS;
601 mutable Selector retainS;
602 mutable Selector autoreleaseS;
603 mutable Selector drainS;
604 mutable std::unique_ptr<BugType> BT;
605
606 public:
607 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
608 };
609 } // end anonymous namespace
610
checkPreObjCMessage(const ObjCMethodCall & msg,CheckerContext & C) const611 void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
612 CheckerContext &C) const {
613 if (!BT) {
614 BT.reset(new APIMisuse(
615 this, "message incorrectly sent to class instead of class instance"));
616
617 ASTContext &Ctx = C.getASTContext();
618 releaseS = GetNullarySelector("release", Ctx);
619 retainS = GetNullarySelector("retain", Ctx);
620 autoreleaseS = GetNullarySelector("autorelease", Ctx);
621 drainS = GetNullarySelector("drain", Ctx);
622 }
623
624 if (msg.isInstanceMessage())
625 return;
626 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
627 assert(Class);
628
629 Selector S = msg.getSelector();
630 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
631 return;
632
633 if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
634 SmallString<200> buf;
635 llvm::raw_svector_ostream os(buf);
636
637 os << "The '";
638 S.print(os);
639 os << "' message should be sent to instances "
640 "of class '" << Class->getName()
641 << "' and not the class directly";
642
643 auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
644 report->addRange(msg.getSourceRange());
645 C.emitReport(std::move(report));
646 }
647 }
648
649 //===----------------------------------------------------------------------===//
650 // Check for passing non-Objective-C types to variadic methods that expect
651 // only Objective-C types.
652 //===----------------------------------------------------------------------===//
653
654 namespace {
655 class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
656 mutable Selector arrayWithObjectsS;
657 mutable Selector dictionaryWithObjectsAndKeysS;
658 mutable Selector setWithObjectsS;
659 mutable Selector orderedSetWithObjectsS;
660 mutable Selector initWithObjectsS;
661 mutable Selector initWithObjectsAndKeysS;
662 mutable std::unique_ptr<BugType> BT;
663
664 bool isVariadicMessage(const ObjCMethodCall &msg) const;
665
666 public:
667 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
668 };
669 } // end anonymous namespace
670
671 /// isVariadicMessage - Returns whether the given message is a variadic message,
672 /// where all arguments must be Objective-C types.
673 bool
isVariadicMessage(const ObjCMethodCall & msg) const674 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
675 const ObjCMethodDecl *MD = msg.getDecl();
676
677 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
678 return false;
679
680 Selector S = msg.getSelector();
681
682 if (msg.isInstanceMessage()) {
683 // FIXME: Ideally we'd look at the receiver interface here, but that's not
684 // useful for init, because alloc returns 'id'. In theory, this could lead
685 // to false positives, for example if there existed a class that had an
686 // initWithObjects: implementation that does accept non-Objective-C pointer
687 // types, but the chance of that happening is pretty small compared to the
688 // gains that this analysis gives.
689 const ObjCInterfaceDecl *Class = MD->getClassInterface();
690
691 switch (findKnownClass(Class)) {
692 case FC_NSArray:
693 case FC_NSOrderedSet:
694 case FC_NSSet:
695 return S == initWithObjectsS;
696 case FC_NSDictionary:
697 return S == initWithObjectsAndKeysS;
698 default:
699 return false;
700 }
701 } else {
702 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
703
704 switch (findKnownClass(Class)) {
705 case FC_NSArray:
706 return S == arrayWithObjectsS;
707 case FC_NSOrderedSet:
708 return S == orderedSetWithObjectsS;
709 case FC_NSSet:
710 return S == setWithObjectsS;
711 case FC_NSDictionary:
712 return S == dictionaryWithObjectsAndKeysS;
713 default:
714 return false;
715 }
716 }
717 }
718
checkPreObjCMessage(const ObjCMethodCall & msg,CheckerContext & C) const719 void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
720 CheckerContext &C) const {
721 if (!BT) {
722 BT.reset(new APIMisuse(this,
723 "Arguments passed to variadic method aren't all "
724 "Objective-C pointer types"));
725
726 ASTContext &Ctx = C.getASTContext();
727 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
728 dictionaryWithObjectsAndKeysS =
729 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
730 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
731 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
732
733 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
734 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
735 }
736
737 if (!isVariadicMessage(msg))
738 return;
739
740 // We are not interested in the selector arguments since they have
741 // well-defined types, so the compiler will issue a warning for them.
742 unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
743
744 // We're not interested in the last argument since it has to be nil or the
745 // compiler would have issued a warning for it elsewhere.
746 unsigned variadicArgsEnd = msg.getNumArgs() - 1;
747
748 if (variadicArgsEnd <= variadicArgsBegin)
749 return;
750
751 // Verify that all arguments have Objective-C types.
752 std::optional<ExplodedNode *> errorNode;
753
754 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
755 QualType ArgTy = msg.getArgExpr(I)->getType();
756 if (ArgTy->isObjCObjectPointerType())
757 continue;
758
759 // Block pointers are treaded as Objective-C pointers.
760 if (ArgTy->isBlockPointerType())
761 continue;
762
763 // Ignore pointer constants.
764 if (isa<loc::ConcreteInt>(msg.getArgSVal(I)))
765 continue;
766
767 // Ignore pointer types annotated with 'NSObject' attribute.
768 if (C.getASTContext().isObjCNSObjectType(ArgTy))
769 continue;
770
771 // Ignore CF references, which can be toll-free bridged.
772 if (coreFoundation::isCFObjectRef(ArgTy))
773 continue;
774
775 // Generate only one error node to use for all bug reports.
776 if (!errorNode)
777 errorNode = C.generateNonFatalErrorNode();
778
779 if (!*errorNode)
780 continue;
781
782 SmallString<128> sbuf;
783 llvm::raw_svector_ostream os(sbuf);
784
785 StringRef TypeName = GetReceiverInterfaceName(msg);
786 if (!TypeName.empty())
787 os << "Argument to '" << TypeName << "' method '";
788 else
789 os << "Argument to method '";
790
791 msg.getSelector().print(os);
792 os << "' should be an Objective-C pointer type, not '";
793 ArgTy.print(os, C.getLangOpts());
794 os << "'";
795
796 auto R =
797 std::make_unique<PathSensitiveBugReport>(*BT, os.str(), *errorNode);
798 R->addRange(msg.getArgSourceRange(I));
799 C.emitReport(std::move(R));
800 }
801 }
802
803 //===----------------------------------------------------------------------===//
804 // Improves the modeling of loops over Cocoa collections.
805 //===----------------------------------------------------------------------===//
806
807 // The map from container symbol to the container count symbol.
808 // We currently will remember the last container count symbol encountered.
809 REGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef)
810 REGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap, SymbolRef, bool)
811
812 namespace {
813 class ObjCLoopChecker
814 : public Checker<check::PostStmt<ObjCForCollectionStmt>,
815 check::PostObjCMessage,
816 check::DeadSymbols,
817 check::PointerEscape > {
818 mutable IdentifierInfo *CountSelectorII = nullptr;
819
820 bool isCollectionCountMethod(const ObjCMethodCall &M,
821 CheckerContext &C) const;
822
823 public:
824 ObjCLoopChecker() = default;
825 void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
826 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
827 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
828 ProgramStateRef checkPointerEscape(ProgramStateRef State,
829 const InvalidatedSymbols &Escaped,
830 const CallEvent *Call,
831 PointerEscapeKind Kind) const;
832 };
833 } // end anonymous namespace
834
isKnownNonNilCollectionType(QualType T)835 static bool isKnownNonNilCollectionType(QualType T) {
836 const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
837 if (!PT)
838 return false;
839
840 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
841 if (!ID)
842 return false;
843
844 switch (findKnownClass(ID)) {
845 case FC_NSArray:
846 case FC_NSDictionary:
847 case FC_NSEnumerator:
848 case FC_NSOrderedSet:
849 case FC_NSSet:
850 return true;
851 default:
852 return false;
853 }
854 }
855
856 /// Assumes that the collection is non-nil.
857 ///
858 /// If the collection is known to be nil, returns NULL to indicate an infeasible
859 /// path.
checkCollectionNonNil(CheckerContext & C,ProgramStateRef State,const ObjCForCollectionStmt * FCS)860 static ProgramStateRef checkCollectionNonNil(CheckerContext &C,
861 ProgramStateRef State,
862 const ObjCForCollectionStmt *FCS) {
863 if (!State)
864 return nullptr;
865
866 SVal CollectionVal = C.getSVal(FCS->getCollection());
867 std::optional<DefinedSVal> KnownCollection =
868 CollectionVal.getAs<DefinedSVal>();
869 if (!KnownCollection)
870 return State;
871
872 ProgramStateRef StNonNil, StNil;
873 std::tie(StNonNil, StNil) = State->assume(*KnownCollection);
874 if (StNil && !StNonNil) {
875 // The collection is nil. This path is infeasible.
876 return nullptr;
877 }
878
879 return StNonNil;
880 }
881
882 /// Assumes that the collection elements are non-nil.
883 ///
884 /// This only applies if the collection is one of those known not to contain
885 /// nil values.
checkElementNonNil(CheckerContext & C,ProgramStateRef State,const ObjCForCollectionStmt * FCS)886 static ProgramStateRef checkElementNonNil(CheckerContext &C,
887 ProgramStateRef State,
888 const ObjCForCollectionStmt *FCS) {
889 if (!State)
890 return nullptr;
891
892 // See if the collection is one where we /know/ the elements are non-nil.
893 if (!isKnownNonNilCollectionType(FCS->getCollection()->getType()))
894 return State;
895
896 const LocationContext *LCtx = C.getLocationContext();
897 const Stmt *Element = FCS->getElement();
898
899 // FIXME: Copied from ExprEngineObjC.
900 std::optional<Loc> ElementLoc;
901 if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
902 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
903 assert(ElemDecl->getInit() == nullptr);
904 ElementLoc = State->getLValue(ElemDecl, LCtx);
905 } else {
906 ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>();
907 }
908
909 if (!ElementLoc)
910 return State;
911
912 // Go ahead and assume the value is non-nil.
913 SVal Val = State->getSVal(*ElementLoc);
914 return State->assume(cast<DefinedOrUnknownSVal>(Val), true);
915 }
916
917 /// Returns NULL state if the collection is known to contain elements
918 /// (or is known not to contain elements if the Assumption parameter is false.)
919 static ProgramStateRef
assumeCollectionNonEmpty(CheckerContext & C,ProgramStateRef State,SymbolRef CollectionS,bool Assumption)920 assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
921 SymbolRef CollectionS, bool Assumption) {
922 if (!State || !CollectionS)
923 return State;
924
925 const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS);
926 if (!CountS) {
927 const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS);
928 if (!KnownNonEmpty)
929 return State->set<ContainerNonEmptyMap>(CollectionS, Assumption);
930 return (Assumption == *KnownNonEmpty) ? State : nullptr;
931 }
932
933 SValBuilder &SvalBuilder = C.getSValBuilder();
934 SVal CountGreaterThanZeroVal =
935 SvalBuilder.evalBinOp(State, BO_GT,
936 nonloc::SymbolVal(*CountS),
937 SvalBuilder.makeIntVal(0, (*CountS)->getType()),
938 SvalBuilder.getConditionType());
939 std::optional<DefinedSVal> CountGreaterThanZero =
940 CountGreaterThanZeroVal.getAs<DefinedSVal>();
941 if (!CountGreaterThanZero) {
942 // The SValBuilder cannot construct a valid SVal for this condition.
943 // This means we cannot properly reason about it.
944 return State;
945 }
946
947 return State->assume(*CountGreaterThanZero, Assumption);
948 }
949
950 static ProgramStateRef
assumeCollectionNonEmpty(CheckerContext & C,ProgramStateRef State,const ObjCForCollectionStmt * FCS,bool Assumption)951 assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
952 const ObjCForCollectionStmt *FCS,
953 bool Assumption) {
954 if (!State)
955 return nullptr;
956
957 SymbolRef CollectionS = C.getSVal(FCS->getCollection()).getAsSymbol();
958 return assumeCollectionNonEmpty(C, State, CollectionS, Assumption);
959 }
960
961 /// If the fist block edge is a back edge, we are reentering the loop.
alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode * N,const ObjCForCollectionStmt * FCS)962 static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N,
963 const ObjCForCollectionStmt *FCS) {
964 if (!N)
965 return false;
966
967 ProgramPoint P = N->getLocation();
968 if (std::optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
969 return BE->getSrc()->getLoopTarget() == FCS;
970 }
971
972 // Keep looking for a block edge.
973 for (const ExplodedNode *N : N->preds()) {
974 if (alreadyExecutedAtLeastOneLoopIteration(N, FCS))
975 return true;
976 }
977
978 return false;
979 }
980
checkPostStmt(const ObjCForCollectionStmt * FCS,CheckerContext & C) const981 void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
982 CheckerContext &C) const {
983 ProgramStateRef State = C.getState();
984
985 // Check if this is the branch for the end of the loop.
986 if (!ExprEngine::hasMoreIteration(State, FCS, C.getLocationContext())) {
987 if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS))
988 State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false);
989
990 // Otherwise, this is a branch that goes through the loop body.
991 } else {
992 State = checkCollectionNonNil(C, State, FCS);
993 State = checkElementNonNil(C, State, FCS);
994 State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true);
995 }
996
997 if (!State)
998 C.generateSink(C.getState(), C.getPredecessor());
999 else if (State != C.getState())
1000 C.addTransition(State);
1001 }
1002
isCollectionCountMethod(const ObjCMethodCall & M,CheckerContext & C) const1003 bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M,
1004 CheckerContext &C) const {
1005 Selector S = M.getSelector();
1006 // Initialize the identifiers on first use.
1007 if (!CountSelectorII)
1008 CountSelectorII = &C.getASTContext().Idents.get("count");
1009
1010 // If the method returns collection count, record the value.
1011 return S.isUnarySelector() &&
1012 (S.getIdentifierInfoForSlot(0) == CountSelectorII);
1013 }
1014
checkPostObjCMessage(const ObjCMethodCall & M,CheckerContext & C) const1015 void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M,
1016 CheckerContext &C) const {
1017 if (!M.isInstanceMessage())
1018 return;
1019
1020 const ObjCInterfaceDecl *ClassID = M.getReceiverInterface();
1021 if (!ClassID)
1022 return;
1023
1024 FoundationClass Class = findKnownClass(ClassID);
1025 if (Class != FC_NSDictionary &&
1026 Class != FC_NSArray &&
1027 Class != FC_NSSet &&
1028 Class != FC_NSOrderedSet)
1029 return;
1030
1031 SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol();
1032 if (!ContainerS)
1033 return;
1034
1035 // If we are processing a call to "count", get the symbolic value returned by
1036 // a call to "count" and add it to the map.
1037 if (!isCollectionCountMethod(M, C))
1038 return;
1039
1040 const Expr *MsgExpr = M.getOriginExpr();
1041 SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol();
1042 if (CountS) {
1043 ProgramStateRef State = C.getState();
1044
1045 C.getSymbolManager().addSymbolDependency(ContainerS, CountS);
1046 State = State->set<ContainerCountMap>(ContainerS, CountS);
1047
1048 if (const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) {
1049 State = State->remove<ContainerNonEmptyMap>(ContainerS);
1050 State = assumeCollectionNonEmpty(C, State, ContainerS, *NonEmpty);
1051 }
1052
1053 C.addTransition(State);
1054 }
1055 }
1056
getMethodReceiverIfKnownImmutable(const CallEvent * Call)1057 static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call) {
1058 const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call);
1059 if (!Message)
1060 return nullptr;
1061
1062 const ObjCMethodDecl *MD = Message->getDecl();
1063 if (!MD)
1064 return nullptr;
1065
1066 const ObjCInterfaceDecl *StaticClass;
1067 if (isa<ObjCProtocolDecl>(MD->getDeclContext())) {
1068 // We can't find out where the method was declared without doing more work.
1069 // Instead, see if the receiver is statically typed as a known immutable
1070 // collection.
1071 StaticClass = Message->getOriginExpr()->getReceiverInterface();
1072 } else {
1073 StaticClass = MD->getClassInterface();
1074 }
1075
1076 if (!StaticClass)
1077 return nullptr;
1078
1079 switch (findKnownClass(StaticClass, /*IncludeSuper=*/false)) {
1080 case FC_None:
1081 return nullptr;
1082 case FC_NSArray:
1083 case FC_NSDictionary:
1084 case FC_NSEnumerator:
1085 case FC_NSNull:
1086 case FC_NSOrderedSet:
1087 case FC_NSSet:
1088 case FC_NSString:
1089 break;
1090 }
1091
1092 return Message->getReceiverSVal().getAsSymbol();
1093 }
1094
1095 ProgramStateRef
checkPointerEscape(ProgramStateRef State,const InvalidatedSymbols & Escaped,const CallEvent * Call,PointerEscapeKind Kind) const1096 ObjCLoopChecker::checkPointerEscape(ProgramStateRef State,
1097 const InvalidatedSymbols &Escaped,
1098 const CallEvent *Call,
1099 PointerEscapeKind Kind) const {
1100 SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call);
1101
1102 // Remove the invalidated symbols from the collection count map.
1103 for (SymbolRef Sym : Escaped) {
1104 // Don't invalidate this symbol's count if we know the method being called
1105 // is declared on an immutable class. This isn't completely correct if the
1106 // receiver is also passed as an argument, but in most uses of NSArray,
1107 // NSDictionary, etc. this isn't likely to happen in a dangerous way.
1108 if (Sym == ImmutableReceiver)
1109 continue;
1110
1111 // The symbol escaped. Pessimistically, assume that the count could have
1112 // changed.
1113 State = State->remove<ContainerCountMap>(Sym);
1114 State = State->remove<ContainerNonEmptyMap>(Sym);
1115 }
1116 return State;
1117 }
1118
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const1119 void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1120 CheckerContext &C) const {
1121 ProgramStateRef State = C.getState();
1122
1123 // Remove the dead symbols from the collection count map.
1124 ContainerCountMapTy Tracked = State->get<ContainerCountMap>();
1125 for (SymbolRef Sym : llvm::make_first_range(Tracked)) {
1126 if (SymReaper.isDead(Sym)) {
1127 State = State->remove<ContainerCountMap>(Sym);
1128 State = State->remove<ContainerNonEmptyMap>(Sym);
1129 }
1130 }
1131
1132 C.addTransition(State);
1133 }
1134
1135 namespace {
1136 /// \class ObjCNonNilReturnValueChecker
1137 /// The checker restricts the return values of APIs known to
1138 /// never (or almost never) return 'nil'.
1139 class ObjCNonNilReturnValueChecker
1140 : public Checker<check::PostObjCMessage,
1141 check::PostStmt<ObjCArrayLiteral>,
1142 check::PostStmt<ObjCDictionaryLiteral>,
1143 check::PostStmt<ObjCBoxedExpr> > {
1144 mutable bool Initialized = false;
1145 mutable Selector ObjectAtIndex;
1146 mutable Selector ObjectAtIndexedSubscript;
1147 mutable Selector NullSelector;
1148
1149 public:
1150 ObjCNonNilReturnValueChecker() = default;
1151
1152 ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
1153 ProgramStateRef State,
1154 CheckerContext &C) const;
assumeExprIsNonNull(const Expr * E,CheckerContext & C) const1155 void assumeExprIsNonNull(const Expr *E, CheckerContext &C) const {
1156 C.addTransition(assumeExprIsNonNull(E, C.getState(), C));
1157 }
1158
checkPostStmt(const ObjCArrayLiteral * E,CheckerContext & C) const1159 void checkPostStmt(const ObjCArrayLiteral *E, CheckerContext &C) const {
1160 assumeExprIsNonNull(E, C);
1161 }
checkPostStmt(const ObjCDictionaryLiteral * E,CheckerContext & C) const1162 void checkPostStmt(const ObjCDictionaryLiteral *E, CheckerContext &C) const {
1163 assumeExprIsNonNull(E, C);
1164 }
checkPostStmt(const ObjCBoxedExpr * E,CheckerContext & C) const1165 void checkPostStmt(const ObjCBoxedExpr *E, CheckerContext &C) const {
1166 assumeExprIsNonNull(E, C);
1167 }
1168
1169 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
1170 };
1171 } // end anonymous namespace
1172
1173 ProgramStateRef
assumeExprIsNonNull(const Expr * NonNullExpr,ProgramStateRef State,CheckerContext & C) const1174 ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr,
1175 ProgramStateRef State,
1176 CheckerContext &C) const {
1177 SVal Val = C.getSVal(NonNullExpr);
1178 if (std::optional<DefinedOrUnknownSVal> DV =
1179 Val.getAs<DefinedOrUnknownSVal>())
1180 return State->assume(*DV, true);
1181 return State;
1182 }
1183
checkPostObjCMessage(const ObjCMethodCall & M,CheckerContext & C) const1184 void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
1185 CheckerContext &C)
1186 const {
1187 ProgramStateRef State = C.getState();
1188
1189 if (!Initialized) {
1190 ASTContext &Ctx = C.getASTContext();
1191 ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
1192 ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
1193 NullSelector = GetNullarySelector("null", Ctx);
1194 }
1195
1196 // Check the receiver type.
1197 if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
1198
1199 // Assume that object returned from '[self init]' or '[super init]' is not
1200 // 'nil' if we are processing an inlined function/method.
1201 //
1202 // A defensive callee will (and should) check if the object returned by
1203 // '[super init]' is 'nil' before doing it's own initialization. However,
1204 // since 'nil' is rarely returned in practice, we should not warn when the
1205 // caller to the defensive constructor uses the object in contexts where
1206 // 'nil' is not accepted.
1207 if (!C.inTopFrame() && M.getDecl() &&
1208 M.getDecl()->getMethodFamily() == OMF_init &&
1209 M.isReceiverSelfOrSuper()) {
1210 State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1211 }
1212
1213 FoundationClass Cl = findKnownClass(Interface);
1214
1215 // Objects returned from
1216 // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
1217 // are never 'nil'.
1218 if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
1219 Selector Sel = M.getSelector();
1220 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
1221 // Go ahead and assume the value is non-nil.
1222 State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1223 }
1224 }
1225
1226 // Objects returned from [NSNull null] are not nil.
1227 if (Cl == FC_NSNull) {
1228 if (M.getSelector() == NullSelector) {
1229 // Go ahead and assume the value is non-nil.
1230 State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1231 }
1232 }
1233 }
1234 C.addTransition(State);
1235 }
1236
1237 //===----------------------------------------------------------------------===//
1238 // Check registration.
1239 //===----------------------------------------------------------------------===//
1240
registerNilArgChecker(CheckerManager & mgr)1241 void ento::registerNilArgChecker(CheckerManager &mgr) {
1242 mgr.registerChecker<NilArgChecker>();
1243 }
1244
shouldRegisterNilArgChecker(const CheckerManager & mgr)1245 bool ento::shouldRegisterNilArgChecker(const CheckerManager &mgr) {
1246 return true;
1247 }
1248
registerCFNumberChecker(CheckerManager & mgr)1249 void ento::registerCFNumberChecker(CheckerManager &mgr) {
1250 mgr.registerChecker<CFNumberChecker>();
1251 }
1252
shouldRegisterCFNumberChecker(const CheckerManager & mgr)1253 bool ento::shouldRegisterCFNumberChecker(const CheckerManager &mgr) {
1254 return true;
1255 }
1256
registerCFRetainReleaseChecker(CheckerManager & mgr)1257 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
1258 mgr.registerChecker<CFRetainReleaseChecker>();
1259 }
1260
shouldRegisterCFRetainReleaseChecker(const CheckerManager & mgr)1261 bool ento::shouldRegisterCFRetainReleaseChecker(const CheckerManager &mgr) {
1262 return true;
1263 }
1264
registerClassReleaseChecker(CheckerManager & mgr)1265 void ento::registerClassReleaseChecker(CheckerManager &mgr) {
1266 mgr.registerChecker<ClassReleaseChecker>();
1267 }
1268
shouldRegisterClassReleaseChecker(const CheckerManager & mgr)1269 bool ento::shouldRegisterClassReleaseChecker(const CheckerManager &mgr) {
1270 return true;
1271 }
1272
registerVariadicMethodTypeChecker(CheckerManager & mgr)1273 void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
1274 mgr.registerChecker<VariadicMethodTypeChecker>();
1275 }
1276
shouldRegisterVariadicMethodTypeChecker(const CheckerManager & mgr)1277 bool ento::shouldRegisterVariadicMethodTypeChecker(const CheckerManager &mgr) {
1278 return true;
1279 }
1280
registerObjCLoopChecker(CheckerManager & mgr)1281 void ento::registerObjCLoopChecker(CheckerManager &mgr) {
1282 mgr.registerChecker<ObjCLoopChecker>();
1283 }
1284
shouldRegisterObjCLoopChecker(const CheckerManager & mgr)1285 bool ento::shouldRegisterObjCLoopChecker(const CheckerManager &mgr) {
1286 return true;
1287 }
1288
registerObjCNonNilReturnValueChecker(CheckerManager & mgr)1289 void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
1290 mgr.registerChecker<ObjCNonNilReturnValueChecker>();
1291 }
1292
shouldRegisterObjCNonNilReturnValueChecker(const CheckerManager & mgr)1293 bool ento::shouldRegisterObjCNonNilReturnValueChecker(const CheckerManager &mgr) {
1294 return true;
1295 }
1296