blob: 739287284bba16abafe4a97d52fc1530dda4ba84 [file] [log] [blame]
Anna Zaksdf18c5a2011-11-16 19:58:13 +00001//== 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 {
Anna Zaksefd69892011-12-14 00:56:18 +000027class GenericTaintChecker : public Checker< check::PostStmt<CallExpr>,
28 check::PostStmt<DeclRefExpr> > {
Anna Zaksdf18c5a2011-11-16 19:58:13 +000029
Anna Zaks8f4caf52011-11-18 02:26:36 +000030 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;
Anna Zaksdf18c5a2011-11-16 19:58:13 +000038
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;
Anna Zaks1009ac72011-12-14 00:56:02 +000043 void processFscanf(const CallExpr *CE, CheckerContext &C) const;
Anna Zaksdf18c5a2011-11-16 19:58:13 +000044 void processRetTaint(const CallExpr *CE, CheckerContext &C) const;
45
Anna Zaksefd69892011-12-14 00:56:18 +000046 bool isStdin(const Expr *E, CheckerContext &C) const;
47 bool isStdin(const DeclRefExpr *E, CheckerContext &C) const;
48
Anna Zaksdf18c5a2011-11-16 19:58:13 +000049public:
50 void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
Anna Zaksefd69892011-12-14 00:56:18 +000051 void checkPostStmt(const DeclRefExpr *DRE, CheckerContext &C) const;
Anna Zaksdf18c5a2011-11-16 19:58:13 +000052};
53}
54
Anna Zaks8f4caf52011-11-18 02:26:36 +000055inline void GenericTaintChecker::initBugType() const {
56 if (!BT)
57 BT.reset(new BugType("Tainted data checking", "General"));
58}
59
Anna Zaksdf18c5a2011-11-16 19:58:13 +000060void 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)
Anna Zaks1009ac72011-12-14 00:56:02 +000071 .Case("fscanf", &GenericTaintChecker::processFscanf)
72 .Case("sscanf", &GenericTaintChecker::processFscanf)
73 // TODO: Add support for vfscanf & family.
Anna Zaksdf18c5a2011-11-16 19:58:13 +000074 .Case("getchar", &GenericTaintChecker::processRetTaint)
Anna Zakse55a22b2011-12-14 00:55:58 +000075 .Case("getenv", &GenericTaintChecker::processRetTaint)
Anna Zaks1009ac72011-12-14 00:56:02 +000076 .Case("fopen", &GenericTaintChecker::processRetTaint)
77 .Case("fdopen", &GenericTaintChecker::processRetTaint)
78 .Case("freopen", &GenericTaintChecker::processRetTaint)
Anna Zaksdf18c5a2011-11-16 19:58:13 +000079 .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);
Anna Zaksefd69892011-12-14 00:56:18 +000085}
Anna Zaksdf18c5a2011-11-16 19:58:13 +000086
Anna Zaksefd69892011-12-14 00:56:18 +000087void 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 }
Anna Zaksdf18c5a2011-11-16 19:58:13 +000093}
Anna Zaks8f4caf52011-11-18 02:26:36 +000094
95SymbolRef GenericTaintChecker::getPointedToSymbol(CheckerContext &C,
96 const Expr* Arg,
97 bool IssueWarning) const {
98 const ProgramState *State = C.getState();
Anna Zaksefd69892011-12-14 00:56:18 +000099 SVal AddrVal = State->getSVal(Arg->IgnoreParens());
Anna Zakse3d250e2011-12-11 18:43:40 +0000100
Anna Zaksefd69892011-12-14 00:56:18 +0000101 // 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());
Anna Zakse3d250e2011-12-11 18:43:40 +0000105 return 0;
Anna Zaksefd69892011-12-14 00:56:18 +0000106 }
Anna Zakse3d250e2011-12-11 18:43:40 +0000107
Anna Zaksdf18c5a2011-11-16 19:58:13 +0000108 Loc *AddrLoc = dyn_cast<Loc>(&AddrVal);
Anna Zaks8f4caf52011-11-18 02:26:36 +0000109
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
Anna Zaksdf18c5a2011-11-16 19:58:13 +0000125 SVal Val = State->getSVal(*AddrLoc);
126 return Val.getAsSymbol();
127}
128
Anna Zaksdf18c5a2011-11-16 19:58:13 +0000129void GenericTaintChecker::processScanf(const CallExpr *CE,
130 CheckerContext &C) const {
131 const ProgramState *State = C.getState();
Anna Zaks1009ac72011-12-14 00:56:02 +0000132 assert(CE->getNumArgs() >= 2);
Anna Zaksdf18c5a2011-11-16 19:58:13 +0000133 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);
Anna Zaks8f4caf52011-11-18 02:26:36 +0000139 SymbolRef Sym = getPointedToSymbol(C, Arg);
Anna Zaksdf18c5a2011-11-16 19:58:13 +0000140 if (Sym)
141 State = State->addTaint(Sym);
142 }
143 C.addTransition(State);
Anna Zaks1009ac72011-12-14 00:56:02 +0000144}
Anna Zaksdf18c5a2011-11-16 19:58:13 +0000145
Anna Zaks1009ac72011-12-14 00:56:02 +0000146/// 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.
Anna Zaksefd69892011-12-14 00:56:18 +0000154 if (!State->isTainted(CE->getArg(0)) && !isStdin(CE->getArg(0), C))
Anna Zaks1009ac72011-12-14 00:56:02 +0000155 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);
Anna Zaksdf18c5a2011-11-16 19:58:13 +0000167}
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
Anna Zaksefd69892011-12-14 00:56:18 +0000175bool 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
Anna Zaksdf18c5a2011-11-16 19:58:13 +0000199void ento::registerGenericTaintChecker(CheckerManager &mgr) {
200 mgr.registerChecker<GenericTaintChecker>();
201}