strcat() and strncat() model additions to CStringChecker.

Validates inputs are not NULL, checks for overlapping strings, concatenates the strings checking for buffer overflow, sets the length of the destination string to the sum of the s1 length and the s2 length, binds the return value to the s1 value.

llvm-svn: 129215
This commit is contained in:
Lenny Maiorani 2011-04-09 15:12:58 +00:00
parent 26e7768f38
commit 467dbd5f13
2 changed files with 222 additions and 9 deletions

View File

@ -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)

View File

@ -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
}