This patch adds modeling of strcmp() to the CString checker. Validates inputs are not NULL and are real C strings, then does the comparison and binds the proper return value. Unit tests included.

llvm-svn: 129364
This commit is contained in:
Lenny Maiorani 2011-04-12 17:08:43 +00:00
parent 240400b746
commit f3539ad5c7
2 changed files with 169 additions and 0 deletions

View File

@ -74,6 +74,8 @@ public:
void evalStrcat(CheckerContext &C, const CallExpr *CE) const;
void evalStrncat(CheckerContext &C, const CallExpr *CE) const;
void evalStrcmp(CheckerContext &C, const CallExpr *CE) const;
// Utility methods
std::pair<const GRState*, const GRState*>
static assumeZero(CheckerContext &C,
@ -87,6 +89,11 @@ public:
SVal getCStringLength(CheckerContext &C, const GRState *&state,
const Expr *Ex, SVal Buf) const;
const StringLiteral *getCStringLiteral(CheckerContext &C,
const GRState *&state,
const Expr *expr,
SVal val) const;
static const GRState *InvalidateBuffer(CheckerContext &C,
const GRState *state,
const Expr *Ex, SVal V);
@ -587,6 +594,26 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state,
}
}
const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C,
const GRState *&state, const Expr *expr, SVal val) const {
// Get the memory region pointed to by the val.
const MemRegion *bufRegion = val.getAsRegion();
if (!bufRegion)
return NULL;
// Strip casts off the memory region.
bufRegion = bufRegion->StripCasts();
// Cast the memory region to a string region.
const StringRegion *strRegion= dyn_cast<StringRegion>(bufRegion);
if (!strRegion)
return NULL;
// Return the actual string in the string region.
return strRegion->getStringLiteral();
}
const GRState *CStringChecker::InvalidateBuffer(CheckerContext &C,
const GRState *state,
const Expr *E, SVal V) {
@ -1074,6 +1101,61 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
C.addTransition(state);
}
void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const {
//int strcmp(const char *restrict s1, const char *restrict s2);
const GRState *state = C.getState();
// Check that the first string is non-null
const Expr *s1 = CE->getArg(0);
SVal s1Val = state->getSVal(s1);
state = checkNonNull(C, state, s1, s1Val);
if (!state)
return;
// Check that the second string is non-null.
const Expr *s2 = CE->getArg(1);
SVal s2Val = state->getSVal(s2);
state = checkNonNull(C, state, s2, s2Val);
if (!state)
return;
// Get the string length of the first string or give up.
SVal s1Length = getCStringLength(C, state, s1, s1Val);
if (s1Length.isUndef())
return;
// Get the string length of the second string or give up.
SVal s2Length = getCStringLength(C, state, s2, s2Val);
if (s2Length.isUndef())
return;
// Get the string literal of the first string.
const StringLiteral *s1StrLiteral = getCStringLiteral(C, state, s1, s1Val);
if (!s1StrLiteral)
return;
llvm::StringRef s1StrRef = s1StrLiteral->getString();
// Get the string literal of the second string.
const StringLiteral *s2StrLiteral = getCStringLiteral(C, state, s2, s2Val);
if (!s2StrLiteral)
return;
llvm::StringRef s2StrRef = s2StrLiteral->getString();
// Compare string 1 to string 2 the same way strcmp() does.
int result = s1StrRef.compare(s2StrRef);
// Build the SVal of the comparison to bind the return value.
SValBuilder &svalBuilder = C.getSValBuilder();
QualType intTy = svalBuilder.getContext().IntTy;
SVal resultVal = svalBuilder.makeIntVal(result, intTy);
// Bind the return value of the expression.
// Set the return value.
state = state->BindExpr(CE, resultVal);
C.addTransition(state);
}
//===----------------------------------------------------------------------===//
// The driver method, and other Checker callbacks.
//===----------------------------------------------------------------------===//
@ -1108,6 +1190,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
.Cases("strncat", "__strncat_chk", &CStringChecker::evalStrncat)
.Case("strlen", &CStringChecker::evalstrLength)
.Case("strnlen", &CStringChecker::evalstrnLength)
.Case("strcmp", &CStringChecker::evalStrcmp)
.Case("bcopy", &CStringChecker::evalBcopy)
.Default(NULL);

View File

@ -596,3 +596,89 @@ void strncat_no_overflow_2(char *y) {
if (strlen(y) == 4)
strncat(x, y, 1); // no-warning
}
//===----------------------------------------------------------------------===
// strcmp()
//===----------------------------------------------------------------------===
#define strcmp BUILTIN(strcmp)
int strcmp(const char *restrict s1, const char *restrict s2);
void strcmp_constant0() {
if (strcmp("123", "123") != 0)
(void)*(char*)0; // no-warning
}
void strcmp_constant_and_var_0() {
char *x = "123";
if (strcmp(x, "123") != 0)
(void)*(char*)0; // no-warning
}
void strcmp_constant_and_var_1() {
char *x = "123";
if (strcmp("123", x) != 0)
(void)*(char*)0; // no-warning
}
void strcmp_0() {
char *x = "123";
char *y = "123";
if (strcmp(x, y) != 0)
(void)*(char*)0; // no-warning
}
void strcmp_1() {
char *x = "234";
char *y = "123";
if (strcmp(x, y) != 1)
(void)*(char*)0; // no-warning
}
void strcmp_2() {
char *x = "123";
char *y = "234";
if (strcmp(x, y) != -1)
(void)*(char*)0; // no-warning
}
void strcmp_null_0() {
char *x = NULL;
char *y = "123";
strcmp(x, y); // expected-warning{{Null pointer argument in call to byte string function}}
}
void strcmp_null_1() {
char *x = "123";
char *y = NULL;
strcmp(x, y); // expected-warning{{Null pointer argument in call to byte string function}}
}
void strcmp_diff_length_0() {
char *x = "12345";
char *y = "234";
if (strcmp(x, y) != -1)
(void)*(char*)0; // no-warning
}
void strcmp_diff_length_1() {
char *x = "123";
char *y = "23456";
if (strcmp(x, y) != -1)
(void)*(char*)0; // no-warning
}
void strcmp_diff_length_2() {
char *x = "12345";
char *y = "123";
if (strcmp(x, y) != 1)
(void)*(char*)0; // no-warning
}
void strcmp_diff_length_3() {
char *x = "123";
char *y = "12345";
if (strcmp(x, y) != -1)
(void)*(char*)0; // no-warning
}