[analyzer] Fix use of length in CStringChecker
authoreinvbri <vince.a.bridgers@ericsson.com>
Thu, 7 Jul 2022 09:38:56 +0000 (04:38 -0500)
committereinvbri <vince.a.bridgers@ericsson.com>
Thu, 14 Jul 2022 00:19:23 +0000 (19:19 -0500)
CStringChecker is using getByteLength to get the length of a string
literal. For targets where a "char" is 8-bits, getByteLength() and
getLength() will be equal for a C string, but for targets where a "char"
is 16-bits getByteLength() returns the size in octets.

This is verified in our downstream target, but we have no way to add a
test case for this case since there is no target supporting 16-bit
"char" upstream. Since this cannot have a test case, I'm asserted this
change is "correct by construction", and visually inspected to be
correct by way of the following example where this was found.

The case that shows this fails using a target with 16-bit chars is here.
getByteLength() for the string literal returns 4, which fails when
checked against "char x[4]". With the change, the string literal is
evaluated to a size of 2 which is a correct number of "char"'s for a
16-bit target.

```
void strcpy_no_overflow_2(char *y) {
  char x[4];
  strcpy(x, "12"); // with getByteLength(), returns 4 using 16-bit chars
}
```

This change exposed that embedded nulls within the string are not
handled. This is documented as a FIXME for a future fix.

```
    void strcpy_no_overflow_3(char *y) {
      char x[3];
      strcpy(x, "12\0");
    }

```

Reviewed By: martong

Differential Revision: https://reviews.llvm.org/D129269

clang/docs/analyzer/checkers.rst
clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
clang/test/Analysis/string.c

index 6f2486fb49dfc79716753b4aa29505bf3aeb8f0b..da53fad14bc570c8e6812300c3bbe2e35ac4bb4d 100644 (file)
@@ -2726,6 +2726,9 @@ alpha.unix.cstring.OutOfBounds (C)
 """"""""""""""""""""""""""""""""""
 Check for out-of-bounds access in string functions; applies to:`` strncopy, strncat``.
 
+This check also applies to string literals, except there is a known bug in that
+the analyzer cannot detect embedded NULL characters.
+
 .. code-block:: c
 
  void test() {
index 2e4c8e6436988b2fd8883470f3bb18ce37d39614..987cf65d6fec6986f0b59090b9ee192ba9eaa6c0 100644 (file)
@@ -848,7 +848,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
     SValBuilder &svalBuilder = C.getSValBuilder();
     QualType sizeTy = svalBuilder.getContext().getSizeType();
     const StringLiteral *strLit = cast<StringRegion>(MR)->getStringLiteral();
-    return svalBuilder.makeIntVal(strLit->getByteLength(), sizeTy);
+    return svalBuilder.makeIntVal(strLit->getLength(), sizeTy);
   }
   case MemRegion::SymbolicRegionKind:
   case MemRegion::AllocaRegionKind:
index d1fcd92e732708f41ee64565a587182a7dc737cb..6bdf59c56f63b8acd9a2d66c284aaa4c1836ec0a 100644 (file)
@@ -1652,3 +1652,18 @@ void test_memset_chk(void) {
   __builtin___memset_chk(&x, 0, sizeof(x), __builtin_object_size(&x, 0));
   clang_analyzer_eval(x == 0); // expected-warning{{TRUE}}
 }
+
+#ifndef SUPPRESS_OUT_OF_BOUND
+void strcpy_no_overflow_2(char *y) {
+  char x[3];
+  // FIXME: string literal modeling does not account for embedded NULLs.
+  //        This case should not elicit a warning, but does.
+  //        See discussion at https://reviews.llvm.org/D129269
+  strcpy(x, "12\0"); // expected-warning{{String copy function overflows the destination buffer}}
+}
+#else
+void strcpy_no_overflow_2(char *y) {
+  char x[3];
+  strcpy(x, "12\0");
+}
+#endif