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