Skip to content

Commit 2b3f207

Browse files
committed
[analyzer] CStringChecker: Fix overly eager assumption that memcmp args overlap.
While analyzing code `memcmp(a, NULL, n);', where `a' has an unconstrained symbolic value, the analyzer was emitting a warning about the *first* argument being a null pointer, even though we'd rather have it warn about the *second* argument. This happens because CStringChecker first checks whether the two argument buffers are in fact the same buffer, in order to take the fast path. This boils down to assuming `a == NULL' to true. Then the subsequent check for null pointer argument "discovers" that `a' is null. Don't take the fast path unless we are *sure* that the buffers are the same. Otherwise proceed as normal. Differential Revision: https://reviews.llvm.org/D71322
1 parent 134faae commit 2b3f207

File tree

3 files changed

+43
-14
lines changed

3 files changed

+43
-14
lines changed

clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,30 +1313,29 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
13131313
ProgramStateRef StSameBuf, StNotSameBuf;
13141314
std::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf);
13151315

1316-
// If the two arguments might be the same buffer, we know the result is 0,
1316+
// If the two arguments are the same buffer, we know the result is 0,
13171317
// and we only need to check one size.
1318-
if (StSameBuf) {
1318+
if (StSameBuf && !StNotSameBuf) {
13191319
state = StSameBuf;
13201320
state = CheckBufferAccess(C, state, Size, Left);
13211321
if (state) {
13221322
state = StSameBuf->BindExpr(CE, LCtx,
13231323
svalBuilder.makeZeroVal(CE->getType()));
13241324
C.addTransition(state);
13251325
}
1326+
return;
13261327
}
13271328

1328-
// If the two arguments might be different buffers, we have to check the
1329-
// size of both of them.
1330-
if (StNotSameBuf) {
1331-
state = StNotSameBuf;
1332-
state = CheckBufferAccess(C, state, Size, Left, Right);
1333-
if (state) {
1334-
// The return value is the comparison result, which we don't know.
1335-
SVal CmpV = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx,
1336-
C.blockCount());
1337-
state = state->BindExpr(CE, LCtx, CmpV);
1338-
C.addTransition(state);
1339-
}
1329+
// If the two arguments might be different buffers, we have to check
1330+
// the size of both of them.
1331+
assert(StNotSameBuf);
1332+
state = CheckBufferAccess(C, state, Size, Left, Right);
1333+
if (state) {
1334+
// The return value is the comparison result, which we don't know.
1335+
SVal CmpV =
1336+
svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
1337+
state = state->BindExpr(CE, LCtx, CmpV);
1338+
C.addTransition(state);
13401339
}
13411340
}
13421341
}

clang/test/Analysis/bstring.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,12 @@ int memcmp7 (char *a, size_t x, size_t y, size_t n) {
462462
memcmp(&a[x*y], a, n);
463463
}
464464

465+
int memcmp8(char *a, size_t n) {
466+
char *b = 0;
467+
// Do not warn about the first argument!
468+
return memcmp(a, b, n); // expected-warning{{Null pointer passed as 2nd argument to memory comparison function}}
469+
}
470+
465471
//===----------------------------------------------------------------------===
466472
// bcopy()
467473
//===----------------------------------------------------------------------===

clang/test/Analysis/string.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,12 @@ void strcmp_union_function_pointer_cast(union argument a) {
867867
fPtr(&a);
868868
}
869869

870+
int strcmp_null_argument(char *a) {
871+
char *b = 0;
872+
// Do not warn about the first argument!
873+
return strcmp(a, b); // expected-warning{{Null pointer passed as 2nd argument to string comparison function}}
874+
}
875+
870876
//===----------------------------------------------------------------------===
871877
// strncmp()
872878
//===----------------------------------------------------------------------===
@@ -976,6 +982,12 @@ void strncmp_embedded_null () {
976982
clang_analyzer_eval(strncmp("ab\0zz", "ab\0yy", 4) == 0); // expected-warning{{TRUE}}
977983
}
978984

985+
int strncmp_null_argument(char *a, size_t n) {
986+
char *b = 0;
987+
// Do not warn about the first argument!
988+
return strncmp(a, b, n); // expected-warning{{Null pointer passed as 2nd argument to string comparison function}}
989+
}
990+
979991
//===----------------------------------------------------------------------===
980992
// strcasecmp()
981993
//===----------------------------------------------------------------------===
@@ -1067,6 +1079,12 @@ void strcasecmp_embedded_null () {
10671079
clang_analyzer_eval(strcasecmp("ab\0zz", "ab\0yy") == 0); // expected-warning{{TRUE}}
10681080
}
10691081

1082+
int strcasecmp_null_argument(char *a) {
1083+
char *b = 0;
1084+
// Do not warn about the first argument!
1085+
return strcasecmp(a, b); // expected-warning{{Null pointer passed as 2nd argument to string comparison function}}
1086+
}
1087+
10701088
//===----------------------------------------------------------------------===
10711089
// strncasecmp()
10721090
//===----------------------------------------------------------------------===
@@ -1176,6 +1194,12 @@ void strncasecmp_embedded_null () {
11761194
clang_analyzer_eval(strncasecmp("ab\0zz", "ab\0yy", 4) == 0); // expected-warning{{TRUE}}
11771195
}
11781196

1197+
int strncasecmp_null_argument(char *a, size_t n) {
1198+
char *b = 0;
1199+
// Do not warn about the first argument!
1200+
return strncasecmp(a, b, n); // expected-warning{{Null pointer passed as 2nd argument to string comparison function}}
1201+
}
1202+
11791203
//===----------------------------------------------------------------------===
11801204
// strsep()
11811205
//===----------------------------------------------------------------------===

0 commit comments

Comments
 (0)