aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnna Zaks <ganna@apple.com>2011-12-14 00:56:18 +0000
committerAnna Zaks <ganna@apple.com>2011-12-14 00:56:18 +0000
commitefd6989f4644c8460854606e085fc69535054058 (patch)
treecdfefd4756583ef38abfd8a881269d890157851c
parent28fd98d66dab4569316de2b5881d91b534a42461 (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.cpp50
-rw-r--r--test/Analysis/taint-tester.c29
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}}