forked from OSchip/llvm-project
Add FILE* leak check to StreamChecker. Patch by Lei Zhang.
llvm-svn: 109225
This commit is contained in:
parent
4ad7271798
commit
5e6ef6d957
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue