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

Lenny Maiorani lenny at colorado.edu
Sat Apr 9 08:12:58 PDT 2011


Author: lenny
Date: Sat Apr  9 10:12:58 2011
New Revision: 129215

URL: http://llvm.org/viewvc/llvm-project?rev=129215&view=rev
Log:
strcat() and strncat() model additions to CStringChecker.

Validates inputs are not NULL, checks for overlapping strings, concatenates the strings checking for buffer overflow, sets the length of the destination string to the sum of the s1 length and the s2 length, binds the return value to the s1 value.


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=129215&r1=129214&r2=129215&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp Sat Apr  9 10:12:58 2011
@@ -69,7 +69,10 @@
   void evalStrncpy(CheckerContext &C, const CallExpr *CE) const;
   void evalStpcpy(CheckerContext &C, const CallExpr *CE) const;
   void evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool returnEnd,
-                        bool isStrncpy) const;
+                        bool isBounded, bool isAppending) const;
+
+  void evalStrcat(CheckerContext &C, const CallExpr *CE) const;
+  void evalStrncat(CheckerContext &C, const CallExpr *CE) const;
 
   // Utility methods
   std::pair<const GRState*, const GRState*>
@@ -912,24 +915,50 @@
 
 void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const {
   // char *strcpy(char *restrict dst, const char *restrict src);
-  evalStrcpyCommon(C, CE, /* returnEnd = */ false, /* isStrncpy = */ false);
+  evalStrcpyCommon(C, CE, 
+                   /* returnEnd = */ false, 
+                   /* isBounded = */ false,
+                   /* isAppending = */ false);
 }
 
 void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const {
   // char *strcpy(char *restrict dst, const char *restrict src);
-  evalStrcpyCommon(C, CE, /* returnEnd = */ false, /* isStrncpy = */ true);
+  evalStrcpyCommon(C, CE, 
+                   /* returnEnd = */ false, 
+                   /* isBounded = */ true,
+                   /* isAppending = */ false);
 }
 
 void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const {
   // char *stpcpy(char *restrict dst, const char *restrict src);
-  evalStrcpyCommon(C, CE, /* returnEnd = */ true, /* isStrncpy = */ false);
+  evalStrcpyCommon(C, CE, 
+                   /* returnEnd = */ true, 
+                   /* isBounded = */ false,
+                   /* isAppending = */ false);
+}
+
+void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const {
+  //char *strcat(char *restrict s1, const char *restrict s2);
+  evalStrcpyCommon(C, CE, 
+                   /* returnEnd = */ false, 
+                   /* isBounded = */ false,
+                   /* isAppending = */ true);
+}
+
+void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const {
+  //char *strncat(char *restrict s1, const char *restrict s2, size_t n);
+  evalStrcpyCommon(C, CE, 
+                   /* returnEnd = */ false, 
+                   /* isBounded = */ true,
+                   /* isAppending = */ true);
 }
 
 void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
-                                      bool returnEnd, bool isStrncpy) const {
+                                      bool returnEnd, bool isBounded,
+                                      bool isAppending) const {
   const GRState *state = C.getState();
 
-  // Check that the destination is non-null
+  // Check that the destination is non-null.
   const Expr *Dst = CE->getArg(0);
   SVal DstVal = state->getSVal(Dst);
 
@@ -951,8 +980,9 @@
   if (strLength.isUndef())
     return;
 
-  if (isStrncpy) {
-    // Get the max number of characters to copy
+  // If the function is strncpy, strncat, etc... it is bounded.
+  if (isBounded) {
+    // Get the max number of characters to copy.
     const Expr *lenExpr = CE->getArg(2);
     SVal lenVal = state->getSVal(lenExpr);
 
@@ -962,7 +992,7 @@
     QualType cmpTy = C.getSValBuilder().getContext().IntTy;
     const GRState *stateTrue, *stateFalse;
     
-    // Check if the max number to copy is less than the length of the src
+    // Check if the max number to copy is less than the length of the src.
     llvm::tie(stateTrue, stateFalse) =
       state->assume(cast<DefinedOrUnknownSVal>
                     (C.getSValBuilder().evalBinOpNN(state, BO_GT, 
@@ -976,6 +1006,29 @@
     }    
   }
 
+  // If this is an appending function (strcat, strncat...) then set the
+  // string length to strlen(src) + strlen(dst) since the buffer will
+  // ultimately contain both.
+  if (isAppending) {
+    // Get the string length of the destination, or give up.
+    SVal dstStrLength = getCStringLength(C, state, Dst, DstVal);
+    if (dstStrLength.isUndef())
+      return;
+
+    NonLoc *srcStrLengthNL = dyn_cast<NonLoc>(&strLength);
+    NonLoc *dstStrLengthNL = dyn_cast<NonLoc>(&dstStrLength);
+    
+    // If src or dst cast to NonLoc is NULL, give up.
+    if ((!srcStrLengthNL) || (!dstStrLengthNL))
+      return;
+
+    QualType addTy = C.getSValBuilder().getContext().getSizeType();
+
+    strLength = C.getSValBuilder().evalBinOpNN(state, BO_Add, 
+                                               *srcStrLengthNL, *dstStrLengthNL,
+                                               addTy);
+  }
+
   SVal Result = (returnEnd ? UnknownVal() : DstVal);
 
   // If the destination is a MemRegion, try to check for a buffer overflow and
@@ -1051,6 +1104,8 @@
     .Cases("strcpy", "__strcpy_chk", &CStringChecker::evalStrcpy)
     .Cases("strncpy", "__strncpy_chk", &CStringChecker::evalStrncpy)
     .Cases("stpcpy", "__stpcpy_chk", &CStringChecker::evalStpcpy)
+    .Cases("strcat", "__strcat_chk", &CStringChecker::evalStrcat)
+    .Cases("strncat", "__strncat_chk", &CStringChecker::evalStrncat)
     .Case("strlen", &CStringChecker::evalstrLength)
     .Case("strnlen", &CStringChecker::evalstrnLength)
     .Case("bcopy", &CStringChecker::evalBcopy)

Modified: cfe/trunk/test/Analysis/string.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/string.c?rev=129215&r1=129214&r2=129215&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/string.c (original)
+++ cfe/trunk/test/Analysis/string.c Sat Apr  9 10:12:58 2011
@@ -438,3 +438,161 @@
   if (strlen(y) == 3)
     stpcpy(x, y); // no-warning
 }
+
+//===----------------------------------------------------------------------===
+// strcat()
+//===----------------------------------------------------------------------===
+
+#ifdef VARIANT
+
+#define __strcat_chk BUILTIN(__strcat_chk)
+char *__strcat_chk(char *restrict s1, const char *restrict s2, size_t destlen);
+
+#define strcat(a,b) __strcat_chk(a,b,(size_t)-1)
+
+#else /* VARIANT */
+
+#define strcat BUILTIN(strcat)
+char *strcat(char *restrict s1, const char *restrict s2);
+
+#endif /* VARIANT */
+
+
+void strcat_null_dst(char *x) {
+  strcat(NULL, x); // expected-warning{{Null pointer argument in call to byte string function}}
+}
+
+void strcat_null_src(char *x) {
+  strcat(x, NULL); // expected-warning{{Null pointer argument in call to byte string function}}
+}
+
+void strcat_fn(char *x) {
+  strcat(x, (char*)&strcat_fn); // expected-warning{{Argument to byte string function is the address of the function 'strcat_fn', which is not a null-terminated string}}
+}
+
+void strcat_effects(char *y) {
+  char x[8] = "123";
+  size_t orig_len = strlen(x);
+  char a = x[0];
+
+  if (strlen(y) != 4)
+    return;
+
+  if (strcat(x, y) != x)
+    (void)*(char*)0; // no-warning
+
+  if ((int)strlen(x) != (orig_len + strlen(y)))
+    (void)*(char*)0; // no-warning
+
+  if (a != x[0])
+    (void)*(char*)0; // expected-warning{{null}}
+}
+
+void strcat_overflow_0(char *y) {
+  char x[4] = "12";
+  if (strlen(y) == 4)
+    strcat(x, y); // expected-warning{{Byte string function overflows destination buffer}}
+}
+
+void strcat_overflow_1(char *y) {
+  char x[4] = "12";
+  if (strlen(y) == 3)
+    strcat(x, y); // expected-warning{{Byte string function overflows destination buffer}}
+}
+
+void strcat_overflow_2(char *y) {
+  char x[4] = "12";
+  if (strlen(y) == 2)
+    strcat(x, y); // expected-warning{{Byte string function overflows destination buffer}}
+}
+
+void strcat_no_overflow(char *y) {
+  char x[5] = "12";
+  if (strlen(y) == 2)
+    strcat(x, y); // no-warning
+}
+
+
+//===----------------------------------------------------------------------===
+// strncat()
+//===----------------------------------------------------------------------===
+
+#ifdef VARIANT
+
+#define __strncat_chk BUILTIN(__strncat_chk)
+char *__strncat_chk(char *restrict s1, const char *restrict s2, size_t n, size_t destlen);
+
+#define strncat(a,b,c) __strncat_chk(a,b,c, (size_t)-1)
+
+#else /* VARIANT */
+
+#define strncat BUILTIN(strncat)
+char *strncat(char *restrict s1, const char *restrict s2, size_t n);
+
+#endif /* VARIANT */
+
+
+void strncat_null_dst(char *x) {
+  strncat(NULL, x, 4); // expected-warning{{Null pointer argument in call to byte string function}}
+}
+
+void strncat_null_src(char *x) {
+  strncat(x, NULL, 4); // expected-warning{{Null pointer argument in call to byte string function}}
+}
+
+void strncat_fn(char *x) {
+  strncat(x, (char*)&strncat_fn, 4); // expected-warning{{Argument to byte string function is the address of the function 'strncat_fn', which is not a null-terminated string}}
+}
+
+void strncat_effects(char *y) {
+  char x[8] = "123";
+  size_t orig_len = strlen(x);
+  char a = x[0];
+
+  if (strlen(y) != 4)
+    return;
+
+  if (strncat(x, y, strlen(y)) != x)
+    (void)*(char*)0; // no-warning
+
+  if (strlen(x) != orig_len + strlen(y))
+    (void)*(char*)0; // no-warning
+
+  if (a != x[0])
+    (void)*(char*)0; // expected-warning{{null}}
+}
+
+void strncat_overflow_0(char *y) {
+  char x[4] = "12";
+  if (strlen(y) == 4)
+    strncat(x, y, strlen(y)); // expected-warning{{Byte string function overflows destination buffer}}
+}
+
+void strncat_overflow_1(char *y) {
+  char x[4] = "12";
+  if (strlen(y) == 3)
+    strncat(x, y, strlen(y)); // expected-warning{{Byte string function overflows destination buffer}}
+}
+
+void strncat_overflow_2(char *y) {
+  char x[4] = "12";
+  if (strlen(y) == 2)
+    strncat(x, y, strlen(y)); // expected-warning{{Byte string function overflows destination buffer}}
+}
+
+void strncat_overflow_3(char *y) {
+  char x[4] = "12";
+  if (strlen(y) == 4)
+    strncat(x, y, 2); // expected-warning{{Byte string function overflows destination buffer}}
+}
+void strncat_no_overflow_1(char *y) {
+  char x[5] = "12";
+  if (strlen(y) == 2)
+    strncat(x, y, strlen(y)); // no-warning
+}
+
+void strncat_no_overflow_2(char *y) {
+  char x[4] = "12";
+  if (strlen(y) == 4)
+    strncat(x, y, 1); // no-warning
+}





More information about the cfe-commits mailing list