[cfe-commits] r126187 - in /cfe/trunk: lib/StaticAnalyzer/Checkers/CStringChecker.cpp test/Analysis/string.c

Ted Kremenek kremenek at apple.com
Mon Feb 21 20:55:06 PST 2011


Author: kremenek
Date: Mon Feb 21 22:55:05 2011
New Revision: 126187

URL: http://llvm.org/viewvc/llvm-project?rev=126187&view=rev
Log:
Add CStringChecker support for strnlen.  Patch by Lenny Maiorani!

Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
    cfe/trunk/test/Analysis/string.c

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp?rev=126187&r1=126186&r2=126187&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp Mon Feb 21 22:55:05 2011
@@ -54,6 +54,9 @@
   void evalMemcmp(CheckerContext &C, const CallExpr *CE);
 
   void evalstrLength(CheckerContext &C, const CallExpr *CE);
+  void evalstrnLength(CheckerContext &C, const CallExpr *CE);
+  void evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, 
+                           bool IsStrnlen = false);
 
   void evalStrcpy(CheckerContext &C, const CallExpr *CE);
   void evalStpcpy(CheckerContext &C, const CallExpr *CE);
@@ -776,6 +779,16 @@
 
 void CStringChecker::evalstrLength(CheckerContext &C, const CallExpr *CE) {
   // size_t strlen(const char *s);
+  evalstrLengthCommon(C, CE, /* IsStrnlen = */ false);
+}
+
+void CStringChecker::evalstrnLength(CheckerContext &C, const CallExpr *CE) {
+  // size_t strnlen(const char *s, size_t maxlen);
+  evalstrLengthCommon(C, CE, /* IsStrnlen = */ true);
+}
+
+void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
+                                         bool IsStrnlen) {
   const GRState *state = C.getState();
   const Expr *Arg = CE->getArg(0);
   SVal ArgVal = state->getSVal(Arg);
@@ -791,6 +804,32 @@
     if (strLength.isUndef())
       return;
 
+    // If the check is for strnlen() then bind the return value to no more than
+    // the maxlen value.
+    if (IsStrnlen) {
+      const Expr *maxlenExpr = CE->getArg(1);
+      SVal maxlenVal = state->getSVal(maxlenExpr);
+    
+      NonLoc *strLengthNL = dyn_cast<NonLoc>(&strLength);
+      NonLoc *maxlenValNL = dyn_cast<NonLoc>(&maxlenVal);
+
+      QualType cmpTy = C.getSValBuilder().getContext().IntTy;
+      const GRState *stateTrue, *stateFalse;
+    
+      // Check if the strLength is greater than or equal to the maxlen
+      llvm::tie(stateTrue, stateFalse) =
+        state->assume(cast<DefinedOrUnknownSVal>
+                      (C.getSValBuilder().evalBinOpNN(state, BO_GE, 
+                                                      *strLengthNL, *maxlenValNL,
+                                                      cmpTy)));
+
+      // If the strLength is greater than or equal to the maxlen, set strLength
+      // to maxlen
+      if (stateTrue && !stateFalse) {
+        strLength = maxlenVal;
+      }
+    }
+
     // If getCStringLength couldn't figure out the length, conjure a return
     // value, so it can be used in constraints, at least.
     if (strLength.isUnknown()) {
@@ -914,6 +953,7 @@
     .Cases("strcpy", "__strcpy_chk", &CStringChecker::evalStrcpy)
     .Cases("stpcpy", "__stpcpy_chk", &CStringChecker::evalStpcpy)
     .Case("strlen", &CStringChecker::evalstrLength)
+    .Case("strnlen", &CStringChecker::evalstrnLength)
     .Case("bcopy", &CStringChecker::evalBcopy)
     .Default(NULL);
 

Modified: cfe/trunk/test/Analysis/string.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/string.c?rev=126187&r1=126186&r2=126187&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/string.c (original)
+++ cfe/trunk/test/Analysis/string.c Mon Feb 21 22:55:05 2011
@@ -140,6 +140,138 @@
 }
 
 //===----------------------------------------------------------------------===
+// strnlen()
+//===----------------------------------------------------------------------===
+
+#define strnlen BUILTIN(strnlen)
+size_t strnlen(const char *s, size_t maxlen);
+
+void strnlen_constant0() {
+  if (strnlen("123", 10) != 3)
+    (void)*(char*)0; // no-warning
+}
+
+void strnlen_constant1() {
+  const char *a = "123";
+  if (strnlen(a, 10) != 3)
+    (void)*(char*)0; // no-warning
+}
+
+void strnlen_constant2(char x) {
+  char a[] = "123";
+  if (strnlen(a, 10) != 3)
+    (void)*(char*)0; // no-warning
+  a[0] = x;
+  if (strnlen(a, 10) != 3)
+    (void)*(char*)0; // expected-warning{{null}}
+}
+
+void strnlen_constant4() {
+  if (strnlen("123456", 3) != 3)
+    (void)*(char*)0; // no-warning
+}
+
+void strnlen_constant5() {
+  const char *a = "123456";
+  if (strnlen(a, 3) != 3)
+    (void)*(char*)0; // no-warning
+}
+
+void strnlen_constant6(char x) {
+  char a[] = "123456";
+  if (strnlen(a, 3) != 3)
+    (void)*(char*)0; // no-warning
+  a[0] = x;
+  if (strnlen(a, 3) != 3)
+    (void)*(char*)0; // expected-warning{{null}}
+}
+
+size_t strnlen_null() {
+  return strnlen(0, 3); // expected-warning{{Null pointer argument in call to byte string function}}
+}
+
+size_t strnlen_fn() {
+  return strnlen((char*)&strlen_fn, 3); // expected-warning{{Argument to byte string function is the address of the function 'strlen_fn', which is not a null-terminated string}}
+}
+
+size_t strnlen_nonloc() {
+label:
+  return strnlen((char*)&&label, 3); // expected-warning{{Argument to byte string function is the address of the label 'label', which is not a null-terminated string}}
+}
+
+void strnlen_subregion() {
+  struct two_stringsn { char a[2], b[2] };
+  extern void use_two_stringsn(struct two_stringsn *);
+
+  struct two_stringsn z;
+  use_two_stringsn(&z);
+
+  size_t a = strnlen(z.a, 10);
+  z.b[0] = 5;
+  size_t b = strnlen(z.a, 10);
+  if (a == 0 && b != 0)
+    (void)*(char*)0; // expected-warning{{never executed}}
+
+  use_two_stringsn(&z);
+
+  size_t c = strnlen(z.a, 10);
+  if (a == 0 && c != 0)
+    (void)*(char*)0; // expected-warning{{null}}
+}
+
+extern void use_stringn(char *);
+void strnlen_argument(char *x) {
+  size_t a = strnlen(x, 10);
+  size_t b = strnlen(x, 10);
+  if (a == 0 && b != 0)
+    (void)*(char*)0; // expected-warning{{never executed}}
+
+  use_stringn(x);
+
+  size_t c = strnlen(x, 10);
+  if (a == 0 && c != 0)
+    (void)*(char*)0; // expected-warning{{null}}  
+}
+
+extern char global_strn[];
+void strnlen_global() {
+  size_t a = strnlen(global_strn, 10);
+  size_t b = strnlen(global_strn, 10);
+  if (a == 0 && b != 0)
+    (void)*(char*)0; // expected-warning{{never executed}}
+
+  // Call a function with unknown effects, which should invalidate globals.
+  use_stringn(0);
+
+  size_t c = strnlen(global_str, 10);
+  if (a == 0 && c != 0)
+    (void)*(char*)0; // expected-warning{{null}}  
+}
+
+void strnlen_indirect(char *x) {
+  size_t a = strnlen(x, 10);
+  char *p = x;
+  char **p2 = &p;
+  size_t b = strnlen(x, 10);
+  if (a == 0 && b != 0)
+    (void)*(char*)0; // expected-warning{{never executed}}
+
+  extern void use_stringn_ptr(char*const*);
+  use_stringn_ptr(p2);
+
+  size_t c = strnlen(x, 10);
+  if (a == 0 && c != 0)
+    (void)*(char*)0; // expected-warning{{null}}
+}
+
+void strnlen_liveness(const char *x) {
+  if (strnlen(x, 10) < 5)
+    return;
+  if (strnlen(x, 10) < 5)
+    (void)*(char*)0; // no-warning
+}
+
+//===----------------------------------------------------------------------===
 // strcpy()
 //===----------------------------------------------------------------------===
 





More information about the cfe-commits mailing list