Add FILE* leak check to StreamChecker. Patch by Lei Zhang.

llvm-svn: 109225
This commit is contained in:
Zhongxing Xu 2010-07-23 14:14:59 +00:00
parent 4ad7271798
commit 5e6ef6d957
2 changed files with 99 additions and 3 deletions

View File

@ -24,7 +24,7 @@ using namespace clang;
namespace {
struct StreamState {
enum Kind { Opened, Closed, OpenFailed } K;
enum Kind { Opened, Closed, OpenFailed, Escaped } K;
const Stmt *S;
StreamState(Kind k, const Stmt *s) : K(k), S(s) {}
@ -32,6 +32,7 @@ struct StreamState {
bool isOpened() const { return K == Opened; }
bool isClosed() const { return K == Closed; }
bool isOpenFailed() const { return K == OpenFailed; }
bool isEscaped() const { return K == Escaped; }
bool operator==(const StreamState &X) const {
return K == X.K && S == X.S;
@ -42,6 +43,9 @@ struct StreamState {
static StreamState getOpenFailed(const Stmt *s) {
return StreamState(OpenFailed, s);
}
static StreamState getEscaped(const Stmt *s) {
return StreamState(Escaped, s);
}
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddInteger(K);
@ -53,14 +57,15 @@ class StreamChecker : public CheckerVisitor<StreamChecker> {
IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread, *II_fwrite,
*II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos,
*II_clearerr, *II_feof, *II_ferror, *II_fileno;
BuiltinBug *BT_nullfp, *BT_illegalwhence, *BT_doubleclose;
BuiltinBug *BT_nullfp, *BT_illegalwhence, *BT_doubleclose, *BT_ResourceLeak;
public:
StreamChecker()
: II_fopen(0), II_tmpfile(0) ,II_fclose(0), II_fread(0), II_fwrite(0),
II_fseek(0), II_ftell(0), II_rewind(0), II_fgetpos(0), II_fsetpos(0),
II_clearerr(0), II_feof(0), II_ferror(0), II_fileno(0),
BT_nullfp(0), BT_illegalwhence(0), BT_doubleclose(0) {}
BT_nullfp(0), BT_illegalwhence(0), BT_doubleclose(0),
BT_ResourceLeak(0) {}
static void *getTag() {
static int x;
@ -68,6 +73,9 @@ public:
}
virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE);
void EvalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper);
void EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng);
void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S);
private:
void Fopen(CheckerContext &C, const CallExpr *CE);
@ -386,3 +394,70 @@ const GRState *StreamChecker::CheckDoubleClose(const CallExpr *CE,
// Close the File Descriptor.
return state->set<StreamState>(Sym, StreamState::getClosed(CE));
}
void StreamChecker::EvalDeadSymbols(CheckerContext &C,SymbolReaper &SymReaper) {
for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
E = SymReaper.dead_end(); I != E; ++I) {
SymbolRef Sym = *I;
const GRState *state = C.getState();
const StreamState *SS = state->get<StreamState>(Sym);
if (!SS)
return;
if (SS->isOpened()) {
ExplodedNode *N = C.GenerateSink();
if (N) {
if (!BT_ResourceLeak)
BT_ResourceLeak = new BuiltinBug("Resource Leak",
"Opened File never closed. Potential Resource leak.");
BugReport *R = new BugReport(*BT_ResourceLeak,
BT_ResourceLeak->getDescription(), N);
C.EmitReport(R);
}
}
}
}
void StreamChecker::EvalEndPath(GREndPathNodeBuilder &B, void *tag,
GRExprEngine &Eng) {
SaveAndRestore<bool> OldHasGen(B.HasGeneratedNode);
const GRState *state = B.getState();
typedef llvm::ImmutableMap<SymbolRef, StreamState> SymMap;
SymMap M = state->get<StreamState>();
for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) {
StreamState SS = I->second;
if (SS.isOpened()) {
ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor());
if (N) {
if (!BT_ResourceLeak)
BT_ResourceLeak = new BuiltinBug("Resource Leak",
"Opened File never closed. Potential Resource leak.");
BugReport *R = new BugReport(*BT_ResourceLeak,
BT_ResourceLeak->getDescription(), N);
Eng.getBugReporter().EmitReport(R);
}
}
}
}
void StreamChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) {
const Expr *RetE = S->getRetValue();
if (!RetE)
return;
const GRState *state = C.getState();
SymbolRef Sym = state->getSVal(RetE).getAsSymbol();
if (!Sym)
return;
const StreamState *SS = state->get<StreamState>(Sym);
if(!SS)
return;
if (SS->isOpened())
state = state->set<StreamState>(Sym, StreamState::getEscaped(S));
C.addTransition(state);
}

View File

@ -17,21 +17,25 @@ void f1(void) {
FILE *p = fopen("foo", "r");
char buf[1024];
fread(buf, 1, 1, p); // expected-warning {{Stream pointer might be NULL.}}
fclose(p);
}
void f2(void) {
FILE *p = fopen("foo", "r");
fseek(p, 1, SEEK_SET); // expected-warning {{Stream pointer might be NULL.}}
fclose(p);
}
void f3(void) {
FILE *p = fopen("foo", "r");
ftell(p); // expected-warning {{Stream pointer might be NULL.}}
fclose(p);
}
void f4(void) {
FILE *p = fopen("foo", "r");
rewind(p); // expected-warning {{Stream pointer might be NULL.}}
fclose(p);
}
void f5(void) {
@ -40,6 +44,7 @@ void f5(void) {
return;
fseek(p, 1, SEEK_SET); // no-warning
fseek(p, 1, 3); // expected-warning {{The whence argument to fseek() should be SEEK_SET, SEEK_END, or SEEK_CUR.}}
fclose(p);
}
void f6(void) {
@ -51,4 +56,20 @@ void f6(void) {
void f7(void) {
FILE *p = tmpfile();
ftell(p); // expected-warning {{Stream pointer might be NULL.}}
fclose(p);
}
void f8(int c) {
FILE *p = fopen("foo.c", "r");
if(c)
return; // expected-warning {{Opened File never closed. Potential Resource leak.}}
fclose(p);
}
FILE *f9(void) {
FILE *p = fopen("foo.c", "r");
if (p)
return p; // no-warning
else
return 0;
}