diff options
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 73 | ||||
-rw-r--r-- | test/Analysis/string.c | 158 |
2 files changed, 222 insertions, 9 deletions
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index ccbc9343f3..c98bf5c598 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -69,7 +69,10 @@ public: void evalStrncpy(CheckerContext &C, const CallExpr *CE) const; void evalStpcpy(CheckerContext &C, const CallExpr *CE) const; void evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool returnEnd, - bool isStrncpy) const; + bool isBounded, bool isAppending) const; + + void evalStrcat(CheckerContext &C, const CallExpr *CE) const; + void evalStrncat(CheckerContext &C, const CallExpr *CE) const; // Utility methods std::pair<const GRState*, const GRState*> @@ -912,24 +915,50 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const { // char *strcpy(char *restrict dst, const char *restrict src); - evalStrcpyCommon(C, CE, /* returnEnd = */ false, /* isStrncpy = */ false); + evalStrcpyCommon(C, CE, + /* returnEnd = */ false, + /* isBounded = */ false, + /* isAppending = */ false); } void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const { // char *strcpy(char *restrict dst, const char *restrict src); - evalStrcpyCommon(C, CE, /* returnEnd = */ false, /* isStrncpy = */ true); + evalStrcpyCommon(C, CE, + /* returnEnd = */ false, + /* isBounded = */ true, + /* isAppending = */ false); } void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const { // char *stpcpy(char *restrict dst, const char *restrict src); - evalStrcpyCommon(C, CE, /* returnEnd = */ true, /* isStrncpy = */ false); + evalStrcpyCommon(C, CE, + /* returnEnd = */ true, + /* isBounded = */ false, + /* isAppending = */ false); +} + +void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const { + //char *strcat(char *restrict s1, const char *restrict s2); + evalStrcpyCommon(C, CE, + /* returnEnd = */ false, + /* isBounded = */ false, + /* isAppending = */ true); +} + +void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const { + //char *strncat(char *restrict s1, const char *restrict s2, size_t n); + evalStrcpyCommon(C, CE, + /* returnEnd = */ false, + /* isBounded = */ true, + /* isAppending = */ true); } void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, - bool returnEnd, bool isStrncpy) const { + bool returnEnd, bool isBounded, + bool isAppending) const { const GRState *state = C.getState(); - // Check that the destination is non-null + // Check that the destination is non-null. const Expr *Dst = CE->getArg(0); SVal DstVal = state->getSVal(Dst); @@ -951,8 +980,9 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, if (strLength.isUndef()) return; - if (isStrncpy) { - // Get the max number of characters to copy + // If the function is strncpy, strncat, etc... it is bounded. + if (isBounded) { + // Get the max number of characters to copy. const Expr *lenExpr = CE->getArg(2); SVal lenVal = state->getSVal(lenExpr); @@ -962,7 +992,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, QualType cmpTy = C.getSValBuilder().getContext().IntTy; const GRState *stateTrue, *stateFalse; - // Check if the max number to copy is less than the length of the src + // Check if the max number to copy is less than the length of the src. llvm::tie(stateTrue, stateFalse) = state->assume(cast<DefinedOrUnknownSVal> (C.getSValBuilder().evalBinOpNN(state, BO_GT, @@ -976,6 +1006,29 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, } } + // If this is an appending function (strcat, strncat...) then set the + // string length to strlen(src) + strlen(dst) since the buffer will + // ultimately contain both. + if (isAppending) { + // Get the string length of the destination, or give up. + SVal dstStrLength = getCStringLength(C, state, Dst, DstVal); + if (dstStrLength.isUndef()) + return; + + NonLoc *srcStrLengthNL = dyn_cast<NonLoc>(&strLength); + NonLoc *dstStrLengthNL = dyn_cast<NonLoc>(&dstStrLength); + + // If src or dst cast to NonLoc is NULL, give up. + if ((!srcStrLengthNL) || (!dstStrLengthNL)) + return; + + QualType addTy = C.getSValBuilder().getContext().getSizeType(); + + strLength = C.getSValBuilder().evalBinOpNN(state, BO_Add, + *srcStrLengthNL, *dstStrLengthNL, + addTy); + } + SVal Result = (returnEnd ? UnknownVal() : DstVal); // If the destination is a MemRegion, try to check for a buffer overflow and @@ -1051,6 +1104,8 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { .Cases("strcpy", "__strcpy_chk", &CStringChecker::evalStrcpy) .Cases("strncpy", "__strncpy_chk", &CStringChecker::evalStrncpy) .Cases("stpcpy", "__stpcpy_chk", &CStringChecker::evalStpcpy) + .Cases("strcat", "__strcat_chk", &CStringChecker::evalStrcat) + .Cases("strncat", "__strncat_chk", &CStringChecker::evalStrncat) .Case("strlen", &CStringChecker::evalstrLength) .Case("strnlen", &CStringChecker::evalstrnLength) .Case("bcopy", &CStringChecker::evalBcopy) diff --git a/test/Analysis/string.c b/test/Analysis/string.c index 430145fc4c..d9e0cf8c4e 100644 --- a/test/Analysis/string.c +++ b/test/Analysis/string.c @@ -438,3 +438,161 @@ void stpcpy_no_overflow(char *y) { if (strlen(y) == 3) stpcpy(x, y); // no-warning } + +//===----------------------------------------------------------------------=== +// strcat() +//===----------------------------------------------------------------------=== + +#ifdef VARIANT + +#define __strcat_chk BUILTIN(__strcat_chk) +char *__strcat_chk(char *restrict s1, const char *restrict s2, size_t destlen); + +#define strcat(a,b) __strcat_chk(a,b,(size_t)-1) + +#else /* VARIANT */ + +#define strcat BUILTIN(strcat) +char *strcat(char *restrict s1, const char *restrict s2); + +#endif /* VARIANT */ + + +void strcat_null_dst(char *x) { + strcat(NULL, x); // expected-warning{{Null pointer argument in call to byte string function}} +} + +void strcat_null_src(char *x) { + strcat(x, NULL); // expected-warning{{Null pointer argument in call to byte string function}} +} + +void strcat_fn(char *x) { + strcat(x, (char*)&strcat_fn); // expected-warning{{Argument to byte string function is the address of the function 'strcat_fn', which is not a null-terminated string}} +} + +void strcat_effects(char *y) { + char x[8] = "123"; + size_t orig_len = strlen(x); + char a = x[0]; + + if (strlen(y) != 4) + return; + + if (strcat(x, y) != x) + (void)*(char*)0; // no-warning + + if ((int)strlen(x) != (orig_len + strlen(y))) + (void)*(char*)0; // no-warning + + if (a != x[0]) + (void)*(char*)0; // expected-warning{{null}} +} + +void strcat_overflow_0(char *y) { + char x[4] = "12"; + if (strlen(y) == 4) + strcat(x, y); // expected-warning{{Byte string function overflows destination buffer}} +} + +void strcat_overflow_1(char *y) { + char x[4] = "12"; + if (strlen(y) == 3) + strcat(x, y); // expected-warning{{Byte string function overflows destination buffer}} +} + +void strcat_overflow_2(char *y) { + char x[4] = "12"; + if (strlen(y) == 2) + strcat(x, y); // expected-warning{{Byte string function overflows destination buffer}} +} + +void strcat_no_overflow(char *y) { + char x[5] = "12"; + if (strlen(y) == 2) + strcat(x, y); // no-warning +} + + +//===----------------------------------------------------------------------=== +// strncat() +//===----------------------------------------------------------------------=== + +#ifdef VARIANT + +#define __strncat_chk BUILTIN(__strncat_chk) +char *__strncat_chk(char *restrict s1, const char *restrict s2, size_t n, size_t destlen); + +#define strncat(a,b,c) __strncat_chk(a,b,c, (size_t)-1) + +#else /* VARIANT */ + +#define strncat BUILTIN(strncat) +char *strncat(char *restrict s1, const char *restrict s2, size_t n); + +#endif /* VARIANT */ + + +void strncat_null_dst(char *x) { + strncat(NULL, x, 4); // expected-warning{{Null pointer argument in call to byte string function}} +} + +void strncat_null_src(char *x) { + strncat(x, NULL, 4); // expected-warning{{Null pointer argument in call to byte string function}} +} + +void strncat_fn(char *x) { + strncat(x, (char*)&strncat_fn, 4); // expected-warning{{Argument to byte string function is the address of the function 'strncat_fn', which is not a null-terminated string}} +} + +void strncat_effects(char *y) { + char x[8] = "123"; + size_t orig_len = strlen(x); + char a = x[0]; + + if (strlen(y) != 4) + return; + + if (strncat(x, y, strlen(y)) != x) + (void)*(char*)0; // no-warning + + if (strlen(x) != orig_len + strlen(y)) + (void)*(char*)0; // no-warning + + if (a != x[0]) + (void)*(char*)0; // expected-warning{{null}} +} + +void strncat_overflow_0(char *y) { + char x[4] = "12"; + if (strlen(y) == 4) + strncat(x, y, strlen(y)); // expected-warning{{Byte string function overflows destination buffer}} +} + +void strncat_overflow_1(char *y) { + char x[4] = "12"; + if (strlen(y) == 3) + strncat(x, y, strlen(y)); // expected-warning{{Byte string function overflows destination buffer}} +} + +void strncat_overflow_2(char *y) { + char x[4] = "12"; + if (strlen(y) == 2) + strncat(x, y, strlen(y)); // expected-warning{{Byte string function overflows destination buffer}} +} + +void strncat_overflow_3(char *y) { + char x[4] = "12"; + if (strlen(y) == 4) + strncat(x, y, 2); // expected-warning{{Byte string function overflows destination buffer}} +} +void strncat_no_overflow_1(char *y) { + char x[5] = "12"; + if (strlen(y) == 2) + strncat(x, y, strlen(y)); // no-warning +} + +void strncat_no_overflow_2(char *y) { + char x[4] = "12"; + if (strlen(y) == 4) + strncat(x, y, 1); // no-warning +} |