]> granicus.if.org Git - clang/commitdiff
Add CStringChecker support for strncpy. Patch by Lenny Maiorani!
authorTed Kremenek <kremenek@apple.com>
Tue, 22 Feb 2011 04:58:34 +0000 (04:58 +0000)
committerTed Kremenek <kremenek@apple.com>
Tue, 22 Feb 2011 04:58:34 +0000 (04:58 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@126188 91177308-0d34-0410-b5e6-96231b3b80d8

lib/StaticAnalyzer/Checkers/CStringChecker.cpp
test/Analysis/string.c

index e9722a6de6b81d053a738901fa958d10dcd34e13..ddeb368f2b92eceae57321900bee05bb929f49b3 100644 (file)
@@ -59,8 +59,10 @@ public:
                            bool IsStrnlen = false);
 
   void evalStrcpy(CheckerContext &C, const CallExpr *CE);
+  void evalStrncpy(CheckerContext &C, const CallExpr *CE);
   void evalStpcpy(CheckerContext &C, const CallExpr *CE);
-  void evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool returnEnd);
+  void evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool returnEnd,
+                        bool isStrncpy);
 
   // Utility methods
   std::pair<const GRState*, const GRState*>
@@ -845,16 +847,21 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
 
 void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) {
   // char *strcpy(char *restrict dst, const char *restrict src);
-  evalStrcpyCommon(C, CE, /* returnEnd = */ false);
+  evalStrcpyCommon(C, CE, /* returnEnd = */ false, /* isStrncpy = */ false);
+}
+
+void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) {
+  // char *strcpy(char *restrict dst, const char *restrict src);
+  evalStrcpyCommon(C, CE, /* returnEnd = */ false, /* isStrncpy = */ true);
 }
 
 void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) {
   // char *stpcpy(char *restrict dst, const char *restrict src);
-  evalStrcpyCommon(C, CE, /* returnEnd = */ true);
+  evalStrcpyCommon(C, CE, /* returnEnd = */ true, /* isStrncpy = */ false);
 }
 
 void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
-                                      bool returnEnd) {
+                                      bool returnEnd, bool isStrncpy) {
   const GRState *state = C.getState();
 
   // Check that the destination is non-null
@@ -879,6 +886,31 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
   if (strLength.isUndef())
     return;
 
+  if (isStrncpy) {
+    // Get the max number of characters to copy
+    const Expr *lenExpr = CE->getArg(2);
+    SVal lenVal = state->getSVal(lenExpr);
+
+    NonLoc *strLengthNL = dyn_cast<NonLoc>(&strLength);
+    NonLoc *lenValNL = dyn_cast<NonLoc>(&lenVal);
+
+    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
+    llvm::tie(stateTrue, stateFalse) =
+      state->assume(cast<DefinedOrUnknownSVal>
+                    (C.getSValBuilder().evalBinOpNN(state, BO_GT, 
+                                                    *strLengthNL, *lenValNL,
+                                                    cmpTy)));
+
+    if (stateTrue) {
+      // Max number to copy is less than the length of the src, so the actual
+      // strLength copied is the max number arg.
+      strLength = lenVal;
+    }    
+  }
+
   SVal Result = (returnEnd ? UnknownVal() : DstVal);
 
   // If the destination is a MemRegion, try to check for a buffer overflow and
@@ -951,6 +983,7 @@ bool CStringChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) {
     .Cases("memcmp", "bcmp", &CStringChecker::evalMemcmp)
     .Cases("memmove", "__memmove_chk", &CStringChecker::evalMemmove)
     .Cases("strcpy", "__strcpy_chk", &CStringChecker::evalStrcpy)
+    .Cases("strncpy", "__strncpy_chk", &CStringChecker::evalStrncpy)
     .Cases("stpcpy", "__stpcpy_chk", &CStringChecker::evalStpcpy)
     .Case("strlen", &CStringChecker::evalstrLength)
     .Case("strnlen", &CStringChecker::evalstrnLength)
index d19439fde56b2c0167d21f1b2f2355c8373d27ad..ac53ae472bb4a2c7a1b378b00822770bb4baa9e7 100644 (file)
@@ -327,6 +327,74 @@ void strcpy_no_overflow(char *y) {
     strcpy(x, y); // no-warning
 }
 
+//===----------------------------------------------------------------------===
+// strncpy()
+//===----------------------------------------------------------------------===
+
+#ifdef VARIANT
+
+#define __strncpy_chk BUILTIN(__strncpy_chk)
+char *__strncpy_chk(char *restrict s1, const char *restrict s2, size_t n, size_t destlen);
+
+#define strncpy(a,b,c) __strncpy_chk(a,b,c, (size_t)-1)
+
+#else /* VARIANT */
+
+#define strncpy BUILTIN(strncpy)
+char *strncpy(char *restrict s1, const char *restrict s2, size_t n);
+
+#endif /* VARIANT */
+
+
+void strncpy_null_dst(char *x) {
+  strncpy(NULL, x, 1); // expected-warning{{Null pointer argument in call to byte string function}}
+}
+
+void strncpy_null_src(char *x) {
+  strncpy(x, NULL, 1); // expected-warning{{Null pointer argument in call to byte string function}}
+}
+
+void strncpy_fn(char *x) {
+  strncpy(x, (char*)&strncpy_fn, 1); // expected-warning{{Argument to byte string function is the address of the function 'strncpy_fn', which is not a null-terminated string}}
+}
+
+void strncpy_effects(char *x, char *y) {
+  char a = x[0];
+
+  if (strncpy(x, y, strlen(y)) != x)
+    (void)*(char*)0; // no-warning
+
+  if (strlen(x) != strlen(y))
+    (void)*(char*)0; // no-warning
+
+  if (a != x[0])
+    (void)*(char*)0; // expected-warning{{null}}
+}
+
+void strncpy_overflow(char *y) {
+  char x[4];
+  if (strlen(y) == 4)
+    strncpy(x, y, strlen(y)); // expected-warning{{Byte string function overflows destination buffer}}
+}
+
+void strncpy_len_overflow(char *y) {
+  char x[4];
+  if (strlen(y) == 3)
+    strncpy(x, y, sizeof(x)); // no-warning
+}
+
+void strncpy_no_overflow(char *y) {
+  char x[4];
+  if (strlen(y) == 3)
+    strncpy(x, y, strlen(y)); // no-warning
+}
+
+void strncpy_no_len_overflow(char *y) {
+  char x[4];
+  if (strlen(y) == 4)
+    strncpy(x, y, sizeof(x)-1); // no-warning
+}
+
 //===----------------------------------------------------------------------===
 // stpcpy()
 //===----------------------------------------------------------------------===