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