aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnna Zaks <ganna@apple.com>2011-12-14 00:56:02 +0000
committerAnna Zaks <ganna@apple.com>2011-12-14 00:56:02 +0000
commit1009ac715501a4fa1951d94722dcbe6ab30068f8 (patch)
tree9ff673646068f2b9d6abc3b98570ca563626765c
parente55a22b917327651178ddea36b3615f579681eea (diff)
[analyzer] Mark output of fscanf and fopen as tainted.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@146533 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp31
-rw-r--r--test/Analysis/taint-tester.c35
2 files changed, 65 insertions, 1 deletions
diff --git a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
index dcf7694464..890a592c17 100644
--- a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -39,6 +39,7 @@ class GenericTaintChecker : public Checker< check::PostStmt<CallExpr> > {
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<FnCheck>(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/test/Analysis/taint-tester.c b/test/Analysis/taint-tester.c
index 7e2d771758..e4f39ff616 100644
--- a/test/Analysis/taint-tester.c
+++ b/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;
+}