r180069 - [analyzer] Model strsep(), particularly that it returns its input.
Jordan Rose
jordan_rose at apple.com
Mon Apr 22 16:18:42 PDT 2013
Author: jrose
Date: Mon Apr 22 18:18:42 2013
New Revision: 180069
URL: http://llvm.org/viewvc/llvm-project?rev=180069&view=rev
Log:
[analyzer] Model strsep(), particularly that it returns its input.
This handles the false positive leak warning in PR15374, and also serves
as a basic model for the strsep() function.
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=180069&r1=180068&r2=180069&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp Mon Apr 22 18:18:42 2013
@@ -114,6 +114,8 @@ public:
bool isBounded = false,
bool ignoreCase = false) const;
+ void evalStrsep(CheckerContext &C, const CallExpr *CE) const;
+
// Utility methods
std::pair<ProgramStateRef , ProgramStateRef >
static assumeZero(CheckerContext &C,
@@ -1752,6 +1754,63 @@ void CStringChecker::evalStrcmpCommon(Ch
C.addTransition(state);
}
+void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
+ //char *strsep(char **stringp, const char *delim);
+ if (CE->getNumArgs() < 2)
+ return;
+
+ // Sanity: does the search string parameter match the return type?
+ const Expr *SearchStrPtr = CE->getArg(0);
+ QualType CharPtrTy = SearchStrPtr->getType()->getPointeeType();
+ if (CharPtrTy.isNull() ||
+ CE->getType().getUnqualifiedType() != CharPtrTy.getUnqualifiedType())
+ return;
+
+ CurrentFunctionDescription = "strsep()";
+ ProgramStateRef State = C.getState();
+ const LocationContext *LCtx = C.getLocationContext();
+
+ // Check that the search string pointer is non-null (though it may point to
+ // a null string).
+ SVal SearchStrVal = State->getSVal(SearchStrPtr, LCtx);
+ State = checkNonNull(C, State, SearchStrPtr, SearchStrVal);
+ if (!State)
+ return;
+
+ // Check that the delimiter string is non-null.
+ const Expr *DelimStr = CE->getArg(1);
+ SVal DelimStrVal = State->getSVal(DelimStr, LCtx);
+ State = checkNonNull(C, State, DelimStr, DelimStrVal);
+ if (!State)
+ return;
+
+ SValBuilder &SVB = C.getSValBuilder();
+ SVal Result;
+ if (Optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) {
+ // Get the current value of the search string pointer, as a char*.
+ Result = State->getSVal(*SearchStrLoc, CharPtrTy);
+
+ // Invalidate the search string, representing the change of one delimiter
+ // character to NUL.
+ State = InvalidateBuffer(C, State, SearchStrPtr, Result);
+
+ // Overwrite the search string pointer. The new value is either an address
+ // further along in the same string, or NULL if there are no more tokens.
+ State = State->bindLoc(*SearchStrLoc,
+ SVB.conjureSymbolVal(getTag(), CE, LCtx, CharPtrTy,
+ C.blockCount()));
+ } else {
+ assert(SearchStrVal.isUnknown());
+ // Conjure a symbolic value. It's the best we can do.
+ Result = SVB.conjureSymbolVal(0, CE, LCtx, C.blockCount());
+ }
+
+ // Set the return value, and finish.
+ State = State->BindExpr(CE, LCtx, Result);
+ C.addTransition(State);
+}
+
+
//===----------------------------------------------------------------------===//
// The driver method, and other Checker callbacks.
//===----------------------------------------------------------------------===//
@@ -1762,6 +1821,7 @@ bool CStringChecker::evalCall(const Call
if (!FDecl)
return false;
+ // FIXME: Poorly-factored string switches are slow.
FnCheck evalFunction = 0;
if (C.isCLibraryFunction(FDecl, "memcpy"))
evalFunction = &CStringChecker::evalMemcpy;
@@ -1793,6 +1853,8 @@ bool CStringChecker::evalCall(const Call
evalFunction = &CStringChecker::evalStrcasecmp;
else if (C.isCLibraryFunction(FDecl, "strncasecmp"))
evalFunction = &CStringChecker::evalStrncasecmp;
+ else if (C.isCLibraryFunction(FDecl, "strsep"))
+ evalFunction = &CStringChecker::evalStrsep;
else if (C.isCLibraryFunction(FDecl, "bcopy"))
evalFunction = &CStringChecker::evalBcopy;
else if (C.isCLibraryFunction(FDecl, "bcmp"))
Modified: cfe/trunk/test/Analysis/string.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/string.c?rev=180069&r1=180068&r2=180069&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/string.c (original)
+++ cfe/trunk/test/Analysis/string.c Mon Apr 22 18:18:42 2013
@@ -1028,6 +1028,57 @@ void strncasecmp_embedded_null () {
}
//===----------------------------------------------------------------------===
+// strsep()
+//===----------------------------------------------------------------------===
+
+char *strsep(char **stringp, const char *delim);
+
+void strsep_null_delim(char *s) {
+ strsep(&s, NULL); // expected-warning{{Null pointer argument in call to strsep()}}
+}
+
+void strsep_null_search() {
+ strsep(NULL, ""); // expected-warning{{Null pointer argument in call to strsep()}}
+}
+
+void strsep_return_original_pointer(char *s) {
+ char *original = s;
+ char *result = strsep(&s, ""); // no-warning
+ clang_analyzer_eval(original == result); // expected-warning{{TRUE}}
+}
+
+void strsep_null_string() {
+ char *s = NULL;
+ char *result = strsep(&s, ""); // no-warning
+ clang_analyzer_eval(result == NULL); // expected-warning{{TRUE}}
+}
+
+void strsep_changes_input_pointer(char *s) {
+ char *original = s;
+ strsep(&s, ""); // no-warning
+ clang_analyzer_eval(s == original); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(s == NULL); // expected-warning{{UNKNOWN}}
+
+ // Check that the value is symbolic.
+ if (s == NULL) {
+ clang_analyzer_eval(s == NULL); // expected-warning{{TRUE}}
+ }
+}
+
+void strsep_changes_input_string() {
+ char str[] = "abc";
+
+ clang_analyzer_eval(str[1] == 'b'); // expected-warning{{TRUE}}
+
+ char *s = str;
+ strsep(&s, "b"); // no-warning
+
+ // The real strsep will change the first delimiter it finds into a NUL
+ // character. For now, we just model the invalidation.
+ clang_analyzer_eval(str[1] == 'b'); // expected-warning{{UNKNOWN}}
+}
+
+//===----------------------------------------------------------------------===
// FIXMEs
//===----------------------------------------------------------------------===
More information about the cfe-commits
mailing list