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