From: Lenny Maiorani Date: Mon, 25 Apr 2011 22:21:00 +0000 (+0000) Subject: Implements the strncmp() checker just like the strcmp() checker, but with bounds... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=357f6ee9f1f6f8e5027377cb3e5907c62c4fe3df;p=clang Implements the strncmp() checker just like the strcmp() checker, but with bounds. Requires LLVM svn r129582. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@130161 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 46f9e6e565..1d25680f07 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -75,6 +75,9 @@ public: void evalStrncat(CheckerContext &C, const CallExpr *CE) const; void evalStrcmp(CheckerContext &C, const CallExpr *CE) const; + void evalStrncmp(CheckerContext &C, const CallExpr *CE) const; + void evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, + bool isBounded = false) const; // Utility methods std::pair @@ -1103,7 +1106,16 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const { //int strcmp(const char *restrict s1, const char *restrict s2); + evalStrcmpCommon(C, CE, /* isBounded = */ false); +} + +void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const { + //int strncmp(const char *restrict s1, const char *restrict s2, size_t n); + evalStrcmpCommon(C, CE, /* isBounded = */ true); +} +void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, + bool isBounded) const { const GRState *state = C.getState(); // Check that the first string is non-null @@ -1142,8 +1154,25 @@ void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const { return; llvm::StringRef s2StrRef = s2StrLiteral->getString(); - // Compare string 1 to string 2 the same way strcmp() does. - int result = s1StrRef.compare(s2StrRef); + int result; + if (isBounded) { + // Get the max number of characters to compare. + const Expr *lenExpr = CE->getArg(2); + SVal lenVal = state->getSVal(lenExpr); + + // Dynamically cast the length to a ConcreteInt. If it is not a ConcreteInt + // then give up, otherwise get the value and use it as the bounds. + nonloc::ConcreteInt *CI = dyn_cast(&lenVal); + if (!CI) + return; + llvm::APSInt lenInt(CI->getValue()); + + // Compare using the bounds provided like strncmp() does. + result = s1StrRef.compare(s2StrRef, (size_t)lenInt.getLimitedValue()); + } else { + // Compare string 1 to string 2 the same way strcmp() does. + result = s1StrRef.compare(s2StrRef); + } // Build the SVal of the comparison to bind the return value. SValBuilder &svalBuilder = C.getSValBuilder(); @@ -1191,6 +1220,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { .Case("strlen", &CStringChecker::evalstrLength) .Case("strnlen", &CStringChecker::evalstrnLength) .Case("strcmp", &CStringChecker::evalStrcmp) + .Case("strncmp", &CStringChecker::evalStrncmp) .Case("bcopy", &CStringChecker::evalBcopy) .Default(NULL); diff --git a/test/Analysis/string.c b/test/Analysis/string.c index 810258a9a3..d81f5ea0cb 100644 --- a/test/Analysis/string.c +++ b/test/Analysis/string.c @@ -682,3 +682,109 @@ void strcmp_diff_length_3() { (void)*(char*)0; // no-warning } +//===----------------------------------------------------------------------=== +// strncmp() +//===----------------------------------------------------------------------=== + +#define strncmp BUILTIN(strncmp) +int strncmp(const char *restrict s1, const char *restrict s2, size_t n); + +void strncmp_constant0() { + if (strncmp("123", "123", 3) != 0) + (void)*(char*)0; // no-warning +} + +void strncmp_constant_and_var_0() { + char *x = "123"; + if (strncmp(x, "123", 3) != 0) + (void)*(char*)0; // no-warning +} + +void strncmp_constant_and_var_1() { + char *x = "123"; + if (strncmp("123", x, 3) != 0) + (void)*(char*)0; // no-warning +} + +void strncmp_0() { + char *x = "123"; + char *y = "123"; + if (strncmp(x, y, 3) != 0) + (void)*(char*)0; // no-warning +} + +void strncmp_1() { + char *x = "234"; + char *y = "123"; + if (strncmp(x, y, 3) != 1) + (void)*(char*)0; // no-warning +} + +void strncmp_2() { + char *x = "123"; + char *y = "234"; + if (strncmp(x, y, 3) != -1) + (void)*(char*)0; // no-warning +} + +void strncmp_null_0() { + char *x = NULL; + char *y = "123"; + strncmp(x, y, 3); // expected-warning{{Null pointer argument in call to byte string function}} +} + +void strncmp_null_1() { + char *x = "123"; + char *y = NULL; + strncmp(x, y, 3); // expected-warning{{Null pointer argument in call to byte string function}} +} + +void strncmp_diff_length_0() { + char *x = "12345"; + char *y = "234"; + if (strncmp(x, y, 5) != -1) + (void)*(char*)0; // no-warning +} + +void strncmp_diff_length_1() { + char *x = "123"; + char *y = "23456"; + if (strncmp(x, y, 5) != -1) + (void)*(char*)0; // no-warning +} + +void strncmp_diff_length_2() { + char *x = "12345"; + char *y = "123"; + if (strncmp(x, y, 5) != 1) + (void)*(char*)0; // no-warning +} + +void strncmp_diff_length_3() { + char *x = "123"; + char *y = "12345"; + if (strncmp(x, y, 5) != -1) + (void)*(char*)0; // no-warning +} + +void strncmp_diff_length_4() { + char *x = "123"; + char *y = "12345"; + if (strncmp(x, y, 3) != 0) + (void)*(char*)0; // no-warning +} + +void strncmp_diff_length_5() { + char *x = "012"; + char *y = "12345"; + if (strncmp(x, y, 3) != -1) + (void)*(char*)0; // no-warning +} + +void strncmp_diff_length_6() { + char *x = "234"; + char *y = "12345"; + if (strncmp(x, y, 3) != 1) + (void)*(char*)0; // no-warning +} +