[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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@180069 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Jordan Rose 2013-04-22 23:18:42 +00:00
Родитель 5d7a96ce69
Коммит af22621352
2 изменённых файлов: 113 добавлений и 0 удалений

Просмотреть файл

@ -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(CheckerContext &C, const CallExpr *CE,
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 CallExpr *CE, CheckerContext &C) const {
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 CallExpr *CE, CheckerContext &C) const {
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"))

Просмотреть файл

@ -1027,6 +1027,57 @@ void strncasecmp_embedded_null () {
clang_analyzer_eval(strncasecmp("ab\0zz", "ab\0yy", 4) == 0); // expected-warning{{TRUE}}
}
//===----------------------------------------------------------------------===
// 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
//===----------------------------------------------------------------------===