From: Zhongxing Xu Date: Fri, 18 Jun 2010 02:47:46 +0000 (+0000) Subject: Add null stream check for more APIs. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=23d90f90413ff1efd7e4410d28ae2cab99af1fdb;p=clang Add null stream check for more APIs. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@106274 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Checker/StreamChecker.cpp b/lib/Checker/StreamChecker.cpp index 01b7d83042..44919f1339 100644 --- a/lib/Checker/StreamChecker.cpp +++ b/lib/Checker/StreamChecker.cpp @@ -24,11 +24,13 @@ using namespace clang; namespace { class StreamChecker : public CheckerVisitor { - IdentifierInfo *II_fopen, *II_fread; + IdentifierInfo *II_fopen, *II_fread, *II_fseek, *II_ftell, *II_rewind; BuiltinBug *BT_nullfp; public: - StreamChecker() : II_fopen(0), II_fread(0), BT_nullfp(0) {} + StreamChecker() + : II_fopen(0), II_fread(0), II_fseek(0), II_ftell(0), II_rewind(0), + BT_nullfp(0) {} static void *getTag() { static int x; @@ -40,9 +42,13 @@ public: private: void FOpen(CheckerContext &C, const CallExpr *CE); void FRead(CheckerContext &C, const CallExpr *CE); + void FSeek(CheckerContext &C, const CallExpr *CE); + void FTell(CheckerContext &C, const CallExpr *CE); + void Rewind(CheckerContext &C, const CallExpr *CE); + bool CheckNullStream(SVal SV, const GRState *state, CheckerContext &C); }; -} +} // end anonymous namespace void clang::RegisterStreamChecker(GRExprEngine &Eng) { Eng.registerCheck(new StreamChecker()); @@ -63,6 +69,15 @@ bool StreamChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { if (!II_fread) II_fread = &Ctx.Idents.get("fread"); + if (!II_fseek) + II_fseek = &Ctx.Idents.get("fseek"); + + if (!II_ftell) + II_ftell = &Ctx.Idents.get("ftell"); + + if (!II_rewind) + II_rewind = &Ctx.Idents.get("rewind"); + if (FD->getIdentifier() == II_fopen) { FOpen(C, CE); return true; @@ -73,6 +88,21 @@ bool StreamChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { return true; } + if (FD->getIdentifier() == II_fseek) { + FSeek(C, CE); + return true; + } + + if (FD->getIdentifier() == II_ftell) { + FTell(C, CE); + return true; + } + + if (FD->getIdentifier() == II_rewind) { + Rewind(C, CE); + return true; + } + return false; } @@ -96,23 +126,47 @@ void StreamChecker::FOpen(CheckerContext &C, const CallExpr *CE) { void StreamChecker::FRead(CheckerContext &C, const CallExpr *CE) { const GRState *state = C.getState(); + if (CheckNullStream(state->getSVal(CE->getArg(3)), state, C)) + return; +} + +void StreamChecker::FSeek(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::FTell(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} - // Assume CallAndMessageChecker has been run. - SVal StreamVal = state->getSVal(CE->getArg(3)); +bool StreamChecker::CheckNullStream(SVal SV, const GRState *state, + CheckerContext &C) { + const DefinedSVal *DV = dyn_cast(&SV); + if (!DV) + return false; - if (const DefinedSVal *DV = dyn_cast(&StreamVal)) { - ConstraintManager &CM = C.getConstraintManager(); - const GRState *stateNotNull, *stateNull; - llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state, *DV); + ConstraintManager &CM = C.getConstraintManager(); + const GRState *stateNotNull, *stateNull; + llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state, *DV); - if (!stateNotNull && stateNull) { - if (ExplodedNode *N = C.GenerateSink(stateNull)) { - if (!BT_nullfp) - BT_nullfp = new BuiltinBug("NULL stream pointer", + if (!stateNotNull && stateNull) { + if (ExplodedNode *N = C.GenerateSink(stateNull)) { + if (!BT_nullfp) + BT_nullfp = new BuiltinBug("NULL stream pointer", "Stream pointer might be NULL."); - BugReport *R =new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N); - C.EmitReport(R); - } + BugReport *R =new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N); + C.EmitReport(R); } + return true; } + return false; } diff --git a/test/Analysis/stream.c b/test/Analysis/stream.c index 17876688dd..f834a6a603 100644 --- a/test/Analysis/stream.c +++ b/test/Analysis/stream.c @@ -2,11 +2,33 @@ typedef __typeof__(sizeof(int)) size_t; typedef struct _IO_FILE FILE; -FILE *fopen(const char *path, const char *mode); -size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); +#define SEEK_SET 0 /* Seek from beginning of file. */ +#define SEEK_CUR 1 /* Seek from current position. */ +#define SEEK_END 2 /* Seek from end of file. */ +extern FILE *fopen(const char *path, const char *mode); +extern size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); +extern int fseek (FILE *__stream, long int __off, int __whence); +extern long int ftell (FILE *__stream); +extern void rewind (FILE *__stream); void f1(void) { FILE *p = fopen("foo", "r"); char buf[1024]; fread(buf, 1, 1, p); // expected-warning {{Stream pointer might be NULL.}} } + +void f2(void) { + FILE *p = fopen("foo", "r"); + fseek(p, 1, SEEK_SET); // expected-warning {{Stream pointer might be NULL.}} +} + +void f3(void) { + FILE *p = fopen("foo", "r"); + ftell(p); // expected-warning {{Stream pointer might be NULL.}} +} + +void f4(void) { + FILE *p = fopen("foo", "r"); + rewind(p); // expected-warning {{Stream pointer might be NULL.}} +} +