forked from OSchip/llvm-project
[analyzer]Malloc: refactor and report use after free by memory
allocating functions. llvm-svn: 157037
This commit is contained in:
parent
aa739093df
commit
46d01605ee
|
@ -146,6 +146,8 @@ private:
|
||||||
/// Check if this is one of the functions which can allocate/reallocate memory
|
/// Check if this is one of the functions which can allocate/reallocate memory
|
||||||
/// pointed to by one of its arguments.
|
/// pointed to by one of its arguments.
|
||||||
bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const;
|
bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const;
|
||||||
|
bool isFreeFunction(const FunctionDecl *FD, ASTContext &C) const;
|
||||||
|
bool isAllocationFunction(const FunctionDecl *FD, ASTContext &C) const;
|
||||||
|
|
||||||
static ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
|
static ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
|
||||||
const CallExpr *CE,
|
const CallExpr *CE,
|
||||||
|
@ -177,6 +179,9 @@ private:
|
||||||
bool FreesMemOnFailure) const;
|
bool FreesMemOnFailure) const;
|
||||||
static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE);
|
static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE);
|
||||||
|
|
||||||
|
///\brief Check if the memory associated with this symbol was released.
|
||||||
|
bool isReleased(SymbolRef Sym, CheckerContext &C) const;
|
||||||
|
|
||||||
bool checkEscape(SymbolRef Sym, const Stmt *S, CheckerContext &C) const;
|
bool checkEscape(SymbolRef Sym, const Stmt *S, CheckerContext &C) const;
|
||||||
bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
|
bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
|
||||||
const Stmt *S = 0) const;
|
const Stmt *S = 0) const;
|
||||||
|
@ -353,25 +358,62 @@ void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {
|
bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {
|
||||||
|
if (isFreeFunction(FD, C))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (isAllocationFunction(FD, C))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MallocChecker::isAllocationFunction(const FunctionDecl *FD,
|
||||||
|
ASTContext &C) const {
|
||||||
if (!FD)
|
if (!FD)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
IdentifierInfo *FunI = FD->getIdentifier();
|
IdentifierInfo *FunI = FD->getIdentifier();
|
||||||
if (!FunI)
|
if (!FunI)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
initIdentifierInfo(C);
|
initIdentifierInfo(C);
|
||||||
|
|
||||||
if (FunI == II_malloc || FunI == II_free || FunI == II_realloc ||
|
if (FunI == II_malloc || FunI == II_realloc ||
|
||||||
FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc ||
|
FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc ||
|
||||||
FunI == II_strdup || FunI == II_strndup)
|
FunI == II_strdup || FunI == II_strndup)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (Filter.CMallocOptimistic && FD->hasAttrs() &&
|
if (Filter.CMallocOptimistic && FD->hasAttrs())
|
||||||
FD->specific_attr_begin<OwnershipAttr>() !=
|
for (specific_attr_iterator<OwnershipAttr>
|
||||||
FD->specific_attr_end<OwnershipAttr>())
|
i = FD->specific_attr_begin<OwnershipAttr>(),
|
||||||
|
e = FD->specific_attr_end<OwnershipAttr>();
|
||||||
|
i != e; ++i)
|
||||||
|
if ((*i)->getOwnKind() == OwnershipAttr::Returns)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MallocChecker::isFreeFunction(const FunctionDecl *FD, ASTContext &C) const {
|
||||||
|
if (!FD)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
IdentifierInfo *FunI = FD->getIdentifier();
|
||||||
|
if (!FunI)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
initIdentifierInfo(C);
|
||||||
|
|
||||||
|
if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (Filter.CMallocOptimistic && FD->hasAttrs())
|
||||||
|
for (specific_attr_iterator<OwnershipAttr>
|
||||||
|
i = FD->specific_attr_begin<OwnershipAttr>(),
|
||||||
|
e = FD->specific_attr_end<OwnershipAttr>();
|
||||||
|
i != e; ++i)
|
||||||
|
if ((*i)->getOwnKind() == OwnershipAttr::Takes ||
|
||||||
|
(*i)->getOwnKind() == OwnershipAttr::Holds)
|
||||||
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -995,7 +1037,8 @@ bool MallocChecker::checkEscape(SymbolRef Sym, const Stmt *S,
|
||||||
}
|
}
|
||||||
|
|
||||||
void MallocChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
|
void MallocChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
|
||||||
if (isMemFunction(C.getCalleeDecl(CE), C.getASTContext()))
|
// We will check for double free in the post visit.
|
||||||
|
if (isFreeFunction(C.getCalleeDecl(CE), C.getASTContext()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Check use after free, when a freed pointer is passed to a call.
|
// Check use after free, when a freed pointer is passed to a call.
|
||||||
|
@ -1082,11 +1125,15 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE,
|
||||||
C.addTransition(state);
|
C.addTransition(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
|
bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const {
|
||||||
const Stmt *S) const {
|
|
||||||
assert(Sym);
|
assert(Sym);
|
||||||
const RefState *RS = C.getState()->get<RegionState>(Sym);
|
const RefState *RS = C.getState()->get<RegionState>(Sym);
|
||||||
if (RS && RS->isReleased()) {
|
return (RS && RS->isReleased());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
|
||||||
|
const Stmt *S) const {
|
||||||
|
if (isReleased(Sym, C)) {
|
||||||
if (ExplodedNode *N = C.generateSink()) {
|
if (ExplodedNode *N = C.generateSink()) {
|
||||||
if (!BT_UseFree)
|
if (!BT_UseFree)
|
||||||
BT_UseFree.reset(new BugType("Use-after-free", "Memory Error"));
|
BT_UseFree.reset(new BugType("Use-after-free", "Memory Error"));
|
||||||
|
@ -1109,7 +1156,7 @@ void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
SymbolRef Sym = l.getLocSymbolInBase();
|
SymbolRef Sym = l.getLocSymbolInBase();
|
||||||
if (Sym)
|
if (Sym)
|
||||||
checkUseAfterFree(Sym, C);
|
checkUseAfterFree(Sym, C, S);
|
||||||
}
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
|
@ -8,6 +8,8 @@ void free(void *);
|
||||||
void *realloc(void *ptr, size_t size);
|
void *realloc(void *ptr, size_t size);
|
||||||
void *reallocf(void *ptr, size_t size);
|
void *reallocf(void *ptr, size_t size);
|
||||||
void *calloc(size_t nmemb, size_t size);
|
void *calloc(size_t nmemb, size_t size);
|
||||||
|
char *strdup(const char *s);
|
||||||
|
char *strndup(const char *s, size_t n);
|
||||||
|
|
||||||
void myfoo(int *p);
|
void myfoo(int *p);
|
||||||
void myfooint(int p);
|
void myfooint(int p);
|
||||||
|
@ -243,6 +245,12 @@ void f7() {
|
||||||
x[0] = 'a'; // expected-warning{{Use of memory after it is freed}}
|
x[0] = 'a'; // expected-warning{{Use of memory after it is freed}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void f8() {
|
||||||
|
char *x = (char*) malloc(4);
|
||||||
|
free(x);
|
||||||
|
char *y = strndup(x, 4); // expected-warning{{Use of memory after it is freed}}
|
||||||
|
}
|
||||||
|
|
||||||
void f7_realloc() {
|
void f7_realloc() {
|
||||||
char *x = (char*) malloc(4);
|
char *x = (char*) malloc(4);
|
||||||
realloc(x,0);
|
realloc(x,0);
|
||||||
|
@ -653,10 +661,6 @@ int *specialMallocWithStruct() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test various allocation/deallocation functions.
|
// Test various allocation/deallocation functions.
|
||||||
|
|
||||||
char *strdup(const char *s);
|
|
||||||
char *strndup(const char *s, size_t n);
|
|
||||||
|
|
||||||
void testStrdup(const char *s, unsigned validIndex) {
|
void testStrdup(const char *s, unsigned validIndex) {
|
||||||
char *s2 = strdup(s);
|
char *s2 = strdup(s);
|
||||||
s2[validIndex + 1] = 'b';// expected-warning {{Memory is never released; potential leak}}
|
s2[validIndex + 1] = 'b';// expected-warning {{Memory is never released; potential leak}}
|
||||||
|
|
Loading…
Reference in New Issue