[cfe-commits] r126187 - in /cfe/trunk: lib/StaticAnalyzer/Checkers/CStringChecker.cpp test/Analysis/string.c
Ted Kremenek
kremenek at apple.com
Mon Feb 21 20:55:06 PST 2011
Author: kremenek
Date: Mon Feb 21 22:55:05 2011
New Revision: 126187
URL: http://llvm.org/viewvc/llvm-project?rev=126187&view=rev
Log:
Add CStringChecker support for strnlen. Patch by Lenny Maiorani!
Modified:
cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
cfe/trunk/test/Analysis/string.c
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp?rev=126187&r1=126186&r2=126187&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp Mon Feb 21 22:55:05 2011
@@ -54,6 +54,9 @@
void evalMemcmp(CheckerContext &C, const CallExpr *CE);
void evalstrLength(CheckerContext &C, const CallExpr *CE);
+ void evalstrnLength(CheckerContext &C, const CallExpr *CE);
+ void evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
+ bool IsStrnlen = false);
void evalStrcpy(CheckerContext &C, const CallExpr *CE);
void evalStpcpy(CheckerContext &C, const CallExpr *CE);
@@ -776,6 +779,16 @@
void CStringChecker::evalstrLength(CheckerContext &C, const CallExpr *CE) {
// size_t strlen(const char *s);
+ evalstrLengthCommon(C, CE, /* IsStrnlen = */ false);
+}
+
+void CStringChecker::evalstrnLength(CheckerContext &C, const CallExpr *CE) {
+ // size_t strnlen(const char *s, size_t maxlen);
+ evalstrLengthCommon(C, CE, /* IsStrnlen = */ true);
+}
+
+void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
+ bool IsStrnlen) {
const GRState *state = C.getState();
const Expr *Arg = CE->getArg(0);
SVal ArgVal = state->getSVal(Arg);
@@ -791,6 +804,32 @@
if (strLength.isUndef())
return;
+ // If the check is for strnlen() then bind the return value to no more than
+ // the maxlen value.
+ if (IsStrnlen) {
+ const Expr *maxlenExpr = CE->getArg(1);
+ SVal maxlenVal = state->getSVal(maxlenExpr);
+
+ NonLoc *strLengthNL = dyn_cast<NonLoc>(&strLength);
+ NonLoc *maxlenValNL = dyn_cast<NonLoc>(&maxlenVal);
+
+ QualType cmpTy = C.getSValBuilder().getContext().IntTy;
+ const GRState *stateTrue, *stateFalse;
+
+ // Check if the strLength is greater than or equal to the maxlen
+ llvm::tie(stateTrue, stateFalse) =
+ state->assume(cast<DefinedOrUnknownSVal>
+ (C.getSValBuilder().evalBinOpNN(state, BO_GE,
+ *strLengthNL, *maxlenValNL,
+ cmpTy)));
+
+ // If the strLength is greater than or equal to the maxlen, set strLength
+ // to maxlen
+ if (stateTrue && !stateFalse) {
+ strLength = maxlenVal;
+ }
+ }
+
// If getCStringLength couldn't figure out the length, conjure a return
// value, so it can be used in constraints, at least.
if (strLength.isUnknown()) {
@@ -914,6 +953,7 @@
.Cases("strcpy", "__strcpy_chk", &CStringChecker::evalStrcpy)
.Cases("stpcpy", "__stpcpy_chk", &CStringChecker::evalStpcpy)
.Case("strlen", &CStringChecker::evalstrLength)
+ .Case("strnlen", &CStringChecker::evalstrnLength)
.Case("bcopy", &CStringChecker::evalBcopy)
.Default(NULL);
Modified: cfe/trunk/test/Analysis/string.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/string.c?rev=126187&r1=126186&r2=126187&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/string.c (original)
+++ cfe/trunk/test/Analysis/string.c Mon Feb 21 22:55:05 2011
@@ -140,6 +140,138 @@
}
//===----------------------------------------------------------------------===
+// strnlen()
+//===----------------------------------------------------------------------===
+
+#define strnlen BUILTIN(strnlen)
+size_t strnlen(const char *s, size_t maxlen);
+
+void strnlen_constant0() {
+ if (strnlen("123", 10) != 3)
+ (void)*(char*)0; // no-warning
+}
+
+void strnlen_constant1() {
+ const char *a = "123";
+ if (strnlen(a, 10) != 3)
+ (void)*(char*)0; // no-warning
+}
+
+void strnlen_constant2(char x) {
+ char a[] = "123";
+ if (strnlen(a, 10) != 3)
+ (void)*(char*)0; // no-warning
+ a[0] = x;
+ if (strnlen(a, 10) != 3)
+ (void)*(char*)0; // expected-warning{{null}}
+}
+
+void strnlen_constant4() {
+ if (strnlen("123456", 3) != 3)
+ (void)*(char*)0; // no-warning
+}
+
+void strnlen_constant5() {
+ const char *a = "123456";
+ if (strnlen(a, 3) != 3)
+ (void)*(char*)0; // no-warning
+}
+
+void strnlen_constant6(char x) {
+ char a[] = "123456";
+ if (strnlen(a, 3) != 3)
+ (void)*(char*)0; // no-warning
+ a[0] = x;
+ if (strnlen(a, 3) != 3)
+ (void)*(char*)0; // expected-warning{{null}}
+}
+
+size_t strnlen_null() {
+ return strnlen(0, 3); // expected-warning{{Null pointer argument in call to byte string function}}
+}
+
+size_t strnlen_fn() {
+ return strnlen((char*)&strlen_fn, 3); // expected-warning{{Argument to byte string function is the address of the function 'strlen_fn', which is not a null-terminated string}}
+}
+
+size_t strnlen_nonloc() {
+label:
+ return strnlen((char*)&&label, 3); // expected-warning{{Argument to byte string function is the address of the label 'label', which is not a null-terminated string}}
+}
+
+void strnlen_subregion() {
+ struct two_stringsn { char a[2], b[2] };
+ extern void use_two_stringsn(struct two_stringsn *);
+
+ struct two_stringsn z;
+ use_two_stringsn(&z);
+
+ size_t a = strnlen(z.a, 10);
+ z.b[0] = 5;
+ size_t b = strnlen(z.a, 10);
+ if (a == 0 && b != 0)
+ (void)*(char*)0; // expected-warning{{never executed}}
+
+ use_two_stringsn(&z);
+
+ size_t c = strnlen(z.a, 10);
+ if (a == 0 && c != 0)
+ (void)*(char*)0; // expected-warning{{null}}
+}
+
+extern void use_stringn(char *);
+void strnlen_argument(char *x) {
+ size_t a = strnlen(x, 10);
+ size_t b = strnlen(x, 10);
+ if (a == 0 && b != 0)
+ (void)*(char*)0; // expected-warning{{never executed}}
+
+ use_stringn(x);
+
+ size_t c = strnlen(x, 10);
+ if (a == 0 && c != 0)
+ (void)*(char*)0; // expected-warning{{null}}
+}
+
+extern char global_strn[];
+void strnlen_global() {
+ size_t a = strnlen(global_strn, 10);
+ size_t b = strnlen(global_strn, 10);
+ if (a == 0 && b != 0)
+ (void)*(char*)0; // expected-warning{{never executed}}
+
+ // Call a function with unknown effects, which should invalidate globals.
+ use_stringn(0);
+
+ size_t c = strnlen(global_str, 10);
+ if (a == 0 && c != 0)
+ (void)*(char*)0; // expected-warning{{null}}
+}
+
+void strnlen_indirect(char *x) {
+ size_t a = strnlen(x, 10);
+ char *p = x;
+ char **p2 = &p;
+ size_t b = strnlen(x, 10);
+ if (a == 0 && b != 0)
+ (void)*(char*)0; // expected-warning{{never executed}}
+
+ extern void use_stringn_ptr(char*const*);
+ use_stringn_ptr(p2);
+
+ size_t c = strnlen(x, 10);
+ if (a == 0 && c != 0)
+ (void)*(char*)0; // expected-warning{{null}}
+}
+
+void strnlen_liveness(const char *x) {
+ if (strnlen(x, 10) < 5)
+ return;
+ if (strnlen(x, 10) < 5)
+ (void)*(char*)0; // no-warning
+}
+
+//===----------------------------------------------------------------------===
// strcpy()
//===----------------------------------------------------------------------===
More information about the cfe-commits
mailing list