[cfe-commits] r107761 - in /cfe/trunk: lib/Checker/CStringChecker.cpp test/Analysis/bstring.c
Jordy Rose
jediknil at belkadan.com
Wed Jul 7 01:15:01 PDT 2010
Author: jrose
Date: Wed Jul 7 03:15:01 2010
New Revision: 107761
URL: http://llvm.org/viewvc/llvm-project?rev=107761&view=rev
Log:
Add memcmp() and bcmp() to CStringChecker. These check for valid access to the buffer arguments and have a special-case for when the buffer arguments are known to be the same address, or when the size is zero.
Modified:
cfe/trunk/lib/Checker/CStringChecker.cpp
cfe/trunk/test/Analysis/bstring.c
Modified: cfe/trunk/lib/Checker/CStringChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Checker/CStringChecker.cpp?rev=107761&r1=107760&r2=107761&view=diff
==============================================================================
--- cfe/trunk/lib/Checker/CStringChecker.cpp (original)
+++ cfe/trunk/lib/Checker/CStringChecker.cpp Wed Jul 7 03:15:01 2010
@@ -35,6 +35,7 @@
const GRState *EvalMemcpy(CheckerContext &C, const CallExpr *CE);
const GRState *EvalMemmove(CheckerContext &C, const CallExpr *CE);
+ const GRState *EvalMemcmp(CheckerContext &C, const CallExpr *CE);
const GRState *EvalBcopy(CheckerContext &C, const CallExpr *CE);
// Utility methods
@@ -388,6 +389,70 @@
}
const GRState *
+CStringChecker::EvalMemcmp(CheckerContext &C, const CallExpr *CE) {
+ // int memcmp(const void *s1, const void *s2, size_t n);
+ const Expr *Left = CE->getArg(0);
+ const Expr *Right = CE->getArg(1);
+ const Expr *Size = CE->getArg(2);
+
+ const GRState *state = C.getState();
+ ValueManager &ValMgr = C.getValueManager();
+ SValuator &SV = ValMgr.getSValuator();
+ const GRState *stateTrue, *stateFalse;
+
+ // If we know the size argument is 0, we know the result is 0, and we don't
+ // have to check either of the buffers. (Another checker will have already
+ // made sure the size isn't undefined, so we can cast it safely.)
+ DefinedOrUnknownSVal SizeV = cast<DefinedOrUnknownSVal>(state->getSVal(Size));
+ DefinedOrUnknownSVal Zero = ValMgr.makeZeroVal(Size->getType());
+
+ DefinedOrUnknownSVal SizeIsZero = SV.EvalEQ(state, SizeV, Zero);
+ llvm::tie(stateTrue, stateFalse) = state->Assume(SizeIsZero);
+
+ // FIXME: This should really cause a bifurcation of the state, but that would
+ // require changing the contract to allow the various Eval* methods to add
+ // transitions themselves. Currently that isn't the case because some of these
+ // functions are "basically" like another function, but with one or two
+ // additional restrictions (like memcpy and memmove).
+
+ if (stateTrue && !stateFalse)
+ return stateTrue->BindExpr(CE, ValMgr.makeZeroVal(CE->getType()));
+
+ // At this point, we still don't know that the size is nonzero, only that it
+ // might be.
+
+ // If we know the two buffers are the same, we know the result is 0.
+ // First, get the two buffers' addresses. Another checker will have already
+ // made sure they're not undefined.
+ DefinedOrUnknownSVal LBuf = cast<DefinedOrUnknownSVal>(state->getSVal(Left));
+ DefinedOrUnknownSVal RBuf = cast<DefinedOrUnknownSVal>(state->getSVal(Right));
+
+ // See if they are the same.
+ DefinedOrUnknownSVal SameBuf = SV.EvalEQ(state, LBuf, RBuf);
+ llvm::tie(stateTrue, stateFalse) = state->Assume(SameBuf);
+
+ // FIXME: This should also bifurcate the state (as above).
+
+ // If the two arguments are known to be the same buffer, we know the result is
+ // zero, and we only need to check one size.
+ if (stateTrue && !stateFalse) {
+ state = CheckBufferAccess(C, stateTrue, Size, Left);
+ return state->BindExpr(CE, ValMgr.makeZeroVal(CE->getType()));
+ }
+
+ // At this point, we don't know if the arguments are the same or not -- we
+ // only know that they *might* be different. We can't make any assumptions.
+
+ // The return value is the comparison result, which we don't know.
+ unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
+ SVal RetVal = ValMgr.getConjuredSymbolVal(NULL, CE, CE->getType(), Count);
+ state = state->BindExpr(CE, RetVal);
+
+ // Check that the accesses will stay in bounds.
+ return CheckBufferAccess(C, state, Size, Left, Right);
+}
+
+const GRState *
CStringChecker::EvalBcopy(CheckerContext &C, const CallExpr *CE) {
// void bcopy(const void *src, void *dst, size_t n);
return CheckBufferAccess(C, C.getState(),
@@ -411,6 +476,7 @@
FnCheck EvalFunction = llvm::StringSwitch<FnCheck>(Name)
.Cases("memcpy", "__memcpy_chk", &CStringChecker::EvalMemcpy)
+ .Cases("memcmp", "bcmp", &CStringChecker::EvalMemcmp)
.Cases("memmove", "__memmove_chk", &CStringChecker::EvalMemmove)
.Case("bcopy", &CStringChecker::EvalBcopy)
.Default(NULL);
Modified: cfe/trunk/test/Analysis/bstring.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/bstring.c?rev=107761&r1=107760&r2=107761&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/bstring.c (original)
+++ cfe/trunk/test/Analysis/bstring.c Wed Jul 7 03:15:01 2010
@@ -8,8 +8,9 @@
//===----------------------------------------------------------------------===
// Some functions are so similar to each other that they follow the same code
-// path, such as memcpy and __memcpy_chk. If VARIANT is defined, make sure to
-// use the variants instead to make sure they are still checked by the analyzer.
+// path, such as memcpy and __memcpy_chk, or memcmp and bcmp. If VARIANT is
+// defined, make sure to use the variants instead to make sure they are still
+// checked by the analyzer.
// Some functions are implemented as builtins. These should be #defined as
// BUILTIN(f), which will prepend "__builtin_" if USE_BUILTINS is defined.
@@ -173,6 +174,71 @@
}
//===----------------------------------------------------------------------===
+// memcmp()
+//===----------------------------------------------------------------------===
+
+#ifdef VARIANT
+
+#define bcmp BUILTIN(bcmp)
+// __builtin_bcmp is not defined with const in Builtins.def.
+int bcmp(/*const*/ void *s1, /*const*/ void *s2, size_t n);
+#define memcmp bcmp
+
+#else /* VARIANT */
+
+#define memcmp BUILTIN(memcmp)
+int memcmp(const void *s1, const void *s2, size_t n);
+
+#endif /* VARIANT */
+
+
+void memcmp0 () {
+ char a[] = {1, 2, 3, 4};
+ char b[4] = { 0 };
+
+ memcmp(a, b, 4); // no-warning
+}
+
+void memcmp1 () {
+ char a[] = {1, 2, 3, 4};
+ char b[10] = { 0 };
+
+ memcmp(a, b, 5); // expected-warning{{out-of-bound}}
+}
+
+void memcmp2 () {
+ char a[] = {1, 2, 3, 4};
+ char b[1] = { 0 };
+
+ memcmp(a, b, 4); // expected-warning{{out-of-bound}}
+}
+
+void memcmp3 () {
+ char a[] = {1, 2, 3, 4};
+
+ if (memcmp(a, a, 4))
+ (void)*(char*)0; // no-warning
+}
+
+void memcmp4 (char *input) {
+ char a[] = {1, 2, 3, 4};
+
+ if (memcmp(a, input, 4))
+ (void)*(char*)0; // expected-warning{{null}}
+}
+
+void memcmp5 (char *input) {
+ char a[] = {1, 2, 3, 4};
+
+ if (memcmp(a, 0, 0)) // no-warning
+ (void)*(char*)0; // no-warning
+ if (memcmp(0, a, 0)) // no-warning
+ (void)*(char*)0; // no-warning
+ if (memcmp(a, input, 0)) // no-warning
+ (void)*(char*)0; // no-warning
+}
+
+//===----------------------------------------------------------------------===
// bcopy()
//===----------------------------------------------------------------------===
More information about the cfe-commits
mailing list