diff options
author | Anna Zaks <ganna@apple.com> | 2011-12-14 00:56:18 +0000 |
---|---|---|
committer | Anna Zaks <ganna@apple.com> | 2011-12-14 00:56:18 +0000 |
commit | efd6989f4644c8460854606e085fc69535054058 (patch) | |
tree | cdfefd4756583ef38abfd8a881269d890157851c | |
parent | 28fd98d66dab4569316de2b5881d91b534a42461 (diff) |
[analyzer] Treat stdin as a source of taint.
Some of the test cases do not currently work because the analyzer core
does not seem to call checkers for pre/post DeclRefExpr visits.
(Opened radar://10573500. To be fixed later on.)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@146536 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp | 50 | ||||
-rw-r--r-- | test/Analysis/taint-tester.c | 29 |
2 files changed, 62 insertions, 17 deletions
diff --git a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp index 890a592c17..739287284b 100644 --- a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -24,7 +24,8 @@ using namespace clang; using namespace ento; namespace { -class GenericTaintChecker : public Checker< check::PostStmt<CallExpr> > { +class GenericTaintChecker : public Checker< check::PostStmt<CallExpr>, + check::PostStmt<DeclRefExpr> > { mutable llvm::OwningPtr<BugType> BT; void initBugType() const; @@ -42,8 +43,12 @@ class GenericTaintChecker : public Checker< check::PostStmt<CallExpr> > { void processFscanf(const CallExpr *CE, CheckerContext &C) const; void processRetTaint(const CallExpr *CE, CheckerContext &C) const; + bool isStdin(const Expr *E, CheckerContext &C) const; + bool isStdin(const DeclRefExpr *E, CheckerContext &C) const; + public: void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPostStmt(const DeclRefExpr *DRE, CheckerContext &C) const; }; } @@ -77,18 +82,28 @@ void GenericTaintChecker::checkPostStmt(const CallExpr *CE, // Check and evaluate the call. if (evalFunction) (this->*evalFunction)(CE, C); +} +void GenericTaintChecker::checkPostStmt(const DeclRefExpr *DRE, + CheckerContext &C) const { + if (isStdin(DRE, C)) { + const ProgramState *NewState = C.getState()->addTaint(DRE); + C.addTransition(NewState); + } } SymbolRef GenericTaintChecker::getPointedToSymbol(CheckerContext &C, const Expr* Arg, bool IssueWarning) const { const ProgramState *State = C.getState(); - SVal AddrVal = State->getSVal(Arg->IgnoreParenCasts()); + SVal AddrVal = State->getSVal(Arg->IgnoreParens()); - // TODO: Taint is not going to propagate? - if (AddrVal.isUnknownOrUndef()) + // TODO: Taint is not going to propagate? Should we ever peel off the casts + // IgnoreParenImpCasts()? + if (AddrVal.isUnknownOrUndef()) { + assert(State->getSVal(Arg->IgnoreParenImpCasts()).isUnknownOrUndef()); return 0; + } Loc *AddrLoc = dyn_cast<Loc>(&AddrVal); @@ -111,7 +126,6 @@ SymbolRef GenericTaintChecker::getPointedToSymbol(CheckerContext &C, return Val.getAsSymbol(); } - void GenericTaintChecker::processScanf(const CallExpr *CE, CheckerContext &C) const { const ProgramState *State = C.getState(); @@ -137,7 +151,7 @@ void GenericTaintChecker::processFscanf(const CallExpr *CE, assert(CE->getNumArgs() >= 2); // Check is the file descriptor is tainted. - if (!State->isTainted(CE->getArg(0))) + if (!State->isTainted(CE->getArg(0)) && !isStdin(CE->getArg(0), C)) return; // All arguments except for the first two should get taint. @@ -158,6 +172,30 @@ void GenericTaintChecker::processRetTaint(const CallExpr *CE, C.addTransition(NewState); } +bool GenericTaintChecker::isStdin(const Expr *E, + CheckerContext &C) const { + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) + return isStdin(DR, C); + return false; +} + +bool GenericTaintChecker::isStdin(const DeclRefExpr *DR, + CheckerContext &C) const { + const VarDecl *D = dyn_cast_or_null<VarDecl>(DR->getDecl()); + if (!D) + return false; + + D = D->getCanonicalDecl(); + if ((D->getName().find("stdin") != StringRef::npos) && D->isExternC()) + if (const PointerType * PtrTy = + dyn_cast<PointerType>(D->getType().getTypePtr())) + if (PtrTy->getPointeeType() == C.getASTContext().getFILEType()) + return true; + + return false; +} + + void ento::registerGenericTaintChecker(CheckerManager &mgr) { mgr.registerChecker<GenericTaintChecker>(); } diff --git a/test/Analysis/taint-tester.c b/test/Analysis/taint-tester.c index e4f39ff616..2908e60fe8 100644 --- a/test/Analysis/taint-tester.c +++ b/test/Analysis/taint-tester.c @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -analyze -analyzer-checker=experimental.security.taint,debug.TaintTest -verify %s +#include <stdarg.h> + int scanf(const char *restrict format, ...); int getchar(void); @@ -84,14 +86,10 @@ void getenvTest(char *home) { } } -struct _IO_FILE { - unsigned fakeField1; - char fakeField2; -}; -typedef struct _IO_FILE FILE; -extern struct _IO_FILE *stdin; -extern struct _IO_FILE *stdout; -extern struct _IO_FILE *stderr; +typedef struct _FILE FILE; +extern FILE *stdin; +extern FILE *stdout; +extern FILE *stderr; int fscanf(FILE *restrict stream, const char *restrict format, ...); int fprintf(FILE *stream, const char *format, ...); int fclose(FILE *stream); @@ -102,13 +100,22 @@ int fscanfTest(void) { char s[80]; int t; + // Check if stdin is treated as tainted. + fscanf(stdin, "%s %d", s, &t); + // Note, here, s is not tainted, but the data s points to is tainted. + char *ts = s; + char tss = s[0]; // expected-warning 1 {{tainted}} + int tt = t; // expected-warning 1 {{tainted}} if((fp=fopen("test", "w")) == 0) // expected-warning 3 {{tainted}} return 1; - // TODO: Have to mark stdin as tainted. - fscanf(stdin, "%s%d", s, &t); - fprintf(fp, "%s %d", s, t); // expected-warning 1 {{tainted}} + fprintf(fp, "%s %d", s, t); // expected-warning 2 {{tainted}} fclose(fp); // expected-warning 1 {{tainted}} + // Check if we propagate taint from stdin when it's used in an assignment. + FILE *pfstd = stdin; + fscanf(pfstd, "%s %d", s, &t); // TODO: This should be tainted as well. + + // Test fscanf and fopen. if((fp=fopen("test","r")) == 0) // expected-warning 3 {{tainted}} return 1; fscanf(fp, "%s%d", s, &t); // expected-warning 1 {{tainted}} |