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;
// 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();
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);
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.
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 {
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 {
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)
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()
//===----------------------------------------------------------------------===