GenericTaintChecker.cpp revision 1009ac715501a4fa1951d94722dcbe6ab30068f8
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
29  mutable llvm::OwningPtr<BugType> BT;
30  void initBugType() const;
31
32  /// Given a pointer argument, get the symbol of the value it contains
33  /// (points to).
34  SymbolRef getPointedToSymbol(CheckerContext &C,
35                               const Expr* Arg,
36                               bool IssueWarning = true) const;
37
38  /// Functions defining the attacke surface.
39  typedef void (GenericTaintChecker::*FnCheck)(const CallExpr *,
40                                               CheckerContext &C) const;
41  void processScanf(const CallExpr *CE, CheckerContext &C) const;
42  void processFscanf(const CallExpr *CE, CheckerContext &C) const;
43  void processRetTaint(const CallExpr *CE, CheckerContext &C) const;
44
45public:
46  void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
47};
48}
49
50inline void GenericTaintChecker::initBugType() const {
51  if (!BT)
52    BT.reset(new BugType("Tainted data checking", "General"));
53}
54
55void GenericTaintChecker::checkPostStmt(const CallExpr *CE,
56                                        CheckerContext &C) const {
57  if (!C.getState())
58    return;
59
60  StringRef Name = C.getCalleeName(CE);
61
62  // Define the attack surface.
63  // Set the evaluation function by switching on the callee name.
64  FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
65    .Case("scanf", &GenericTaintChecker::processScanf)
66    .Case("fscanf", &GenericTaintChecker::processFscanf)
67    .Case("sscanf", &GenericTaintChecker::processFscanf)
68    // TODO: Add support for vfscanf & family.
69    .Case("getchar", &GenericTaintChecker::processRetTaint)
70    .Case("getenv", &GenericTaintChecker::processRetTaint)
71    .Case("fopen", &GenericTaintChecker::processRetTaint)
72    .Case("fdopen", &GenericTaintChecker::processRetTaint)
73    .Case("freopen", &GenericTaintChecker::processRetTaint)
74    .Default(NULL);
75
76  // If the callee isn't defined, it is not of security concern.
77  // Check and evaluate the call.
78  if (evalFunction)
79    (this->*evalFunction)(CE, C);
80
81}
82
83SymbolRef GenericTaintChecker::getPointedToSymbol(CheckerContext &C,
84                                                  const Expr* Arg,
85                                                  bool IssueWarning) const {
86  const ProgramState *State = C.getState();
87  SVal AddrVal = State->getSVal(Arg->IgnoreParenCasts());
88
89  // TODO: Taint is not going to propagate?
90  if (AddrVal.isUnknownOrUndef())
91    return 0;
92
93  Loc *AddrLoc = dyn_cast<Loc>(&AddrVal);
94
95  if (!AddrLoc && !IssueWarning)
96    return 0;
97
98  // If the Expr is not a location, issue a warning.
99  if (!AddrLoc) {
100    assert(IssueWarning);
101    if (ExplodedNode *N = C.generateSink(State)) {
102      initBugType();
103      BugReport *report = new BugReport(*BT, "Pointer argument is expected.",N);
104      report->addRange(Arg->getSourceRange());
105      C.EmitReport(report);
106    }
107    return 0;
108  }
109
110  SVal Val = State->getSVal(*AddrLoc);
111  return Val.getAsSymbol();
112}
113
114
115void GenericTaintChecker::processScanf(const CallExpr *CE,
116                                       CheckerContext &C) const {
117  const ProgramState *State = C.getState();
118  assert(CE->getNumArgs() >= 2);
119  SVal x = State->getSVal(CE->getArg(1));
120  // All arguments except for the very first one should get taint.
121  for (unsigned int i = 1; i < CE->getNumArgs(); ++i) {
122    // The arguments are pointer arguments. The data they are pointing at is
123    // tainted after the call.
124    const Expr* Arg = CE->getArg(i);
125    SymbolRef Sym = getPointedToSymbol(C, Arg);
126    if (Sym)
127      State = State->addTaint(Sym);
128  }
129  C.addTransition(State);
130}
131
132/// If argument 0 (file descriptor) is tainted, all arguments except for arg 0
133/// and arg 1 should get taint.
134void GenericTaintChecker::processFscanf(const CallExpr *CE,
135                                        CheckerContext &C) const {
136  const ProgramState *State = C.getState();
137  assert(CE->getNumArgs() >= 2);
138
139  // Check is the file descriptor is tainted.
140  if (!State->isTainted(CE->getArg(0)))
141    return;
142
143  // All arguments except for the first two should get taint.
144  for (unsigned int i = 2; i < CE->getNumArgs(); ++i) {
145    // The arguments are pointer arguments. The data they are pointing at is
146    // tainted after the call.
147    const Expr* Arg = CE->getArg(i);
148    SymbolRef Sym = getPointedToSymbol(C, Arg);
149    if (Sym)
150      State = State->addTaint(Sym);
151  }
152  C.addTransition(State);
153}
154
155void GenericTaintChecker::processRetTaint(const CallExpr *CE,
156                                          CheckerContext &C) const {
157  const ProgramState *NewState = C.getState()->addTaint(CE);
158  C.addTransition(NewState);
159}
160
161void ento::registerGenericTaintChecker(CheckerManager &mgr) {
162  mgr.registerChecker<GenericTaintChecker>();
163}
164