From 73669ab60214dc17eb57ac26e0cfb79acf41ac8a Mon Sep 17 00:00:00 2001 From: Devin Coughlin <dcoughlin@apple.com> Date: Mon, 15 Jun 2015 01:00:42 +0000 Subject: [PATCH] [analyzer] Remove ObjCContainersChecker size information when a CFMutableArrayRef escapes Update ObjCContainersChecker to be notified when pointers escape so it can remove size information for escaping CFMutableArrayRefs. When such pointers escape, un-analyzed code could mutate the array and cause the size information to be incorrect. rdar://problem/19406485 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@239709 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Checkers/ObjCContainersChecker.cpp | 25 ++++++++++++++++++- test/Analysis/CFContainers.mm | 22 ++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp index d1938a0f7f7..4f0b7e51da1 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp @@ -29,7 +29,8 @@ using namespace ento; namespace { class ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>, - check::PostStmt<CallExpr> > { + check::PostStmt<CallExpr>, + check::PointerEscape> { mutable std::unique_ptr<BugType> BT; inline void initBugType() const { if (!BT) @@ -52,6 +53,10 @@ public: void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; }; } // end anonymous namespace @@ -146,6 +151,24 @@ void ObjCContainersChecker::checkPreStmt(const CallExpr *CE, } } +ProgramStateRef +ObjCContainersChecker::checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const { + for (InvalidatedSymbols::const_iterator I = Escaped.begin(), + E = Escaped.end(); + I != E; ++I) { + SymbolRef Sym = *I; + // When a symbol for a mutable array escapes, we can't reason precisely + // about its size any more -- so remove it from the map. + // Note that we aren't notified here when a CFMutableArrayRef escapes as a + // CFArrayRef. This is because CFArrayRef is typedef'd as a pointer to a + // const-qualified type. + State = State->remove<ArraySizeMap>(Sym); + } + return State; +} /// Register checker. void ento::registerObjCContainersChecker(CheckerManager &mgr) { mgr.registerChecker<ObjCContainersChecker>(); diff --git a/test/Analysis/CFContainers.mm b/test/Analysis/CFContainers.mm index b01942310f7..f315bc9f045 100644 --- a/test/Analysis/CFContainers.mm +++ b/test/Analysis/CFContainers.mm @@ -19,6 +19,7 @@ typedef struct { } CFArrayCallBacks; typedef const struct __CFArray * CFArrayRef; CFArrayRef CFArrayCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFArrayCallBacks *callBacks); +typedef struct __CFArray * CFMutableArrayRef; typedef const struct __CFString * CFStringRef; enum { kCFNumberSInt8Type = 1, @@ -202,3 +203,24 @@ void TestConst(CFArrayRef A, CFIndex sIndex, void* x[]) { void TestNullArray() { CFArrayGetValueAtIndex(0, 0); } + +void ArrayRefMutableEscape(CFMutableArrayRef a); +void ArrayRefEscape(CFArrayRef a); + +void TestCFMutableArrayRefEscapeViaMutableArgument(CFMutableArrayRef a) { + CFIndex aLen = CFArrayGetCount(a); + ArrayRefMutableEscape(a); + + // ArrayRefMutableEscape could mutate a to make it have + // at least aLen + 1 elements, so do not report an error here. + CFArrayGetValueAtIndex(a, aLen); +} + +void TestCFMutableArrayRefEscapeViaImmutableArgument(CFMutableArrayRef a) { + CFIndex aLen = CFArrayGetCount(a); + ArrayRefEscape(a); + + // ArrayRefEscape is declared to take a CFArrayRef (i.e, an immutable array) + // so we assume it does not change the length of a. + CFArrayGetValueAtIndex(a, aLen); // expected-warning {{Index is out of bounds}} +} -- GitLab