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