]> granicus.if.org Git - clang/commitdiff
[analyzer] Switched to checkPreCall interface for detecting usage after free.
authorAnton Yartsev <anton.yartsev@gmail.com>
Wed, 10 Apr 2013 22:21:41 +0000 (22:21 +0000)
committerAnton Yartsev <anton.yartsev@gmail.com>
Wed, 10 Apr 2013 22:21:41 +0000 (22:21 +0000)
Now the check is also applied to arguments for Objective-C method calls and to 'this' pointer.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@179230 91177308-0d34-0410-b5e6-96231b3b80d8

lib/StaticAnalyzer/Checkers/MallocChecker.cpp
test/Analysis/NewDelete-checker-test.cpp
test/Analysis/malloc.mm
test/Analysis/new.cpp

index 03471a52362f602fb5cd5bdea3f5b2593fff89b4..64e017ed1ce1db8850e49cd91d9cfc3a35921268 100644 (file)
@@ -147,7 +147,7 @@ class MallocChecker : public Checker<check::DeadSymbols,
                                      check::PointerEscape,
                                      check::ConstPointerEscape,
                                      check::PreStmt<ReturnStmt>,
-                                     check::PreStmt<CallExpr>,
+                                     check::PreCall,
                                      check::PostStmt<CallExpr>,
                                      check::PostStmt<CXXNewExpr>,
                                      check::PreStmt<CXXDeleteExpr>,
@@ -181,7 +181,7 @@ public:
 
   ChecksFilter Filter;
 
-  void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
+  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
   void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
   void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
   void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
@@ -1671,26 +1671,39 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
   C.addTransition(state->set<RegionState>(RS), N);
 }
 
-void MallocChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
+void MallocChecker::checkPreCall(const CallEvent &Call,
+                                 CheckerContext &C) const {
+
   // We will check for double free in the post visit.
-  if ((Filter.CMallocOptimistic || Filter.CMallocPessimistic) &&
-      isFreeFunction(C.getCalleeDecl(CE), C.getASTContext()))
-    return;
+  if (const AnyFunctionCall *FC = dyn_cast<AnyFunctionCall>(&Call)) {
+    const FunctionDecl *FD = FC->getDecl();
+    if (!FD)
+      return;
 
-  if (Filter.CNewDeleteChecker &&
-      isStandardNewDelete(C.getCalleeDecl(CE), C.getASTContext()))
-    return;
+    if ((Filter.CMallocOptimistic || Filter.CMallocPessimistic) &&
+        isFreeFunction(FD, C.getASTContext()))
+      return;
 
-  // Check use after free, when a freed pointer is passed to a call.
-  ProgramStateRef State = C.getState();
-  for (CallExpr::const_arg_iterator I = CE->arg_begin(),
-                                    E = CE->arg_end(); I != E; ++I) {
-    const Expr *A = *I;
-    if (A->getType().getTypePtr()->isAnyPointerType()) {
-      SymbolRef Sym = C.getSVal(A).getAsSymbol();
+    if (Filter.CNewDeleteChecker &&
+        isStandardNewDelete(FD, C.getASTContext()))
+      return;
+  }
+
+  // Check if the callee of a method is deleted.
+  if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) {
+    SymbolRef Sym = CC->getCXXThisVal().getAsSymbol();
+    if (!Sym || checkUseAfterFree(Sym, C, CC->getCXXThisExpr()))
+      return;
+  }
+
+  // Check arguments for being used after free.
+  for (unsigned I = 0, E = Call.getNumArgs(); I != E; ++I) {
+    SVal ArgSVal = Call.getArgSVal(I);
+    if (ArgSVal.getAs<Loc>()) {
+      SymbolRef Sym = ArgSVal.getAsSymbol();
       if (!Sym)
         continue;
-      if (checkUseAfterFree(Sym, C, A))
+      if (checkUseAfterFree(Sym, C, Call.getArgExpr(I)))
         return;
     }
   }
index bf95221eac8922096bbcee855b2226adf8f3d589..35125ccc8f669d93487b015e8bf3a5e60914e654 100644 (file)
@@ -54,7 +54,6 @@ void testGlobalNoThrowPlacementExprNewBeforeOverload() {
 // expected-warning@-2{{Potential leak of memory pointed to by 'p'}}
 #endif
 
-
 //----- Standard pointer placement operators
 void testGlobalPointerPlacementNew() {
   int i;
@@ -91,14 +90,54 @@ void testNewInvalidationPlacement(PtrWrapper *w) {
 // other checks
 //---------------
 
-void f(int *);
+class SomeClass {
+public:
+  void f(int *p);
+};
+
+void f(int *p1, int *p2 = 0, int *p3 = 0);
+void g(SomeClass &c, ...);
 
-void testUseAfterDelete() {
+void testUseFirstArgAfterDelete() {
   int *p = new int;
   delete p;
   f(p); // expected-warning{{Use of memory after it is freed}}
 }
 
+void testUseMiddleArgAfterDelete(int *p) {
+  delete p;
+  f(0, p); // expected-warning{{Use of memory after it is freed}}
+}
+
+void testUseLastArgAfterDelete(int *p) {
+  delete p;
+  f(0, 0, p); // expected-warning{{Use of memory after it is freed}}
+}
+
+void testUseRefArgAfterDelete(SomeClass &c) {
+  delete &c;
+  g(c); // expected-warning{{Use of memory after it is freed}}
+}
+
+void testVariadicArgAfterDelete() {
+  SomeClass c;
+  int *p = new int;
+  delete p;
+  g(c, 0, p); // expected-warning{{Use of memory after it is freed}}
+}
+
+void testUseMethodArgAfterDelete(int *p) {
+  SomeClass *c = new SomeClass;
+  delete p;
+  c->f(p); // expected-warning{{Use of memory after it is freed}}
+}
+
+void testUseThisAfterDelete() {
+  SomeClass *c = new SomeClass;
+  delete c;
+  c->f(0); // expected-warning{{Use of memory after it is freed}}
+}
+
 void testDeleteAlloca() {
   int *p = (int *)__builtin_alloca(sizeof(int));
   delete p; // expected-warning{{Memory allocated by alloca() should not be deallocated}}
index bd9d2d2a7f71455bd52a70b6bfb685097ba00409..c7fe86bf0bcc3c68967aaad6ea02fb9684bcadc1 100644 (file)
@@ -81,7 +81,17 @@ void testRelinquished2() {
   void *data = malloc(42);
   NSData *nsdata;
   free(data);
-  [NSData dataWithBytesNoCopy:data length:42]; // expected-warning {{Attempt to free released memory}}
+  [NSData dataWithBytesNoCopy:data length:42]; // expected-warning {{Use of memory after it is freed}}
+}
+
+@interface My
++ (void)param:(void *)p;
+@end
+
+void testUseAfterFree() {
+  int *p = (int *)malloc(sizeof(int));
+  free(p);
+  [My param:p];  // expected-warning{{Use of memory after it is freed}}
 }
 
 void testNoCopy() {
index 802243088e9b9d05878c2cd16ef67fa0ef3e89c2..8d3eee9baa6a49940ae523979a3b75bbfc320537 100644 (file)
@@ -8,6 +8,12 @@ extern "C" void *malloc(size_t);
 extern "C" void free(void *);
 
 int someGlobal;
+
+class SomeClass {
+public:
+  void f(int *p);
+};
+
 void testImplicitlyDeclaredGlobalNew() {
   if (someGlobal != 0)
     return;
@@ -101,6 +107,12 @@ void testCacheOut(PtrWrapper w) {
   new (&w.x) (int*)(0); // we cache out here; don't crash
 }
 
+void testUseAfter(int *p) {
+  SomeClass *c = new SomeClass;
+  free(p);
+  c->f(p); // expected-warning{{Use of memory after it is freed}}
+  delete c;
+}
 
 //--------------------------------------------------------------------
 // Check for intersection with other checkers from MallocChecker.cpp 
@@ -152,6 +164,12 @@ void testCustomPlacementNewAfterFree() {
   p = new(0, p) int; // expected-warning{{Use of memory after it is freed}}
 }
 
+void testUsingThisAfterDelete() {
+  SomeClass *c = new SomeClass;
+  delete c;
+  c->f(0); // no-warning
+}
+
 //--------------------------------
 // Incorrectly-modelled behavior
 //--------------------------------