[cfe-commits] r146793 - in /cfe/trunk: lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp test/Analysis/taint-tester.c

Anna Zaks ganna at apple.com
Fri Dec 16 16:26:34 PST 2011


Author: zaks
Date: Fri Dec 16 18:26:34 2011
New Revision: 146793

URL: http://llvm.org/viewvc/llvm-project?rev=146793&view=rev
Log:
[analyzer] Add support for taint flowing through a function (atoi).

Check if the input parameters are tainted (or point to tainted data) on
a checkPreStmt<CallExpr>. If the output should be tainted, record it in
the state. On post visit (checkPostStmt<CallExpr>), use the state to
make decisions (in addition to the existing logic). Use this logic for
atoi and fscanf.

Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
    cfe/trunk/test/Analysis/taint-tester.c

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp?rev=146793&r1=146792&r2=146793&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp Fri Dec 16 18:26:34 2011
@@ -18,6 +18,7 @@
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 
 using namespace clang;
@@ -25,72 +26,128 @@
 
 namespace {
 class GenericTaintChecker : public Checker< check::PostStmt<CallExpr>,
-                                            check::PostStmt<DeclRefExpr> > {
+                                            check::PreStmt<CallExpr> > {
+public:
+  enum TaintOnPreVisitKind {
+    /// No taint propagates from pre-visit to post-visit.
+    PrevisitNone = 0,
+    /// Based on the pre-visit, the return argument of the call
+    /// should be tainted.
+    PrevisitTaintRet = 1,
+    /// Based on the pre-visit, the call can taint values through it's
+    /// pointer/reference arguments.
+    PrevisitTaintArgs = 2
+  };
 
+private:
   mutable llvm::OwningPtr<BugType> BT;
   void initBugType() const;
 
   /// Given a pointer argument, get the symbol of the value it contains
   /// (points to).
   SymbolRef getPointedToSymbol(CheckerContext &C,
-                               const Expr* Arg,
+                               const Expr *Arg,
                                bool IssueWarning = true) const;
 
-  /// Functions defining the attacke surface.
-  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;
+  /// Functions defining the attack surface.
+  typedef const ProgramState *(GenericTaintChecker::*FnCheck)(const CallExpr *,
+                                                       CheckerContext &C) const;
+  const ProgramState *postScanf(const CallExpr *CE, CheckerContext &C) const;
+  const ProgramState *postFscanf(const CallExpr *CE, CheckerContext &C) const;
+  const ProgramState *postRetTaint(const CallExpr *CE, CheckerContext &C) const;
+  const ProgramState *postDefault(const CallExpr *CE, CheckerContext &C) const;
+
+  /// Taint the scanned input if the file is tainted.
+  const ProgramState *preFscanf(const CallExpr *CE, CheckerContext &C) const;
+  /// Taint if any of the arguments are tainted.
+  const ProgramState *preAnyArgs(const CallExpr *CE, CheckerContext &C) const;
 
   /// Check if the region the expression evaluates to is the standard input,
   /// and thus, is tainted.
   bool isStdin(const Expr *E, CheckerContext &C) const;
 
 public:
+  static void *getTag() { static int Tag; return &Tag; }
+
   void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
   void checkPostStmt(const DeclRefExpr *DRE, CheckerContext &C) const;
+
+  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+
 };
 }
 
+/// Definitions for the checker specific state.
+namespace { struct TaintOnPreVisit {};}
+namespace clang {
+namespace ento {
+  /// A flag which is used to pass information from call pre-visit instruction
+  /// to the call post-visit. The value is an unsigned, which takes on values
+  /// of the TaintOnPreVisitKind enumeration.
+  template<>
+  struct ProgramStateTrait<TaintOnPreVisit> :
+    public ProgramStatePartialTrait<unsigned> {
+    static void *GDMIndex() { return GenericTaintChecker::getTag(); }
+  };
+}
+}
+
 inline void GenericTaintChecker::initBugType() const {
   if (!BT)
     BT.reset(new BugType("Tainted data checking", "General"));
 }
 
-void GenericTaintChecker::checkPostStmt(const CallExpr *CE,
-                                        CheckerContext &C) const {
-  if (!C.getState())
-    return;
+void GenericTaintChecker::checkPreStmt(const CallExpr *CE,
+                                       CheckerContext &C) const {
+  const ProgramState *State = C.getState();
 
+  // Set the evaluation function by switching on the callee name.
   StringRef Name = C.getCalleeName(CE);
+  FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
+    .Case("fscanf", &GenericTaintChecker::preFscanf)
+    .Case("atoi", &GenericTaintChecker::preAnyArgs)
+    .Case("atol", &GenericTaintChecker::preAnyArgs)
+    .Case("atoll", &GenericTaintChecker::preAnyArgs)
+    .Default(0);
+
+  // Check and evaluate the call.
+  if (evalFunction)
+    State = (this->*evalFunction)(CE, C);
+  if (!State)
+    return;
+
+  C.addTransition(State);
+}
+
+void GenericTaintChecker::checkPostStmt(const CallExpr *CE,
+                                        CheckerContext &C) const {
+  const ProgramState *State = C.getState();
   
   // Define the attack surface.
   // Set the evaluation function by switching on the callee name.
+  StringRef Name = C.getCalleeName(CE);
   FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
-    .Case("scanf", &GenericTaintChecker::processScanf)
-    .Case("fscanf", &GenericTaintChecker::processFscanf)
-    .Case("sscanf", &GenericTaintChecker::processFscanf)
+    .Case("scanf", &GenericTaintChecker::postScanf)
+    .Case("fscanf", &GenericTaintChecker::postFscanf)
+    .Case("sscanf", &GenericTaintChecker::postFscanf)
     // 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);
+    .Case("getchar", &GenericTaintChecker::postRetTaint)
+    .Case("getenv", &GenericTaintChecker::postRetTaint)
+    .Case("fopen", &GenericTaintChecker::postRetTaint)
+    .Case("fdopen", &GenericTaintChecker::postRetTaint)
+    .Case("freopen", &GenericTaintChecker::postRetTaint)
+    .Default(&GenericTaintChecker::postDefault);
 
   // If the callee isn't defined, it is not of security concern.
   // Check and evaluate the call.
   if (evalFunction)
-    (this->*evalFunction)(CE, C);
-}
+    State = (this->*evalFunction)(CE, C);
+  if (!State)
+    return;
 
-void GenericTaintChecker::checkPostStmt(const DeclRefExpr *DRE,
-                                       CheckerContext &C) const {
-  if (isStdin(DRE, C)) {
-    const ProgramState *NewState = C.getState()->addTaint(DRE);
-    C.addTransition(NewState);
-  }
+  assert(State->get<TaintOnPreVisit>() == PrevisitNone &&
+         "State has to be cleared.");
+  C.addTransition(State);
 }
 
 SymbolRef GenericTaintChecker::getPointedToSymbol(CheckerContext &C,
@@ -122,8 +179,45 @@
   return Val.getAsSymbol();
 }
 
-void GenericTaintChecker::processScanf(const CallExpr *CE,
-                                       CheckerContext &C) const {
+const ProgramState *GenericTaintChecker::preFscanf(const CallExpr *CE,
+                                                   CheckerContext &C) const {
+  assert(CE->getNumArgs() >= 2);
+  const ProgramState *State = C.getState();
+
+  // Check is the file descriptor is tainted.
+  if (State->isTainted(CE->getArg(0)) || isStdin(CE->getArg(0), C))
+    return State->set<TaintOnPreVisit>(PrevisitTaintArgs);
+  return 0;
+}
+
+// If any other arguments are tainted, mark state as tainted on pre-visit.
+const ProgramState * GenericTaintChecker::preAnyArgs(const CallExpr *CE,
+                                                     CheckerContext &C) const {
+  for (unsigned int i = 0; i < CE->getNumArgs(); ++i) {
+    const ProgramState *State = C.getState();
+    const Expr *Arg = CE->getArg(i);
+    if (State->isTainted(Arg) || State->isTainted(getPointedToSymbol(C, Arg)))
+      return State = State->set<TaintOnPreVisit>(PrevisitTaintRet);
+  }
+  return 0;
+}
+
+const ProgramState *GenericTaintChecker::postDefault(const CallExpr *CE,
+                                                     CheckerContext &C) const {
+  const ProgramState *State = C.getState();
+
+  // Check if we know that the result needs to be tainted based on the
+  // pre-visit analysis.
+  if (State->get<TaintOnPreVisit>() == PrevisitTaintRet) {
+    State = State->addTaint(CE);
+    return State->set<TaintOnPreVisit>(PrevisitNone);
+  }
+
+  return 0;
+}
+
+const ProgramState *GenericTaintChecker::postScanf(const CallExpr *CE,
+                                                   CheckerContext &C) const {
   const ProgramState *State = C.getState();
   assert(CE->getNumArgs() >= 2);
   SVal x = State->getSVal(CE->getArg(1));
@@ -132,23 +226,27 @@
     // 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);
+        SymbolRef Sym = getPointedToSymbol(C, Arg);
     if (Sym)
       State = State->addTaint(Sym);
   }
-  C.addTransition(State);
+  return 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 *GenericTaintChecker::postFscanf(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)) && !isStdin(CE->getArg(0), C))
-    return;
+  // Fscanf is only tainted if the input file is tainted at pre visit, so
+  // check for that first.
+  if (State->get<TaintOnPreVisit>() == PrevisitNone)
+    return 0;
+
+  // Reset the taint state.
+  State = State->set<TaintOnPreVisit>(PrevisitNone);
 
   // All arguments except for the first two should get taint.
   for (unsigned int i = 2; i < CE->getNumArgs(); ++i) {
@@ -159,13 +257,12 @@
     if (Sym)
       State = State->addTaint(Sym);
   }
-  C.addTransition(State);
+  return State;
 }
 
-void GenericTaintChecker::processRetTaint(const CallExpr *CE,
-                                          CheckerContext &C) const {
-  const ProgramState *NewState = C.getState()->addTaint(CE);
-  C.addTransition(NewState);
+const ProgramState *GenericTaintChecker::postRetTaint(const CallExpr *CE,
+                                                      CheckerContext &C) const {
+  return C.getState()->addTaint(CE);
 }
 
 bool GenericTaintChecker::isStdin(const Expr *E,

Modified: cfe/trunk/test/Analysis/taint-tester.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/taint-tester.c?rev=146793&r1=146792&r2=146793&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/taint-tester.c (original)
+++ cfe/trunk/test/Analysis/taint-tester.c Fri Dec 16 18:26:34 2011
@@ -150,3 +150,19 @@
   fscanf(*ppp, "%d", &iii);
   int jjj = iii;// expected-warning + {{tainted}}
 }
+
+// Test propagation functions - the ones that propagate taint from arguments to
+// return value, ptr arguments.
+
+int atoi(const char *nptr);
+// TODO: Add support for atol and atoll.
+long atol(const char *nptr);
+long long atoll(const char *nptr);
+
+void atoiTest() {
+  char s[80];
+  scanf("%s", s);
+  int d = atoi(s); // expected-warning + {{tainted}}
+  int td = d; // expected-warning + {{tainted}}
+}
+





More information about the cfe-commits mailing list