]> granicus.if.org Git - clang/commitdiff
[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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@156769 91177308-0d34-0410-b5e6-96231b3b80d8

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

index bd1a56a1aa05811f3f999e8f11ab19d8b2418376..942280d03d270077ded0eaf5d9da9105d72c5cd2 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 24e29ebb02b39806919777e2eeccd72dcc7e0790..dc38681b35b28b170cb41828fee26fc8a64a525b 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()
 //===----------------------------------------------------------------------===