From: Lenny Maiorani Date: Tue, 12 Apr 2011 17:08:43 +0000 (+0000) Subject: This patch adds modeling of strcmp() to the CString checker. Validates inputs are... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=318dd92ad834857ea5bb91de288c1eb56cdbec1a;p=clang 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@129364 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index c98bf5c598..60628cfd2f 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -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 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(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); diff --git a/test/Analysis/string.c b/test/Analysis/string.c index d9e0cf8c4e..a68b4d7759 100644 --- a/test/Analysis/string.c +++ b/test/Analysis/string.c @@ -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 +} +