summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp25
-rw-r--r--clang/test/Analysis/CFContainers.mm22
2 files changed, 46 insertions, 1 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
index d1938a0f7f7..4f0b7e51da1 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
+++ b/clang/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/clang/test/Analysis/CFContainers.mm b/clang/test/Analysis/CFContainers.mm
index b01942310f7..f315bc9f045 100644
--- a/clang/test/Analysis/CFContainers.mm
+++ b/clang/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}}
+}
OpenPOWER on IntegriCloud