[cfe-commits] r147500 - in /cfe/trunk: lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp test/Analysis/unix-fns.c

Ted Kremenek kremenek at apple.com
Tue Jan 3 15:43:13 PST 2012


Author: kremenek
Date: Tue Jan  3 17:43:13 2012
New Revision: 147500

URL: http://llvm.org/viewvc/llvm-project?rev=147500&view=rev
Log:
Enhance UnixAPIChecker to also warn about zero-sized allocations to calloc() and realloc().  Patch by Cyril Roelandt!

Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
    cfe/trunk/test/Analysis/unix-fns.c

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp?rev=147500&r1=147499&r2=147500&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp Tue Jan  3 17:43:13 2012
@@ -36,10 +36,17 @@
 
   void CheckOpen(CheckerContext &C, const CallExpr *CE) const;
   void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const;
+  void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
   void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
+  void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;
 
   typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &,
                                              const CallExpr *) const;
+private:
+  bool ReportZeroByteAllocation(CheckerContext &C,
+                                const ProgramState *falseState,
+                                const Expr *arg,
+                                const char *fn_name) const;
 };
 } //end anonymous namespace
 
@@ -170,53 +177,132 @@
 }
 
 //===----------------------------------------------------------------------===//
-// "malloc" with allocation size 0
+// "calloc",  "malloc" and "realloc" with allocation size 0
 //===----------------------------------------------------------------------===//
 
+// Returns true if we try to do a zero byte allocation, false otherwise.
+// Fills in trueState and falseState.
+static bool IsZeroByteAllocation(const ProgramState *state,
+                                const SVal argVal,
+                                const ProgramState **trueState,
+                                const ProgramState **falseState) {
+  llvm::tie(*trueState, *falseState) = state->assume(cast<DefinedSVal>(argVal));
+  return (*falseState && !*trueState);
+}
+
+// Generates an error report, indicating that the function whose name is given
+// will perform a zero byte allocation.
+// Returns false if an error occured, true otherwise.
+bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C,
+                                              const ProgramState *falseState,
+                                              const Expr *arg,
+                                              const char *fn_name) const {
+  ExplodedNode *N = C.generateSink(falseState);
+  if (!N)
+    return false;
+
+  // FIXME: Add reference to CERT advisory, and/or C99 standard in bug
+  // output.
+  LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes");
+
+  llvm::SmallString<256> S;
+  llvm::raw_svector_ostream os(S);    
+  os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
+  BugReport *report = new BugReport(*BT_mallocZero, os.str(), N);
+
+  report->addRange(arg->getSourceRange());
+  report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, arg));
+  C.EmitReport(report);
+
+  return true;
+}
+
+void UnixAPIChecker::CheckCallocZero(CheckerContext &C,
+                                     const CallExpr *CE) const {
+  unsigned int nArgs = CE->getNumArgs();
+  if (nArgs != 2)
+    return;
+
+  const ProgramState *state = C.getState();
+  const ProgramState *trueState = NULL, *falseState = NULL;
+
+  unsigned int i;
+  for (i = 0; i < nArgs; i++) {
+    const Expr *arg = CE->getArg(i);
+    SVal argVal = state->getSVal(arg);
+    if (argVal.isUnknownOrUndef()) {
+      if (i == 0)
+        continue;
+      else
+        return;
+    }
+
+    if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
+      if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
+        return;
+      else if (i == 0)
+        continue;
+      else
+        return;
+    }
+  }
+
+  // Assume the the value is non-zero going forward.
+  assert(trueState);
+  if (trueState != state)
+    C.addTransition(trueState);
+}
+
 // FIXME: Eventually this should be rolled into the MallocChecker, but this
 // check is more basic and is valuable for widespread use.
 void UnixAPIChecker::CheckMallocZero(CheckerContext &C,
                                      const CallExpr *CE) const {
-
   // Sanity check that malloc takes one argument.
   if (CE->getNumArgs() != 1)
     return;
 
   // Check if the allocation size is 0.
   const ProgramState *state = C.getState();
-  SVal argVal = state->getSVal(CE->getArg(0));
+  const ProgramState *trueState = NULL, *falseState = NULL;
+  const Expr *arg = CE->getArg(0);
+  SVal argVal = state->getSVal(arg);
 
   if (argVal.isUnknownOrUndef())
     return;
-  
-  const ProgramState *trueState, *falseState;
-  llvm::tie(trueState, falseState) = state->assume(cast<DefinedSVal>(argVal));
-  
-  // Is the value perfectly constrained to zero?
-  if (falseState && !trueState) {
-    ExplodedNode *N = C.generateSink(falseState);
-    if (!N)
-      return;
-    
-    // FIXME: Add reference to CERT advisory, and/or C99 standard in bug
-    // output.
 
-    LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes");
-    
-    BugReport *report =
-      new BugReport(*BT_mallocZero, "Call to 'malloc' has an allocation"
-                                            " size of 0 bytes", N);
-    report->addRange(CE->getArg(0)->getSourceRange());
-    report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
-                                                                CE->getArg(0)));
-    C.EmitReport(report);
+  // Is the value perfectly constrained to zero?
+  if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
+    (void) ReportZeroByteAllocation(C, falseState, arg, "malloc"); 
     return;
   }
   // Assume the the value is non-zero going forward.
   assert(trueState);
-  if (trueState != state) {
+  if (trueState != state)
     C.addTransition(trueState);
+}
+
+void UnixAPIChecker::CheckReallocZero(CheckerContext &C,
+                                      const CallExpr *CE) const {
+  if (CE->getNumArgs() != 2)
+    return;
+
+  const ProgramState *state = C.getState();
+  const ProgramState *trueState = NULL, *falseState = NULL;
+  const Expr *arg = CE->getArg(1);
+  SVal argVal = state->getSVal(arg);
+
+  if (argVal.isUnknownOrUndef())
+    return;
+
+  if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
+    ReportZeroByteAllocation(C, falseState, arg, "realloc");
+    return;
   }
+
+  // Assume the the value is non-zero going forward.
+  assert(trueState);
+  if (trueState != state)
+    C.addTransition(trueState);
 }
   
 //===----------------------------------------------------------------------===//
@@ -232,7 +318,9 @@
     llvm::StringSwitch<SubChecker>(FName)
       .Case("open", &UnixAPIChecker::CheckOpen)
       .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce)
+      .Case("calloc", &UnixAPIChecker::CheckCallocZero)
       .Case("malloc", &UnixAPIChecker::CheckMallocZero)
+      .Case("realloc", &UnixAPIChecker::CheckReallocZero)
       .Default(NULL);
 
   if (SC)

Modified: cfe/trunk/test/Analysis/unix-fns.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/unix-fns.c?rev=147500&r1=147499&r2=147500&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/unix-fns.c (original)
+++ cfe/trunk/test/Analysis/unix-fns.c Tue Jan  3 17:43:13 2012
@@ -9,7 +9,9 @@
 int pthread_once(pthread_once_t *, void (*)(void));
 typedef long unsigned int __darwin_size_t;
 typedef __darwin_size_t size_t;
+void *calloc(size_t, size_t);
 void *malloc(size_t);
+void *realloc(void *, size_t);
 
 typedef void (^dispatch_block_t)(void);
 typedef long dispatch_once_t;
@@ -66,3 +68,33 @@
     foo[i] = 0;
   }
 }
+void test_calloc(void) {
+  char *foo = calloc(0, 42); // expected-warning{{Call to 'calloc' has an allocation size of 0 bytes}}
+  for (unsigned i = 0; i < 100; i++) {
+    foo[i] = 0;
+  }
+}
+void test_calloc2(void) {
+  char *foo = calloc(42, 0); // expected-warning{{Call to 'calloc' has an allocation size of 0 bytes}}
+  for (unsigned i = 0; i < 100; i++) {
+    foo[i] = 0;
+  }
+}
+void test_calloc_nowarn(size_t nmemb, size_t size) {
+  char *foo = calloc(nmemb, size); // no-warning
+  for (unsigned i = 0; i < 100; i++) {
+    foo[i] = 0;
+  }
+}
+void test_realloc(char *ptr) {
+  char *foo = realloc(ptr, 0); // expected-warning{{Call to 'realloc' has an allocation size of 0 bytes}}
+  for (unsigned i = 0; i < 100; i++) {
+    foo[i] = 0;
+  }
+}
+void test_realloc_nowarn(char *ptr, size_t size) {
+  char *foo = realloc(ptr, size); // no-warning
+  for (unsigned i = 0; i < 100; i++) {
+    foo[i] = 0;
+  }
+}





More information about the cfe-commits mailing list