diff --git a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp index dcf7694464ed..890a592c17c9 100644 --- a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -39,6 +39,7 @@ class GenericTaintChecker : public Checker< check::PostStmt > { typedef void (GenericTaintChecker::*FnCheck)(const CallExpr *, CheckerContext &C) const; void processScanf(const CallExpr *CE, CheckerContext &C) const; + void processFscanf(const CallExpr *CE, CheckerContext &C) const; void processRetTaint(const CallExpr *CE, CheckerContext &C) const; public: @@ -62,8 +63,14 @@ void GenericTaintChecker::checkPostStmt(const CallExpr *CE, // Set the evaluation function by switching on the callee name. FnCheck evalFunction = llvm::StringSwitch(Name) .Case("scanf", &GenericTaintChecker::processScanf) + .Case("fscanf", &GenericTaintChecker::processFscanf) + .Case("sscanf", &GenericTaintChecker::processFscanf) + // TODO: Add support for vfscanf & family. .Case("getchar", &GenericTaintChecker::processRetTaint) .Case("getenv", &GenericTaintChecker::processRetTaint) + .Case("fopen", &GenericTaintChecker::processRetTaint) + .Case("fdopen", &GenericTaintChecker::processRetTaint) + .Case("freopen", &GenericTaintChecker::processRetTaint) .Default(NULL); // If the callee isn't defined, it is not of security concern. @@ -108,7 +115,7 @@ SymbolRef GenericTaintChecker::getPointedToSymbol(CheckerContext &C, void GenericTaintChecker::processScanf(const CallExpr *CE, CheckerContext &C) const { const ProgramState *State = C.getState(); - assert(CE->getNumArgs() == 2); + assert(CE->getNumArgs() >= 2); SVal x = State->getSVal(CE->getArg(1)); // All arguments except for the very first one should get taint. for (unsigned int i = 1; i < CE->getNumArgs(); ++i) { @@ -120,7 +127,29 @@ void GenericTaintChecker::processScanf(const CallExpr *CE, State = State->addTaint(Sym); } C.addTransition(State); +} +/// If argument 0 (file descriptor) is tainted, all arguments except for arg 0 +/// and arg 1 should get taint. +void GenericTaintChecker::processFscanf(const CallExpr *CE, + CheckerContext &C) const { + const ProgramState *State = C.getState(); + assert(CE->getNumArgs() >= 2); + + // Check is the file descriptor is tainted. + if (!State->isTainted(CE->getArg(0))) + return; + + // All arguments except for the first two should get taint. + for (unsigned int i = 2; i < CE->getNumArgs(); ++i) { + // The arguments are pointer arguments. The data they are pointing at is + // tainted after the call. + const Expr* Arg = CE->getArg(i); + SymbolRef Sym = getPointedToSymbol(C, Arg); + if (Sym) + State = State->addTaint(Sym); + } + C.addTransition(State); } void GenericTaintChecker::processRetTaint(const CallExpr *CE, diff --git a/clang/test/Analysis/taint-tester.c b/clang/test/Analysis/taint-tester.c index 7e2d77175871..e4f39ff6167c 100644 --- a/clang/test/Analysis/taint-tester.c +++ b/clang/test/Analysis/taint-tester.c @@ -54,6 +54,10 @@ void taintTracking(int x) { int tx = xy.x; // expected-warning {{tainted}} int ty = xy.y; // FIXME: This should be tainted as well. char ntz = xy.z;// no warning + // Now, scanf scans both. + scanf("%d %d", &xy.y, &xy.x); + int ttx = xy.x; // expected-warning {{tainted}} + int tty = xy.y; // expected-warning {{tainted}} } void BitwiseOp(int in, char inn) { @@ -80,3 +84,34 @@ 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; +int fscanf(FILE *restrict stream, const char *restrict format, ...); +int fprintf(FILE *stream, const char *format, ...); +int fclose(FILE *stream); +FILE *fopen(const char *path, const char *mode); + +int fscanfTest(void) { + FILE *fp; + char s[80]; + int t; + + 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}} + fclose(fp); // expected-warning 1 {{tainted}} + + if((fp=fopen("test","r")) == 0) // expected-warning 3 {{tainted}} + return 1; + fscanf(fp, "%s%d", s, &t); // expected-warning 1 {{tainted}} + fprintf(stdout, "%s %d", s, t); // expected-warning 1 {{tainted}} + return 0; +}