diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -480,6 +480,14 @@ if (!Filter.CheckCStringOutOfBounds) return State; + SVal BufStart = + svalBuilder.evalCast(BufVal, PtrTy, Buffer.Expression->getType()); + + // Check if the first byte of the buffer is accessible. + State = CheckLocation(C, State, Buffer, BufStart, Access, CK); + if (!State) + return nullptr; + // Get the access length and make sure it is known. // FIXME: This assumes the caller has already checked that the access length // is positive. And that it's unsigned. @@ -496,8 +504,6 @@ NonLoc LastOffset = Offset.castAs(); // Check that the first buffer is sufficiently long. - SVal BufStart = - svalBuilder.evalCast(BufVal, PtrTy, Buffer.Expression->getType()); if (std::optional BufLoc = BufStart.getAs()) { SVal BufEnd = diff --git a/clang/test/Analysis/string.c b/clang/test/Analysis/string.c --- a/clang/test/Analysis/string.c +++ b/clang/test/Analysis/string.c @@ -71,6 +71,7 @@ int scanf(const char *restrict format, ...); void *malloc(size_t); void free(void *); +void *memcpy(void *dest, const void *src, unsigned long n); //===----------------------------------------------------------------------=== // strlen() @@ -1713,3 +1714,21 @@ free(dataBuffer); } #endif + +#ifndef SUPPRESS_OUT_OF_BOUND +void CWE124_Buffer_Underwrite__malloc_char_memcpy() { + char * dataBuffer = (char *)malloc(100*sizeof(char)); + if (dataBuffer == NULL) return; + memset(dataBuffer, 'A', 100-1); + dataBuffer[100-1] = '\0'; + char *data = dataBuffer - 8; + + char source[100]; + memset(source, 'C', 100-1); // fill with 'C's + source[100-1] = '\0'; // null terminate + + memcpy(data, source, 100*sizeof(char)); // expected-warning {{Memory copy function overflows the destination buffer}} + data[100-1] = '\0'; + free(dataBuffer); +} +#endif