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

Ted Kremenek kremenek at apple.com
Wed Jan 11 00:13:21 PST 2012


Author: kremenek
Date: Wed Jan 11 02:13:21 2012
New Revision: 147931

URL: http://llvm.org/viewvc/llvm-project?rev=147931&view=rev
Log:
"This change adds alloca/valloc checks to UnixAPIChecker. It includes a small refactoring for
the common *alloc functions as well as a few tiny wibbles (adds a note
to CWE/CERT advisory numbers in the bug output, and fixes a couple
80-column-wide violations.)"

Patch by Austin Seipp!

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=147931&r1=147930&r2=147931&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp Wed Jan 11 02:13:21 2012
@@ -39,6 +39,8 @@
   void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
   void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
   void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;
+  void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const;
+  void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const;
 
   typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &,
                                              const CallExpr *) const;
@@ -47,6 +49,11 @@
                                 const ProgramState *falseState,
                                 const Expr *arg,
                                 const char *fn_name) const;
+  void BasicAllocationCheck(CheckerContext &C,
+                            const CallExpr *CE,
+                            const unsigned numArgs,
+                            const unsigned sizeArg,
+                            const char *fn) const;
 };
 } //end anonymous namespace
 
@@ -178,8 +185,10 @@
 }
 
 //===----------------------------------------------------------------------===//
-// "calloc",  "malloc" and "realloc" with allocation size 0
+// "calloc", "malloc", "realloc", "alloca" and "valloc" with allocation size 0
 //===----------------------------------------------------------------------===//
+// FIXME: Eventually these should be rolled into the MallocChecker, but right now
+// they're more basic and valuable for widespread use.
 
 // Returns true if we try to do a zero byte allocation, false otherwise.
 // Fills in trueState and falseState.
@@ -187,7 +196,9 @@
                                 const SVal argVal,
                                 const ProgramState **trueState,
                                 const ProgramState **falseState) {
-  llvm::tie(*trueState, *falseState) = state->assume(cast<DefinedSVal>(argVal));
+  llvm::tie(*trueState, *falseState) =
+    state->assume(cast<DefinedSVal>(argVal));
+  
   return (*falseState && !*trueState);
 }
 
@@ -202,9 +213,8 @@
   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");
+  LazyInitialize(BT_mallocZero,
+    "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
 
   llvm::SmallString<256> S;
   llvm::raw_svector_ostream os(S);    
@@ -218,6 +228,37 @@
   return true;
 }
 
+// Does a basic check for 0-sized allocations suitable for most of the below
+// functions (modulo "calloc")
+void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C,
+                                          const CallExpr *CE,
+                                          const unsigned numArgs,
+                                          const unsigned sizeArg,
+                                          const char *fn) const {
+  // Sanity check for the correct number of arguments
+  if (CE->getNumArgs() != numArgs)
+    return;
+
+  // Check if the allocation size is 0.
+  const ProgramState *state = C.getState();
+  const ProgramState *trueState = NULL, *falseState = NULL;
+  const Expr *arg = CE->getArg(sizeArg);
+  SVal argVal = state->getSVal(arg, C.getLocationContext());
+
+  if (argVal.isUnknownOrUndef())
+    return;
+
+  // Is the value perfectly constrained to zero?
+  if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
+    (void) ReportZeroByteAllocation(C, falseState, arg, fn); 
+    return;
+  }
+  // Assume the the value is non-zero going forward.
+  assert(trueState);
+  if (trueState != state)
+    C.addTransition(trueState);                           
+}
+
 void UnixAPIChecker::CheckCallocZero(CheckerContext &C,
                                      const CallExpr *CE) const {
   unsigned int nArgs = CE->getNumArgs();
@@ -254,63 +295,33 @@
     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();
-  const ProgramState *trueState = NULL, *falseState = NULL;
-  const Expr *arg = CE->getArg(0);
-  SVal argVal = state->getSVal(arg, C.getLocationContext());
-
-  if (argVal.isUnknownOrUndef())
-    return;
-
-  // 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)
-    C.addTransition(trueState);
+  BasicAllocationCheck(C, CE, 1, 0, "malloc");
 }
 
 void UnixAPIChecker::CheckReallocZero(CheckerContext &C,
                                       const CallExpr *CE) const {
-  if (CE->getNumArgs() != 2)
-    return;
+  BasicAllocationCheck(C, CE, 2, 1, "realloc");
+}
 
-  const ProgramState *state = C.getState();
-  const ProgramState *trueState = NULL, *falseState = NULL;
-  const Expr *arg = CE->getArg(1);
-  SVal argVal = state->getSVal(arg, C.getLocationContext());
+void UnixAPIChecker::CheckAllocaZero(CheckerContext &C,
+                                     const CallExpr *CE) const {
+  BasicAllocationCheck(C, CE, 1, 0, "alloca");
+}
 
-  if (argVal.isUnknownOrUndef())
-    return;
+void UnixAPIChecker::CheckVallocZero(CheckerContext &C,
+                                     const CallExpr *CE) const {
+  BasicAllocationCheck(C, CE, 1, 0, "valloc");
+}
 
-  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);
-}
-  
 //===----------------------------------------------------------------------===//
 // Central dispatch function.
 //===----------------------------------------------------------------------===//
 
-void UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
+void UnixAPIChecker::checkPreStmt(const CallExpr *CE,
+                                  CheckerContext &C) const {
   StringRef FName = C.getCalleeName(CE);
   if (FName.empty())
     return;
@@ -322,6 +333,8 @@
       .Case("calloc", &UnixAPIChecker::CheckCallocZero)
       .Case("malloc", &UnixAPIChecker::CheckMallocZero)
       .Case("realloc", &UnixAPIChecker::CheckReallocZero)
+      .Cases("alloca", "__builtin_alloca", &UnixAPIChecker::CheckAllocaZero)
+      .Case("valloc", &UnixAPIChecker::CheckVallocZero)
       .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=147931&r1=147930&r2=147931&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/unix-fns.c (original)
+++ cfe/trunk/test/Analysis/unix-fns.c Wed Jan 11 02:13:21 2012
@@ -12,6 +12,8 @@
 void *calloc(size_t, size_t);
 void *malloc(size_t);
 void *realloc(void *, size_t);
+void *alloca(size_t);
+void *valloc(size_t);
 
 typedef void (^dispatch_block_t)(void);
 typedef long dispatch_once_t;
@@ -98,3 +100,39 @@
     foo[i] = 0;
   }
 }
+void test_alloca() {
+  char *foo = alloca(0); // expected-warning{{Call to 'alloca' has an allocation size of 0 bytes}}
+  for(unsigned i = 0; i < 100; i++) {
+    foo[i] = 0; 
+  }
+}
+void test_alloca_nowarn(size_t sz) {
+  char *foo = alloca(sz); // no-warning
+  for(unsigned i = 0; i < 100; i++) {
+    foo[i] = 0;
+  }
+}
+void test_builtin_alloca() {
+  char *foo2 = __builtin_alloca(0); // expected-warning{{Call to 'alloca' has an allocation size of 0 bytes}}
+  for(unsigned i = 0; i < 100; i++) {
+    foo2[i] = 0; 
+  }
+}
+void test_builtin_alloca_nowarn(size_t sz) {
+  char *foo2 = __builtin_alloca(sz); // no-warning
+  for(unsigned i = 0; i < 100; i++) {
+    foo2[i] = 0;
+  }
+}
+void test_valloc() {
+  char *foo = valloc(0); // expected-warning{{Call to 'valloc' has an allocation size of 0 bytes}}
+  for(unsigned i = 0; i < 100; i++) {
+    foo[i] = 0; 
+  }
+}
+void test_valloc_nowarn(size_t sz) {
+  char *foo = valloc(sz); // no-warning
+  for(unsigned i = 0; i < 100; i++) {
+    foo[i] = 0;
+  }
+}





More information about the cfe-commits mailing list