Adding Static Analyzer checker for mempcpy().

Models mempcpy() so that if length is NULL the destination pointer is returned. Otherwise, the source and destination are confirmed not to be NULL and not overlapping. Finally the copy is validated to not cause a buffer overrun and the return value is bound to the address of the byte after the last byte copied.

llvm-svn: 128677
This commit is contained in:
Lenny Maiorani 2011-03-31 21:36:53 +00:00
parent 5291054ef1
commit 79d74141b1
2 changed files with 174 additions and 9 deletions

View File

@ -49,11 +49,14 @@ public:
const CallExpr *) const;
void evalMemcpy(CheckerContext &C, const CallExpr *CE) const;
void evalMempcpy(CheckerContext &C, const CallExpr *CE) const;
void evalMemmove(CheckerContext &C, const CallExpr *CE) const;
void evalBcopy(CheckerContext &C, const CallExpr *CE) const;
void evalCopyCommon(CheckerContext &C, const GRState *state,
void evalCopyCommon(CheckerContext &C, const CallExpr *CE,
const GRState *state,
const Expr *Size, const Expr *Source, const Expr *Dest,
bool Restricted = false) const;
bool Restricted = false,
bool IsMempcpy = false) const;
void evalMemcmp(CheckerContext &C, const CallExpr *CE) const;
@ -655,9 +658,12 @@ bool CStringChecker::SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx,
// evaluation of individual function calls.
//===----------------------------------------------------------------------===//
void CStringChecker::evalCopyCommon(CheckerContext &C, const GRState *state,
void CStringChecker::evalCopyCommon(CheckerContext &C,
const CallExpr *CE,
const GRState *state,
const Expr *Size, const Expr *Dest,
const Expr *Source, bool Restricted) const {
const Expr *Source, bool Restricted,
bool IsMempcpy) const {
// See if the size argument is zero.
SVal sizeVal = state->getSVal(Size);
QualType sizeTy = Size->getType();
@ -665,12 +671,39 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const GRState *state,
const GRState *stateZeroSize, *stateNonZeroSize;
llvm::tie(stateZeroSize, stateNonZeroSize) = assumeZero(C, state, sizeVal, sizeTy);
// If the size is zero, there won't be any actual memory access.
if (stateZeroSize)
// Get the value of the Dest.
SVal destVal = state->getSVal(Dest);
// If the size is zero, there won't be any actual memory access, so
// just bind the return value to the destination buffer and return.
if (stateZeroSize) {
C.addTransition(stateZeroSize);
if (IsMempcpy)
state->BindExpr(CE, destVal);
else
state->BindExpr(CE, sizeVal);
return;
}
// If the size can be nonzero, we have to check the other arguments.
if (stateNonZeroSize) {
// Ensure the destination is not null. If it is NULL there will be a
// NULL pointer dereference.
state = checkNonNull(C, state, Dest, destVal);
if (!state)
return;
// Get the value of the Src.
SVal srcVal = state->getSVal(Source);
// Ensure the source is not null. If it is NULL there will be a
// NULL pointer dereference.
state = checkNonNull(C, state, Source, srcVal);
if (!state)
return;
// Ensure the buffers do not overlap.
state = stateNonZeroSize;
state = CheckBufferAccess(C, state, Size, Dest, Source,
/* FirstIsDst = */ true);
@ -678,6 +711,26 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const GRState *state,
state = CheckOverlap(C, state, Size, Dest, Source);
if (state) {
// If this is mempcpy, get the byte after the last byte copied and
// bind the expr.
if (IsMempcpy) {
loc::MemRegionVal *destRegVal = dyn_cast<loc::MemRegionVal>(&destVal);
// Get the length to copy.
SVal lenVal = state->getSVal(Size);
NonLoc *lenValNonLoc = dyn_cast<NonLoc>(&lenVal);
// Get the byte after the last byte copied.
SVal lastElement = C.getSValBuilder().evalBinOpLN(state, BO_Add,
*destRegVal,
*lenValNonLoc,
Dest->getType());
// The byte after the last byte copied is the return value.
state = state->BindExpr(CE, lastElement);
}
// Invalidate the destination.
// FIXME: Even if we can't perfectly model the copy, we should see if we
// can use LazyCompoundVals to copy the source values into the destination.
@ -696,7 +749,16 @@ void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const {
const Expr *Dest = CE->getArg(0);
const GRState *state = C.getState();
state = state->BindExpr(CE, state->getSVal(Dest));
evalCopyCommon(C, state, CE->getArg(2), Dest, CE->getArg(1), true);
evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true);
}
void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
// void *mempcpy(void *restrict dst, const void *restrict src, size_t n);
// The return value is a pointer to the byte following the last written byte.
const Expr *Dest = CE->getArg(0);
const GRState *state = C.getState();
evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true, true);
}
void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
@ -705,12 +767,13 @@ void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
const Expr *Dest = CE->getArg(0);
const GRState *state = C.getState();
state = state->BindExpr(CE, state->getSVal(Dest));
evalCopyCommon(C, state, CE->getArg(2), Dest, CE->getArg(1));
evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1));
}
void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const {
// void bcopy(const void *src, void *dst, size_t n);
evalCopyCommon(C, C.getState(), CE->getArg(2), CE->getArg(1), CE->getArg(0));
evalCopyCommon(C, CE, C.getState(),
CE->getArg(2), CE->getArg(1), CE->getArg(0));
}
void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
@ -982,6 +1045,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
.Cases("memcpy", "__memcpy_chk", &CStringChecker::evalMemcpy)
.Case("mempcpy", &CStringChecker::evalMempcpy)
.Cases("memcmp", "bcmp", &CStringChecker::evalMemcmp)
.Cases("memmove", "__memmove_chk", &CStringChecker::evalMemmove)
.Cases("strcpy", "__strcpy_chk", &CStringChecker::evalStrcpy)

View File

@ -129,9 +129,110 @@ void memcpy11() {
void memcpy12() {
char a[4] = {0};
memcpy(0, a, 0); // no-warning
}
void memcpy13() {
char a[4] = {0};
memcpy(a, 0, 0); // no-warning
}
//===----------------------------------------------------------------------===
// mempcpy()
//===----------------------------------------------------------------------===
#define mempcpy BUILTIN(mempcpy)
void *mempcpy(void *restrict s1, const void *restrict s2, size_t n);
void mempcpy0 () {
char src[] = {1, 2, 3, 4};
char dst[5] = {0};
mempcpy(dst, src, 4); // no-warning
if (mempcpy(dst, src, 4) != &dst[4]) {
(void)*(char*)0; // no-warning
}
if (dst[0] != 0)
(void)*(char*)0; // expected-warning{{null}}
}
void mempcpy1 () {
char src[] = {1, 2, 3, 4};
char dst[10];
mempcpy(dst, src, 5); // expected-warning{{Byte string function accesses out-of-bound array element}}
}
void mempcpy2 () {
char src[] = {1, 2, 3, 4};
char dst[1];
mempcpy(dst, src, 4); // expected-warning{{Byte string function overflows destination buffer}}
}
void mempcpy3 () {
char src[] = {1, 2, 3, 4};
char dst[3];
mempcpy(dst+1, src+2, 2); // no-warning
}
void mempcpy4 () {
char src[] = {1, 2, 3, 4};
char dst[10];
mempcpy(dst+2, src+2, 3); // expected-warning{{Byte string function accesses out-of-bound array element}}
}
void mempcpy5() {
char src[] = {1, 2, 3, 4};
char dst[3];
mempcpy(dst+2, src+2, 2); // expected-warning{{Byte string function overflows destination buffer}}
}
void mempcpy6() {
int a[4] = {0};
mempcpy(a, a, 8); // expected-warning{{overlapping}}
}
void mempcpy7() {
int a[4] = {0};
mempcpy(a+2, a+1, 8); // expected-warning{{overlapping}}
}
void mempcpy8() {
int a[4] = {0};
mempcpy(a+1, a+2, 8); // expected-warning{{overlapping}}
}
void mempcpy9() {
int a[4] = {0};
mempcpy(a+2, a+1, 4); // no-warning
mempcpy(a+1, a+2, 4); // no-warning
}
void mempcpy10() {
char a[4] = {0};
mempcpy(0, a, 4); // expected-warning{{Null pointer argument in call to byte string function}}
}
void mempcpy11() {
char a[4] = {0};
mempcpy(a, 0, 4); // expected-warning{{Null pointer argument in call to byte string function}}
}
void mempcpy12() {
char a[4] = {0};
mempcpy(0, a, 0); // no-warning
}
void mempcpy13() {
char a[4] = {0};
mempcpy(a, 0, 0); // no-warning
}
//===----------------------------------------------------------------------===
// memmove()
//===----------------------------------------------------------------------===