diff options
author | Ted Kremenek <kremenek@apple.com> | 2011-02-22 04:55:05 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2011-02-22 04:55:05 +0000 |
commit | be4242ce039f0542ea0dd5f234aa0ee698f90c53 (patch) | |
tree | 782d4dd22d1cb659c979f9509a2fb9babfdc1dd8 | |
parent | 6f17433b2d50262856ab09f52af96c6132b01012 (diff) |
Add CStringChecker support for strnlen. Patch by Lenny Maiorani!
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@126187 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 40 | ||||
-rw-r--r-- | test/Analysis/string.c | 132 |
2 files changed, 172 insertions, 0 deletions
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 03f9047e96..e9722a6de6 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -54,6 +54,9 @@ public: void evalMemcmp(CheckerContext &C, const CallExpr *CE); void evalstrLength(CheckerContext &C, const CallExpr *CE); + void evalstrnLength(CheckerContext &C, const CallExpr *CE); + void evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, + bool IsStrnlen = false); void evalStrcpy(CheckerContext &C, const CallExpr *CE); void evalStpcpy(CheckerContext &C, const CallExpr *CE); @@ -776,6 +779,16 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) { void CStringChecker::evalstrLength(CheckerContext &C, const CallExpr *CE) { // size_t strlen(const char *s); + evalstrLengthCommon(C, CE, /* IsStrnlen = */ false); +} + +void CStringChecker::evalstrnLength(CheckerContext &C, const CallExpr *CE) { + // size_t strnlen(const char *s, size_t maxlen); + evalstrLengthCommon(C, CE, /* IsStrnlen = */ true); +} + +void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, + bool IsStrnlen) { const GRState *state = C.getState(); const Expr *Arg = CE->getArg(0); SVal ArgVal = state->getSVal(Arg); @@ -791,6 +804,32 @@ void CStringChecker::evalstrLength(CheckerContext &C, const CallExpr *CE) { if (strLength.isUndef()) return; + // If the check is for strnlen() then bind the return value to no more than + // the maxlen value. + if (IsStrnlen) { + const Expr *maxlenExpr = CE->getArg(1); + SVal maxlenVal = state->getSVal(maxlenExpr); + + NonLoc *strLengthNL = dyn_cast<NonLoc>(&strLength); + NonLoc *maxlenValNL = dyn_cast<NonLoc>(&maxlenVal); + + QualType cmpTy = C.getSValBuilder().getContext().IntTy; + const GRState *stateTrue, *stateFalse; + + // Check if the strLength is greater than or equal to the maxlen + llvm::tie(stateTrue, stateFalse) = + state->assume(cast<DefinedOrUnknownSVal> + (C.getSValBuilder().evalBinOpNN(state, BO_GE, + *strLengthNL, *maxlenValNL, + cmpTy))); + + // If the strLength is greater than or equal to the maxlen, set strLength + // to maxlen + if (stateTrue && !stateFalse) { + strLength = maxlenVal; + } + } + // If getCStringLength couldn't figure out the length, conjure a return // value, so it can be used in constraints, at least. if (strLength.isUnknown()) { @@ -914,6 +953,7 @@ bool CStringChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) { .Cases("strcpy", "__strcpy_chk", &CStringChecker::evalStrcpy) .Cases("stpcpy", "__stpcpy_chk", &CStringChecker::evalStpcpy) .Case("strlen", &CStringChecker::evalstrLength) + .Case("strnlen", &CStringChecker::evalstrnLength) .Case("bcopy", &CStringChecker::evalBcopy) .Default(NULL); diff --git a/test/Analysis/string.c b/test/Analysis/string.c index baf48930d7..d19439fde5 100644 --- a/test/Analysis/string.c +++ b/test/Analysis/string.c @@ -140,6 +140,138 @@ void strlen_liveness(const char *x) { } //===----------------------------------------------------------------------=== +// strnlen() +//===----------------------------------------------------------------------=== + +#define strnlen BUILTIN(strnlen) +size_t strnlen(const char *s, size_t maxlen); + +void strnlen_constant0() { + if (strnlen("123", 10) != 3) + (void)*(char*)0; // no-warning +} + +void strnlen_constant1() { + const char *a = "123"; + if (strnlen(a, 10) != 3) + (void)*(char*)0; // no-warning +} + +void strnlen_constant2(char x) { + char a[] = "123"; + if (strnlen(a, 10) != 3) + (void)*(char*)0; // no-warning + a[0] = x; + if (strnlen(a, 10) != 3) + (void)*(char*)0; // expected-warning{{null}} +} + +void strnlen_constant4() { + if (strnlen("123456", 3) != 3) + (void)*(char*)0; // no-warning +} + +void strnlen_constant5() { + const char *a = "123456"; + if (strnlen(a, 3) != 3) + (void)*(char*)0; // no-warning +} + +void strnlen_constant6(char x) { + char a[] = "123456"; + if (strnlen(a, 3) != 3) + (void)*(char*)0; // no-warning + a[0] = x; + if (strnlen(a, 3) != 3) + (void)*(char*)0; // expected-warning{{null}} +} + +size_t strnlen_null() { + return strnlen(0, 3); // expected-warning{{Null pointer argument in call to byte string function}} +} + +size_t strnlen_fn() { + return strnlen((char*)&strlen_fn, 3); // expected-warning{{Argument to byte string function is the address of the function 'strlen_fn', which is not a null-terminated string}} +} + +size_t strnlen_nonloc() { +label: + return strnlen((char*)&&label, 3); // expected-warning{{Argument to byte string function is the address of the label 'label', which is not a null-terminated string}} +} + +void strnlen_subregion() { + struct two_stringsn { char a[2], b[2] }; + extern void use_two_stringsn(struct two_stringsn *); + + struct two_stringsn z; + use_two_stringsn(&z); + + size_t a = strnlen(z.a, 10); + z.b[0] = 5; + size_t b = strnlen(z.a, 10); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + use_two_stringsn(&z); + + size_t c = strnlen(z.a, 10); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} + +extern void use_stringn(char *); +void strnlen_argument(char *x) { + size_t a = strnlen(x, 10); + size_t b = strnlen(x, 10); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + use_stringn(x); + + size_t c = strnlen(x, 10); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} + +extern char global_strn[]; +void strnlen_global() { + size_t a = strnlen(global_strn, 10); + size_t b = strnlen(global_strn, 10); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + // Call a function with unknown effects, which should invalidate globals. + use_stringn(0); + + size_t c = strnlen(global_str, 10); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} + +void strnlen_indirect(char *x) { + size_t a = strnlen(x, 10); + char *p = x; + char **p2 = &p; + size_t b = strnlen(x, 10); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + extern void use_stringn_ptr(char*const*); + use_stringn_ptr(p2); + + size_t c = strnlen(x, 10); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} + +void strnlen_liveness(const char *x) { + if (strnlen(x, 10) < 5) + return; + if (strnlen(x, 10) < 5) + (void)*(char*)0; // no-warning +} + +//===----------------------------------------------------------------------=== // strcpy() //===----------------------------------------------------------------------=== |