GenericTaintChecker.cpp revision efd6989f4644c8460854606e085fc69535054058
1//== GenericTaintChecker.cpp ----------------------------------- -*- C++ -*--=//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This checker defines the attack surface for generic taint propagation.
11//
12// The taint information produced by it might be useful to other checkers. For
13// example, checkers should report errors which involve tainted data more
14// aggressively, even if the involved symbols are under constrained.
15//
16//===----------------------------------------------------------------------===//
17#include "ClangSACheckers.h"
18#include "clang/StaticAnalyzer/Core/Checker.h"
19#include "clang/StaticAnalyzer/Core/CheckerManager.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
22
23using namespace clang;
24using namespace ento;
25
26namespace {
27class GenericTaintChecker : public Checker< check::PostStmt<CallExpr>,
28                                            check::PostStmt<DeclRefExpr> > {
29
30  mutable llvm::OwningPtr<BugType> BT;
31  void initBugType() const;
32
33  /// Given a pointer argument, get the symbol of the value it contains
34  /// (points to).
35  SymbolRef getPointedToSymbol(CheckerContext &C,
36                               const Expr* Arg,
37                               bool IssueWarning = true) const;
38
39  /// Functions defining the attacke surface.
40  typedef void (GenericTaintChecker::*FnCheck)(const CallExpr *,
41                                               CheckerContext &C) const;
42  void processScanf(const CallExpr *CE, CheckerContext &C) const;
43  void processFscanf(const CallExpr *CE, CheckerContext &C) const;
44  void processRetTaint(const CallExpr *CE, CheckerContext &C) const;
45
46  bool isStdin(const Expr *E, CheckerContext &C) const;
47  bool isStdin(const DeclRefExpr *E, CheckerContext &C) const;
48
49public:
50  void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
51  void checkPostStmt(const DeclRefExpr *DRE, CheckerContext &C) const;
52};
53}
54
55inline void GenericTaintChecker::initBugType() const {
56  if (!BT)
57    BT.reset(new BugType("Tainted data checking", "General"));
58}
59
60void GenericTaintChecker::checkPostStmt(const CallExpr *CE,
61                                        CheckerContext &C) const {
62  if (!C.getState())
63    return;
64
65  StringRef Name = C.getCalleeName(CE);
66
67  // Define the attack surface.
68  // Set the evaluation function by switching on the callee name.
69  FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
70    .Case("scanf", &GenericTaintChecker::processScanf)
71    .Case("fscanf", &GenericTaintChecker::processFscanf)
72    .Case("sscanf", &GenericTaintChecker::processFscanf)
73    // TODO: Add support for vfscanf & family.
74    .Case("getchar", &GenericTaintChecker::processRetTaint)
75    .Case("getenv", &GenericTaintChecker::processRetTaint)
76    .Case("fopen", &GenericTaintChecker::processRetTaint)
77    .Case("fdopen", &GenericTaintChecker::processRetTaint)
78    .Case("freopen", &GenericTaintChecker::processRetTaint)
79    .Default(NULL);
80
81  // If the callee isn't defined, it is not of security concern.
82  // Check and evaluate the call.
83  if (evalFunction)
84    (this->*evalFunction)(CE, C);
85}
86
87void GenericTaintChecker::checkPostStmt(const DeclRefExpr *DRE,
88                                       CheckerContext &C) const {
89  if (isStdin(DRE, C)) {
90    const ProgramState *NewState = C.getState()->addTaint(DRE);
91    C.addTransition(NewState);
92  }
93}
94
95SymbolRef GenericTaintChecker::getPointedToSymbol(CheckerContext &C,
96                                                  const Expr* Arg,
97                                                  bool IssueWarning) const {
98  const ProgramState *State = C.getState();
99  SVal AddrVal = State->getSVal(Arg->IgnoreParens());
100
101  // TODO: Taint is not going to propagate? Should we ever peel off the casts
102  // IgnoreParenImpCasts()?
103  if (AddrVal.isUnknownOrUndef()) {
104    assert(State->getSVal(Arg->IgnoreParenImpCasts()).isUnknownOrUndef());
105    return 0;
106  }
107
108  Loc *AddrLoc = dyn_cast<Loc>(&AddrVal);
109
110  if (!AddrLoc && !IssueWarning)
111    return 0;
112
113  // If the Expr is not a location, issue a warning.
114  if (!AddrLoc) {
115    assert(IssueWarning);
116    if (ExplodedNode *N = C.generateSink(State)) {
117      initBugType();
118      BugReport *report = new BugReport(*BT, "Pointer argument is expected.",N);
119      report->addRange(Arg->getSourceRange());
120      C.EmitReport(report);
121    }
122    return 0;
123  }
124
125  SVal Val = State->getSVal(*AddrLoc);
126  return Val.getAsSymbol();
127}
128
129void GenericTaintChecker::processScanf(const CallExpr *CE,
130                                       CheckerContext &C) const {
131  const ProgramState *State = C.getState();
132  assert(CE->getNumArgs() >= 2);
133  SVal x = State->getSVal(CE->getArg(1));
134  // All arguments except for the very first one should get taint.
135  for (unsigned int i = 1; i < CE->getNumArgs(); ++i) {
136    // The arguments are pointer arguments. The data they are pointing at is
137    // tainted after the call.
138    const Expr* Arg = CE->getArg(i);
139    SymbolRef Sym = getPointedToSymbol(C, Arg);
140    if (Sym)
141      State = State->addTaint(Sym);
142  }
143  C.addTransition(State);
144}
145
146/// If argument 0 (file descriptor) is tainted, all arguments except for arg 0
147/// and arg 1 should get taint.
148void GenericTaintChecker::processFscanf(const CallExpr *CE,
149                                        CheckerContext &C) const {
150  const ProgramState *State = C.getState();
151  assert(CE->getNumArgs() >= 2);
152
153  // Check is the file descriptor is tainted.
154  if (!State->isTainted(CE->getArg(0)) && !isStdin(CE->getArg(0), C))
155    return;
156
157  // All arguments except for the first two should get taint.
158  for (unsigned int i = 2; i < CE->getNumArgs(); ++i) {
159    // The arguments are pointer arguments. The data they are pointing at is
160    // tainted after the call.
161    const Expr* Arg = CE->getArg(i);
162    SymbolRef Sym = getPointedToSymbol(C, Arg);
163    if (Sym)
164      State = State->addTaint(Sym);
165  }
166  C.addTransition(State);
167}
168
169void GenericTaintChecker::processRetTaint(const CallExpr *CE,
170                                          CheckerContext &C) const {
171  const ProgramState *NewState = C.getState()->addTaint(CE);
172  C.addTransition(NewState);
173}
174
175bool GenericTaintChecker::isStdin(const Expr *E,
176                                  CheckerContext &C) const {
177  if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
178    return isStdin(DR, C);
179  return false;
180}
181
182bool GenericTaintChecker::isStdin(const DeclRefExpr *DR,
183                                  CheckerContext &C) const {
184  const VarDecl *D = dyn_cast_or_null<VarDecl>(DR->getDecl());
185  if (!D)
186    return false;
187
188  D = D->getCanonicalDecl();
189  if ((D->getName().find("stdin") != StringRef::npos) && D->isExternC())
190    if (const PointerType * PtrTy =
191          dyn_cast<PointerType>(D->getType().getTypePtr()))
192      if (PtrTy->getPointeeType() == C.getASTContext().getFILEType())
193        return true;
194
195  return false;
196}
197
198
199void ento::registerGenericTaintChecker(CheckerManager &mgr) {
200  mgr.registerChecker<GenericTaintChecker>();
201}
202