[analyzer] strncpy: Special-case a length of 0 to avoid an incorrect warning.
authorJordy Rose <jediknil@belkadan.com>
Mon, 14 May 2012 17:58:35 +0000 (17:58 +0000)
committerJordy Rose <jediknil@belkadan.com>
Mon, 14 May 2012 17:58:35 +0000 (17:58 +0000)
We check the address of the last element accessed, but with 0 calculating that
address results in element -1. This patch bails out early (and avoids a bunch
of other work at that).

Fixes PR12807.

llvm-svn: 156769

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

index bd1a56a..942280d 100644 (file)
@@ -1404,6 +1404,24 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
         // For strncpy, this is just checking that lenVal <= sizeof(dst)
         // (Yes, strncpy and strncat differ in how they treat termination.
         // strncat ALWAYS terminates, but strncpy doesn't.)
+
+        // We need a special case for when the copy size is zero, in which
+        // case strncpy will do no work at all. Our bounds check uses n-1
+        // as the last element accessed, so n == 0 is problematic.
+        ProgramStateRef StateZeroSize, StateNonZeroSize;
+        llvm::tie(StateZeroSize, StateNonZeroSize) =
+          assumeZero(C, state, *lenValNL, sizeTy);
+
+        // If the size is known to be zero, we're done.
+        if (StateZeroSize && !StateNonZeroSize) {
+          StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, DstVal);
+          C.addTransition(StateZeroSize);
+          return;
+        }
+
+        // Otherwise, go ahead and figure out the last element we'll touch.
+        // We don't record the non-zero assumption here because we can't
+        // be sure. We won't warn on a possible zero.
         NonLoc one = cast<NonLoc>(svalBuilder.makeIntVal(1, sizeTy));
         maxLastElementIndex = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL,
                                                       one, sizeTy);
index 24e29eb..dc38681 100644 (file)
@@ -578,6 +578,17 @@ void strncpy_exactly_matching_buffer2(char *y) {
                (void)*(int*)0; // no-warning
 }
 
+void strncpy_zero(char *src) {
+  char dst[] = "123";
+  strncpy(dst, src, 0); // no-warning
+}
+
+void strncpy_empty() {
+  char dst[] = "123";
+  char src[] = "";
+  strncpy(dst, src, 4); // no-warning
+}
+
 //===----------------------------------------------------------------------===
 // strncat()
 //===----------------------------------------------------------------------===
@@ -716,6 +727,17 @@ void strncat_too_big(char *dst, char *src) {
   strncat(dst, src, 2); // expected-warning{{This expression will create a string whose length is too big to be represented as a size_t}}
 }
 
+void strncat_zero(char *src) {
+  char dst[] = "123";
+  strncat(dst, src, 0); // no-warning
+}
+
+void strncat_empty() {
+  char dst[8] = "123";
+  char src[] = "";
+  strncat(dst, src, 4); // no-warning
+}
+
 //===----------------------------------------------------------------------===
 // strcmp()
 //===----------------------------------------------------------------------===