1*67e74705SXin Li //==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==//
2*67e74705SXin Li //
3*67e74705SXin Li // The LLVM Compiler Infrastructure
4*67e74705SXin Li //
5*67e74705SXin Li // This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li // License. See LICENSE.TXT for details.
7*67e74705SXin Li //
8*67e74705SXin Li //===----------------------------------------------------------------------===//
9*67e74705SXin Li //
10*67e74705SXin Li // This file defines a set of flow-insensitive security checks.
11*67e74705SXin Li //
12*67e74705SXin Li //===----------------------------------------------------------------------===//
13*67e74705SXin Li
14*67e74705SXin Li #include "ClangSACheckers.h"
15*67e74705SXin Li #include "clang/AST/StmtVisitor.h"
16*67e74705SXin Li #include "clang/Analysis/AnalysisContext.h"
17*67e74705SXin Li #include "clang/Basic/TargetInfo.h"
18*67e74705SXin Li #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
19*67e74705SXin Li #include "clang/StaticAnalyzer/Core/Checker.h"
20*67e74705SXin Li #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
21*67e74705SXin Li #include "llvm/ADT/SmallString.h"
22*67e74705SXin Li #include "llvm/ADT/StringSwitch.h"
23*67e74705SXin Li #include "llvm/Support/raw_ostream.h"
24*67e74705SXin Li
25*67e74705SXin Li using namespace clang;
26*67e74705SXin Li using namespace ento;
27*67e74705SXin Li
isArc4RandomAvailable(const ASTContext & Ctx)28*67e74705SXin Li static bool isArc4RandomAvailable(const ASTContext &Ctx) {
29*67e74705SXin Li const llvm::Triple &T = Ctx.getTargetInfo().getTriple();
30*67e74705SXin Li return T.getVendor() == llvm::Triple::Apple ||
31*67e74705SXin Li T.getOS() == llvm::Triple::CloudABI ||
32*67e74705SXin Li T.getOS() == llvm::Triple::FreeBSD ||
33*67e74705SXin Li T.getOS() == llvm::Triple::NetBSD ||
34*67e74705SXin Li T.getOS() == llvm::Triple::OpenBSD ||
35*67e74705SXin Li T.getOS() == llvm::Triple::Bitrig ||
36*67e74705SXin Li T.getOS() == llvm::Triple::DragonFly;
37*67e74705SXin Li }
38*67e74705SXin Li
39*67e74705SXin Li namespace {
40*67e74705SXin Li struct ChecksFilter {
41*67e74705SXin Li DefaultBool check_gets;
42*67e74705SXin Li DefaultBool check_getpw;
43*67e74705SXin Li DefaultBool check_mktemp;
44*67e74705SXin Li DefaultBool check_mkstemp;
45*67e74705SXin Li DefaultBool check_strcpy;
46*67e74705SXin Li DefaultBool check_rand;
47*67e74705SXin Li DefaultBool check_vfork;
48*67e74705SXin Li DefaultBool check_FloatLoopCounter;
49*67e74705SXin Li DefaultBool check_UncheckedReturn;
50*67e74705SXin Li
51*67e74705SXin Li CheckName checkName_gets;
52*67e74705SXin Li CheckName checkName_getpw;
53*67e74705SXin Li CheckName checkName_mktemp;
54*67e74705SXin Li CheckName checkName_mkstemp;
55*67e74705SXin Li CheckName checkName_strcpy;
56*67e74705SXin Li CheckName checkName_rand;
57*67e74705SXin Li CheckName checkName_vfork;
58*67e74705SXin Li CheckName checkName_FloatLoopCounter;
59*67e74705SXin Li CheckName checkName_UncheckedReturn;
60*67e74705SXin Li };
61*67e74705SXin Li
62*67e74705SXin Li class WalkAST : public StmtVisitor<WalkAST> {
63*67e74705SXin Li BugReporter &BR;
64*67e74705SXin Li AnalysisDeclContext* AC;
65*67e74705SXin Li enum { num_setids = 6 };
66*67e74705SXin Li IdentifierInfo *II_setid[num_setids];
67*67e74705SXin Li
68*67e74705SXin Li const bool CheckRand;
69*67e74705SXin Li const ChecksFilter &filter;
70*67e74705SXin Li
71*67e74705SXin Li public:
WalkAST(BugReporter & br,AnalysisDeclContext * ac,const ChecksFilter & f)72*67e74705SXin Li WalkAST(BugReporter &br, AnalysisDeclContext* ac,
73*67e74705SXin Li const ChecksFilter &f)
74*67e74705SXin Li : BR(br), AC(ac), II_setid(),
75*67e74705SXin Li CheckRand(isArc4RandomAvailable(BR.getContext())),
76*67e74705SXin Li filter(f) {}
77*67e74705SXin Li
78*67e74705SXin Li // Statement visitor methods.
79*67e74705SXin Li void VisitCallExpr(CallExpr *CE);
80*67e74705SXin Li void VisitForStmt(ForStmt *S);
81*67e74705SXin Li void VisitCompoundStmt (CompoundStmt *S);
VisitStmt(Stmt * S)82*67e74705SXin Li void VisitStmt(Stmt *S) { VisitChildren(S); }
83*67e74705SXin Li
84*67e74705SXin Li void VisitChildren(Stmt *S);
85*67e74705SXin Li
86*67e74705SXin Li // Helpers.
87*67e74705SXin Li bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD);
88*67e74705SXin Li
89*67e74705SXin Li typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *);
90*67e74705SXin Li
91*67e74705SXin Li // Checker-specific methods.
92*67e74705SXin Li void checkLoopConditionForFloat(const ForStmt *FS);
93*67e74705SXin Li void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD);
94*67e74705SXin Li void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD);
95*67e74705SXin Li void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD);
96*67e74705SXin Li void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD);
97*67e74705SXin Li void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD);
98*67e74705SXin Li void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD);
99*67e74705SXin Li void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);
100*67e74705SXin Li void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);
101*67e74705SXin Li void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD);
102*67e74705SXin Li void checkUncheckedReturnValue(CallExpr *CE);
103*67e74705SXin Li };
104*67e74705SXin Li } // end anonymous namespace
105*67e74705SXin Li
106*67e74705SXin Li //===----------------------------------------------------------------------===//
107*67e74705SXin Li // AST walking.
108*67e74705SXin Li //===----------------------------------------------------------------------===//
109*67e74705SXin Li
VisitChildren(Stmt * S)110*67e74705SXin Li void WalkAST::VisitChildren(Stmt *S) {
111*67e74705SXin Li for (Stmt *Child : S->children())
112*67e74705SXin Li if (Child)
113*67e74705SXin Li Visit(Child);
114*67e74705SXin Li }
115*67e74705SXin Li
VisitCallExpr(CallExpr * CE)116*67e74705SXin Li void WalkAST::VisitCallExpr(CallExpr *CE) {
117*67e74705SXin Li // Get the callee.
118*67e74705SXin Li const FunctionDecl *FD = CE->getDirectCallee();
119*67e74705SXin Li
120*67e74705SXin Li if (!FD)
121*67e74705SXin Li return;
122*67e74705SXin Li
123*67e74705SXin Li // Get the name of the callee. If it's a builtin, strip off the prefix.
124*67e74705SXin Li IdentifierInfo *II = FD->getIdentifier();
125*67e74705SXin Li if (!II) // if no identifier, not a simple C function
126*67e74705SXin Li return;
127*67e74705SXin Li StringRef Name = II->getName();
128*67e74705SXin Li if (Name.startswith("__builtin_"))
129*67e74705SXin Li Name = Name.substr(10);
130*67e74705SXin Li
131*67e74705SXin Li // Set the evaluation function by switching on the callee name.
132*67e74705SXin Li FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
133*67e74705SXin Li .Case("gets", &WalkAST::checkCall_gets)
134*67e74705SXin Li .Case("getpw", &WalkAST::checkCall_getpw)
135*67e74705SXin Li .Case("mktemp", &WalkAST::checkCall_mktemp)
136*67e74705SXin Li .Case("mkstemp", &WalkAST::checkCall_mkstemp)
137*67e74705SXin Li .Case("mkdtemp", &WalkAST::checkCall_mkstemp)
138*67e74705SXin Li .Case("mkstemps", &WalkAST::checkCall_mkstemp)
139*67e74705SXin Li .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)
140*67e74705SXin Li .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat)
141*67e74705SXin Li .Case("drand48", &WalkAST::checkCall_rand)
142*67e74705SXin Li .Case("erand48", &WalkAST::checkCall_rand)
143*67e74705SXin Li .Case("jrand48", &WalkAST::checkCall_rand)
144*67e74705SXin Li .Case("lrand48", &WalkAST::checkCall_rand)
145*67e74705SXin Li .Case("mrand48", &WalkAST::checkCall_rand)
146*67e74705SXin Li .Case("nrand48", &WalkAST::checkCall_rand)
147*67e74705SXin Li .Case("lcong48", &WalkAST::checkCall_rand)
148*67e74705SXin Li .Case("rand", &WalkAST::checkCall_rand)
149*67e74705SXin Li .Case("rand_r", &WalkAST::checkCall_rand)
150*67e74705SXin Li .Case("random", &WalkAST::checkCall_random)
151*67e74705SXin Li .Case("vfork", &WalkAST::checkCall_vfork)
152*67e74705SXin Li .Default(nullptr);
153*67e74705SXin Li
154*67e74705SXin Li // If the callee isn't defined, it is not of security concern.
155*67e74705SXin Li // Check and evaluate the call.
156*67e74705SXin Li if (evalFunction)
157*67e74705SXin Li (this->*evalFunction)(CE, FD);
158*67e74705SXin Li
159*67e74705SXin Li // Recurse and check children.
160*67e74705SXin Li VisitChildren(CE);
161*67e74705SXin Li }
162*67e74705SXin Li
VisitCompoundStmt(CompoundStmt * S)163*67e74705SXin Li void WalkAST::VisitCompoundStmt(CompoundStmt *S) {
164*67e74705SXin Li for (Stmt *Child : S->children())
165*67e74705SXin Li if (Child) {
166*67e74705SXin Li if (CallExpr *CE = dyn_cast<CallExpr>(Child))
167*67e74705SXin Li checkUncheckedReturnValue(CE);
168*67e74705SXin Li Visit(Child);
169*67e74705SXin Li }
170*67e74705SXin Li }
171*67e74705SXin Li
VisitForStmt(ForStmt * FS)172*67e74705SXin Li void WalkAST::VisitForStmt(ForStmt *FS) {
173*67e74705SXin Li checkLoopConditionForFloat(FS);
174*67e74705SXin Li
175*67e74705SXin Li // Recurse and check children.
176*67e74705SXin Li VisitChildren(FS);
177*67e74705SXin Li }
178*67e74705SXin Li
179*67e74705SXin Li //===----------------------------------------------------------------------===//
180*67e74705SXin Li // Check: floating poing variable used as loop counter.
181*67e74705SXin Li // Originally: <rdar://problem/6336718>
182*67e74705SXin Li // Implements: CERT security coding advisory FLP-30.
183*67e74705SXin Li //===----------------------------------------------------------------------===//
184*67e74705SXin Li
185*67e74705SXin Li static const DeclRefExpr*
getIncrementedVar(const Expr * expr,const VarDecl * x,const VarDecl * y)186*67e74705SXin Li getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) {
187*67e74705SXin Li expr = expr->IgnoreParenCasts();
188*67e74705SXin Li
189*67e74705SXin Li if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) {
190*67e74705SXin Li if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
191*67e74705SXin Li B->getOpcode() == BO_Comma))
192*67e74705SXin Li return nullptr;
193*67e74705SXin Li
194*67e74705SXin Li if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y))
195*67e74705SXin Li return lhs;
196*67e74705SXin Li
197*67e74705SXin Li if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y))
198*67e74705SXin Li return rhs;
199*67e74705SXin Li
200*67e74705SXin Li return nullptr;
201*67e74705SXin Li }
202*67e74705SXin Li
203*67e74705SXin Li if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
204*67e74705SXin Li const NamedDecl *ND = DR->getDecl();
205*67e74705SXin Li return ND == x || ND == y ? DR : nullptr;
206*67e74705SXin Li }
207*67e74705SXin Li
208*67e74705SXin Li if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr))
209*67e74705SXin Li return U->isIncrementDecrementOp()
210*67e74705SXin Li ? getIncrementedVar(U->getSubExpr(), x, y) : nullptr;
211*67e74705SXin Li
212*67e74705SXin Li return nullptr;
213*67e74705SXin Li }
214*67e74705SXin Li
215*67e74705SXin Li /// CheckLoopConditionForFloat - This check looks for 'for' statements that
216*67e74705SXin Li /// use a floating point variable as a loop counter.
217*67e74705SXin Li /// CERT: FLP30-C, FLP30-CPP.
218*67e74705SXin Li ///
checkLoopConditionForFloat(const ForStmt * FS)219*67e74705SXin Li void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
220*67e74705SXin Li if (!filter.check_FloatLoopCounter)
221*67e74705SXin Li return;
222*67e74705SXin Li
223*67e74705SXin Li // Does the loop have a condition?
224*67e74705SXin Li const Expr *condition = FS->getCond();
225*67e74705SXin Li
226*67e74705SXin Li if (!condition)
227*67e74705SXin Li return;
228*67e74705SXin Li
229*67e74705SXin Li // Does the loop have an increment?
230*67e74705SXin Li const Expr *increment = FS->getInc();
231*67e74705SXin Li
232*67e74705SXin Li if (!increment)
233*67e74705SXin Li return;
234*67e74705SXin Li
235*67e74705SXin Li // Strip away '()' and casts.
236*67e74705SXin Li condition = condition->IgnoreParenCasts();
237*67e74705SXin Li increment = increment->IgnoreParenCasts();
238*67e74705SXin Li
239*67e74705SXin Li // Is the loop condition a comparison?
240*67e74705SXin Li const BinaryOperator *B = dyn_cast<BinaryOperator>(condition);
241*67e74705SXin Li
242*67e74705SXin Li if (!B)
243*67e74705SXin Li return;
244*67e74705SXin Li
245*67e74705SXin Li // Is this a comparison?
246*67e74705SXin Li if (!(B->isRelationalOp() || B->isEqualityOp()))
247*67e74705SXin Li return;
248*67e74705SXin Li
249*67e74705SXin Li // Are we comparing variables?
250*67e74705SXin Li const DeclRefExpr *drLHS =
251*67e74705SXin Li dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts());
252*67e74705SXin Li const DeclRefExpr *drRHS =
253*67e74705SXin Li dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts());
254*67e74705SXin Li
255*67e74705SXin Li // Does at least one of the variables have a floating point type?
256*67e74705SXin Li drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : nullptr;
257*67e74705SXin Li drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : nullptr;
258*67e74705SXin Li
259*67e74705SXin Li if (!drLHS && !drRHS)
260*67e74705SXin Li return;
261*67e74705SXin Li
262*67e74705SXin Li const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : nullptr;
263*67e74705SXin Li const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : nullptr;
264*67e74705SXin Li
265*67e74705SXin Li if (!vdLHS && !vdRHS)
266*67e74705SXin Li return;
267*67e74705SXin Li
268*67e74705SXin Li // Does either variable appear in increment?
269*67e74705SXin Li const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS);
270*67e74705SXin Li
271*67e74705SXin Li if (!drInc)
272*67e74705SXin Li return;
273*67e74705SXin Li
274*67e74705SXin Li // Emit the error. First figure out which DeclRefExpr in the condition
275*67e74705SXin Li // referenced the compared variable.
276*67e74705SXin Li assert(drInc->getDecl());
277*67e74705SXin Li const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS;
278*67e74705SXin Li
279*67e74705SXin Li SmallVector<SourceRange, 2> ranges;
280*67e74705SXin Li SmallString<256> sbuf;
281*67e74705SXin Li llvm::raw_svector_ostream os(sbuf);
282*67e74705SXin Li
283*67e74705SXin Li os << "Variable '" << drCond->getDecl()->getName()
284*67e74705SXin Li << "' with floating point type '" << drCond->getType().getAsString()
285*67e74705SXin Li << "' should not be used as a loop counter";
286*67e74705SXin Li
287*67e74705SXin Li ranges.push_back(drCond->getSourceRange());
288*67e74705SXin Li ranges.push_back(drInc->getSourceRange());
289*67e74705SXin Li
290*67e74705SXin Li const char *bugType = "Floating point variable used as loop counter";
291*67e74705SXin Li
292*67e74705SXin Li PathDiagnosticLocation FSLoc =
293*67e74705SXin Li PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);
294*67e74705SXin Li BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
295*67e74705SXin Li bugType, "Security", os.str(),
296*67e74705SXin Li FSLoc, ranges);
297*67e74705SXin Li }
298*67e74705SXin Li
299*67e74705SXin Li //===----------------------------------------------------------------------===//
300*67e74705SXin Li // Check: Any use of 'gets' is insecure.
301*67e74705SXin Li // Originally: <rdar://problem/6335715>
302*67e74705SXin Li // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)
303*67e74705SXin Li // CWE-242: Use of Inherently Dangerous Function
304*67e74705SXin Li //===----------------------------------------------------------------------===//
305*67e74705SXin Li
checkCall_gets(const CallExpr * CE,const FunctionDecl * FD)306*67e74705SXin Li void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {
307*67e74705SXin Li if (!filter.check_gets)
308*67e74705SXin Li return;
309*67e74705SXin Li
310*67e74705SXin Li const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
311*67e74705SXin Li if (!FPT)
312*67e74705SXin Li return;
313*67e74705SXin Li
314*67e74705SXin Li // Verify that the function takes a single argument.
315*67e74705SXin Li if (FPT->getNumParams() != 1)
316*67e74705SXin Li return;
317*67e74705SXin Li
318*67e74705SXin Li // Is the argument a 'char*'?
319*67e74705SXin Li const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
320*67e74705SXin Li if (!PT)
321*67e74705SXin Li return;
322*67e74705SXin Li
323*67e74705SXin Li if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
324*67e74705SXin Li return;
325*67e74705SXin Li
326*67e74705SXin Li // Issue a warning.
327*67e74705SXin Li PathDiagnosticLocation CELoc =
328*67e74705SXin Li PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
329*67e74705SXin Li BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
330*67e74705SXin Li "Potential buffer overflow in call to 'gets'",
331*67e74705SXin Li "Security",
332*67e74705SXin Li "Call to function 'gets' is extremely insecure as it can "
333*67e74705SXin Li "always result in a buffer overflow",
334*67e74705SXin Li CELoc, CE->getCallee()->getSourceRange());
335*67e74705SXin Li }
336*67e74705SXin Li
337*67e74705SXin Li //===----------------------------------------------------------------------===//
338*67e74705SXin Li // Check: Any use of 'getpwd' is insecure.
339*67e74705SXin Li // CWE-477: Use of Obsolete Functions
340*67e74705SXin Li //===----------------------------------------------------------------------===//
341*67e74705SXin Li
checkCall_getpw(const CallExpr * CE,const FunctionDecl * FD)342*67e74705SXin Li void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {
343*67e74705SXin Li if (!filter.check_getpw)
344*67e74705SXin Li return;
345*67e74705SXin Li
346*67e74705SXin Li const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
347*67e74705SXin Li if (!FPT)
348*67e74705SXin Li return;
349*67e74705SXin Li
350*67e74705SXin Li // Verify that the function takes two arguments.
351*67e74705SXin Li if (FPT->getNumParams() != 2)
352*67e74705SXin Li return;
353*67e74705SXin Li
354*67e74705SXin Li // Verify the first argument type is integer.
355*67e74705SXin Li if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType())
356*67e74705SXin Li return;
357*67e74705SXin Li
358*67e74705SXin Li // Verify the second argument type is char*.
359*67e74705SXin Li const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>();
360*67e74705SXin Li if (!PT)
361*67e74705SXin Li return;
362*67e74705SXin Li
363*67e74705SXin Li if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
364*67e74705SXin Li return;
365*67e74705SXin Li
366*67e74705SXin Li // Issue a warning.
367*67e74705SXin Li PathDiagnosticLocation CELoc =
368*67e74705SXin Li PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
369*67e74705SXin Li BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
370*67e74705SXin Li "Potential buffer overflow in call to 'getpw'",
371*67e74705SXin Li "Security",
372*67e74705SXin Li "The getpw() function is dangerous as it may overflow the "
373*67e74705SXin Li "provided buffer. It is obsoleted by getpwuid().",
374*67e74705SXin Li CELoc, CE->getCallee()->getSourceRange());
375*67e74705SXin Li }
376*67e74705SXin Li
377*67e74705SXin Li //===----------------------------------------------------------------------===//
378*67e74705SXin Li // Check: Any use of 'mktemp' is insecure. It is obsoleted by mkstemp().
379*67e74705SXin Li // CWE-377: Insecure Temporary File
380*67e74705SXin Li //===----------------------------------------------------------------------===//
381*67e74705SXin Li
checkCall_mktemp(const CallExpr * CE,const FunctionDecl * FD)382*67e74705SXin Li void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
383*67e74705SXin Li if (!filter.check_mktemp) {
384*67e74705SXin Li // Fall back to the security check of looking for enough 'X's in the
385*67e74705SXin Li // format string, since that is a less severe warning.
386*67e74705SXin Li checkCall_mkstemp(CE, FD);
387*67e74705SXin Li return;
388*67e74705SXin Li }
389*67e74705SXin Li
390*67e74705SXin Li const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
391*67e74705SXin Li if(!FPT)
392*67e74705SXin Li return;
393*67e74705SXin Li
394*67e74705SXin Li // Verify that the function takes a single argument.
395*67e74705SXin Li if (FPT->getNumParams() != 1)
396*67e74705SXin Li return;
397*67e74705SXin Li
398*67e74705SXin Li // Verify that the argument is Pointer Type.
399*67e74705SXin Li const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
400*67e74705SXin Li if (!PT)
401*67e74705SXin Li return;
402*67e74705SXin Li
403*67e74705SXin Li // Verify that the argument is a 'char*'.
404*67e74705SXin Li if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
405*67e74705SXin Li return;
406*67e74705SXin Li
407*67e74705SXin Li // Issue a warning.
408*67e74705SXin Li PathDiagnosticLocation CELoc =
409*67e74705SXin Li PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
410*67e74705SXin Li BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
411*67e74705SXin Li "Potential insecure temporary file in call 'mktemp'",
412*67e74705SXin Li "Security",
413*67e74705SXin Li "Call to function 'mktemp' is insecure as it always "
414*67e74705SXin Li "creates or uses insecure temporary file. Use 'mkstemp' "
415*67e74705SXin Li "instead",
416*67e74705SXin Li CELoc, CE->getCallee()->getSourceRange());
417*67e74705SXin Li }
418*67e74705SXin Li
419*67e74705SXin Li
420*67e74705SXin Li //===----------------------------------------------------------------------===//
421*67e74705SXin Li // Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.
422*67e74705SXin Li //===----------------------------------------------------------------------===//
423*67e74705SXin Li
checkCall_mkstemp(const CallExpr * CE,const FunctionDecl * FD)424*67e74705SXin Li void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
425*67e74705SXin Li if (!filter.check_mkstemp)
426*67e74705SXin Li return;
427*67e74705SXin Li
428*67e74705SXin Li StringRef Name = FD->getIdentifier()->getName();
429*67e74705SXin Li std::pair<signed, signed> ArgSuffix =
430*67e74705SXin Li llvm::StringSwitch<std::pair<signed, signed> >(Name)
431*67e74705SXin Li .Case("mktemp", std::make_pair(0,-1))
432*67e74705SXin Li .Case("mkstemp", std::make_pair(0,-1))
433*67e74705SXin Li .Case("mkdtemp", std::make_pair(0,-1))
434*67e74705SXin Li .Case("mkstemps", std::make_pair(0,1))
435*67e74705SXin Li .Default(std::make_pair(-1, -1));
436*67e74705SXin Li
437*67e74705SXin Li assert(ArgSuffix.first >= 0 && "Unsupported function");
438*67e74705SXin Li
439*67e74705SXin Li // Check if the number of arguments is consistent with out expectations.
440*67e74705SXin Li unsigned numArgs = CE->getNumArgs();
441*67e74705SXin Li if ((signed) numArgs <= ArgSuffix.first)
442*67e74705SXin Li return;
443*67e74705SXin Li
444*67e74705SXin Li const StringLiteral *strArg =
445*67e74705SXin Li dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first)
446*67e74705SXin Li ->IgnoreParenImpCasts());
447*67e74705SXin Li
448*67e74705SXin Li // Currently we only handle string literals. It is possible to do better,
449*67e74705SXin Li // either by looking at references to const variables, or by doing real
450*67e74705SXin Li // flow analysis.
451*67e74705SXin Li if (!strArg || strArg->getCharByteWidth() != 1)
452*67e74705SXin Li return;
453*67e74705SXin Li
454*67e74705SXin Li // Count the number of X's, taking into account a possible cutoff suffix.
455*67e74705SXin Li StringRef str = strArg->getString();
456*67e74705SXin Li unsigned numX = 0;
457*67e74705SXin Li unsigned n = str.size();
458*67e74705SXin Li
459*67e74705SXin Li // Take into account the suffix.
460*67e74705SXin Li unsigned suffix = 0;
461*67e74705SXin Li if (ArgSuffix.second >= 0) {
462*67e74705SXin Li const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second);
463*67e74705SXin Li llvm::APSInt Result;
464*67e74705SXin Li if (!suffixEx->EvaluateAsInt(Result, BR.getContext()))
465*67e74705SXin Li return;
466*67e74705SXin Li // FIXME: Issue a warning.
467*67e74705SXin Li if (Result.isNegative())
468*67e74705SXin Li return;
469*67e74705SXin Li suffix = (unsigned) Result.getZExtValue();
470*67e74705SXin Li n = (n > suffix) ? n - suffix : 0;
471*67e74705SXin Li }
472*67e74705SXin Li
473*67e74705SXin Li for (unsigned i = 0; i < n; ++i)
474*67e74705SXin Li if (str[i] == 'X') ++numX;
475*67e74705SXin Li
476*67e74705SXin Li if (numX >= 6)
477*67e74705SXin Li return;
478*67e74705SXin Li
479*67e74705SXin Li // Issue a warning.
480*67e74705SXin Li PathDiagnosticLocation CELoc =
481*67e74705SXin Li PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
482*67e74705SXin Li SmallString<512> buf;
483*67e74705SXin Li llvm::raw_svector_ostream out(buf);
484*67e74705SXin Li out << "Call to '" << Name << "' should have at least 6 'X's in the"
485*67e74705SXin Li " format string to be secure (" << numX << " 'X'";
486*67e74705SXin Li if (numX != 1)
487*67e74705SXin Li out << 's';
488*67e74705SXin Li out << " seen";
489*67e74705SXin Li if (suffix) {
490*67e74705SXin Li out << ", " << suffix << " character";
491*67e74705SXin Li if (suffix > 1)
492*67e74705SXin Li out << 's';
493*67e74705SXin Li out << " used as a suffix";
494*67e74705SXin Li }
495*67e74705SXin Li out << ')';
496*67e74705SXin Li BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
497*67e74705SXin Li "Insecure temporary file creation", "Security",
498*67e74705SXin Li out.str(), CELoc, strArg->getSourceRange());
499*67e74705SXin Li }
500*67e74705SXin Li
501*67e74705SXin Li //===----------------------------------------------------------------------===//
502*67e74705SXin Li // Check: Any use of 'strcpy' is insecure.
503*67e74705SXin Li //
504*67e74705SXin Li // CWE-119: Improper Restriction of Operations within
505*67e74705SXin Li // the Bounds of a Memory Buffer
506*67e74705SXin Li //===----------------------------------------------------------------------===//
checkCall_strcpy(const CallExpr * CE,const FunctionDecl * FD)507*67e74705SXin Li void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
508*67e74705SXin Li if (!filter.check_strcpy)
509*67e74705SXin Li return;
510*67e74705SXin Li
511*67e74705SXin Li if (!checkCall_strCommon(CE, FD))
512*67e74705SXin Li return;
513*67e74705SXin Li
514*67e74705SXin Li // Issue a warning.
515*67e74705SXin Li PathDiagnosticLocation CELoc =
516*67e74705SXin Li PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
517*67e74705SXin Li BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
518*67e74705SXin Li "Potential insecure memory buffer bounds restriction in "
519*67e74705SXin Li "call 'strcpy'",
520*67e74705SXin Li "Security",
521*67e74705SXin Li "Call to function 'strcpy' is insecure as it does not "
522*67e74705SXin Li "provide bounding of the memory buffer. Replace "
523*67e74705SXin Li "unbounded copy functions with analogous functions that "
524*67e74705SXin Li "support length arguments such as 'strlcpy'. CWE-119.",
525*67e74705SXin Li CELoc, CE->getCallee()->getSourceRange());
526*67e74705SXin Li }
527*67e74705SXin Li
528*67e74705SXin Li //===----------------------------------------------------------------------===//
529*67e74705SXin Li // Check: Any use of 'strcat' is insecure.
530*67e74705SXin Li //
531*67e74705SXin Li // CWE-119: Improper Restriction of Operations within
532*67e74705SXin Li // the Bounds of a Memory Buffer
533*67e74705SXin Li //===----------------------------------------------------------------------===//
checkCall_strcat(const CallExpr * CE,const FunctionDecl * FD)534*67e74705SXin Li void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
535*67e74705SXin Li if (!filter.check_strcpy)
536*67e74705SXin Li return;
537*67e74705SXin Li
538*67e74705SXin Li if (!checkCall_strCommon(CE, FD))
539*67e74705SXin Li return;
540*67e74705SXin Li
541*67e74705SXin Li // Issue a warning.
542*67e74705SXin Li PathDiagnosticLocation CELoc =
543*67e74705SXin Li PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
544*67e74705SXin Li BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
545*67e74705SXin Li "Potential insecure memory buffer bounds restriction in "
546*67e74705SXin Li "call 'strcat'",
547*67e74705SXin Li "Security",
548*67e74705SXin Li "Call to function 'strcat' is insecure as it does not "
549*67e74705SXin Li "provide bounding of the memory buffer. Replace "
550*67e74705SXin Li "unbounded copy functions with analogous functions that "
551*67e74705SXin Li "support length arguments such as 'strlcat'. CWE-119.",
552*67e74705SXin Li CELoc, CE->getCallee()->getSourceRange());
553*67e74705SXin Li }
554*67e74705SXin Li
555*67e74705SXin Li //===----------------------------------------------------------------------===//
556*67e74705SXin Li // Common check for str* functions with no bounds parameters.
557*67e74705SXin Li //===----------------------------------------------------------------------===//
checkCall_strCommon(const CallExpr * CE,const FunctionDecl * FD)558*67e74705SXin Li bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) {
559*67e74705SXin Li const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
560*67e74705SXin Li if (!FPT)
561*67e74705SXin Li return false;
562*67e74705SXin Li
563*67e74705SXin Li // Verify the function takes two arguments, three in the _chk version.
564*67e74705SXin Li int numArgs = FPT->getNumParams();
565*67e74705SXin Li if (numArgs != 2 && numArgs != 3)
566*67e74705SXin Li return false;
567*67e74705SXin Li
568*67e74705SXin Li // Verify the type for both arguments.
569*67e74705SXin Li for (int i = 0; i < 2; i++) {
570*67e74705SXin Li // Verify that the arguments are pointers.
571*67e74705SXin Li const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
572*67e74705SXin Li if (!PT)
573*67e74705SXin Li return false;
574*67e74705SXin Li
575*67e74705SXin Li // Verify that the argument is a 'char*'.
576*67e74705SXin Li if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
577*67e74705SXin Li return false;
578*67e74705SXin Li }
579*67e74705SXin Li
580*67e74705SXin Li return true;
581*67e74705SXin Li }
582*67e74705SXin Li
583*67e74705SXin Li //===----------------------------------------------------------------------===//
584*67e74705SXin Li // Check: Linear congruent random number generators should not be used
585*67e74705SXin Li // Originally: <rdar://problem/63371000>
586*67e74705SXin Li // CWE-338: Use of cryptographically weak prng
587*67e74705SXin Li //===----------------------------------------------------------------------===//
588*67e74705SXin Li
checkCall_rand(const CallExpr * CE,const FunctionDecl * FD)589*67e74705SXin Li void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
590*67e74705SXin Li if (!filter.check_rand || !CheckRand)
591*67e74705SXin Li return;
592*67e74705SXin Li
593*67e74705SXin Li const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
594*67e74705SXin Li if (!FTP)
595*67e74705SXin Li return;
596*67e74705SXin Li
597*67e74705SXin Li if (FTP->getNumParams() == 1) {
598*67e74705SXin Li // Is the argument an 'unsigned short *'?
599*67e74705SXin Li // (Actually any integer type is allowed.)
600*67e74705SXin Li const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>();
601*67e74705SXin Li if (!PT)
602*67e74705SXin Li return;
603*67e74705SXin Li
604*67e74705SXin Li if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())
605*67e74705SXin Li return;
606*67e74705SXin Li } else if (FTP->getNumParams() != 0)
607*67e74705SXin Li return;
608*67e74705SXin Li
609*67e74705SXin Li // Issue a warning.
610*67e74705SXin Li SmallString<256> buf1;
611*67e74705SXin Li llvm::raw_svector_ostream os1(buf1);
612*67e74705SXin Li os1 << '\'' << *FD << "' is a poor random number generator";
613*67e74705SXin Li
614*67e74705SXin Li SmallString<256> buf2;
615*67e74705SXin Li llvm::raw_svector_ostream os2(buf2);
616*67e74705SXin Li os2 << "Function '" << *FD
617*67e74705SXin Li << "' is obsolete because it implements a poor random number generator."
618*67e74705SXin Li << " Use 'arc4random' instead";
619*67e74705SXin Li
620*67e74705SXin Li PathDiagnosticLocation CELoc =
621*67e74705SXin Li PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
622*67e74705SXin Li BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
623*67e74705SXin Li "Security", os2.str(), CELoc,
624*67e74705SXin Li CE->getCallee()->getSourceRange());
625*67e74705SXin Li }
626*67e74705SXin Li
627*67e74705SXin Li //===----------------------------------------------------------------------===//
628*67e74705SXin Li // Check: 'random' should not be used
629*67e74705SXin Li // Originally: <rdar://problem/63371000>
630*67e74705SXin Li //===----------------------------------------------------------------------===//
631*67e74705SXin Li
checkCall_random(const CallExpr * CE,const FunctionDecl * FD)632*67e74705SXin Li void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {
633*67e74705SXin Li if (!CheckRand || !filter.check_rand)
634*67e74705SXin Li return;
635*67e74705SXin Li
636*67e74705SXin Li const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
637*67e74705SXin Li if (!FTP)
638*67e74705SXin Li return;
639*67e74705SXin Li
640*67e74705SXin Li // Verify that the function takes no argument.
641*67e74705SXin Li if (FTP->getNumParams() != 0)
642*67e74705SXin Li return;
643*67e74705SXin Li
644*67e74705SXin Li // Issue a warning.
645*67e74705SXin Li PathDiagnosticLocation CELoc =
646*67e74705SXin Li PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
647*67e74705SXin Li BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
648*67e74705SXin Li "'random' is not a secure random number generator",
649*67e74705SXin Li "Security",
650*67e74705SXin Li "The 'random' function produces a sequence of values that "
651*67e74705SXin Li "an adversary may be able to predict. Use 'arc4random' "
652*67e74705SXin Li "instead", CELoc, CE->getCallee()->getSourceRange());
653*67e74705SXin Li }
654*67e74705SXin Li
655*67e74705SXin Li //===----------------------------------------------------------------------===//
656*67e74705SXin Li // Check: 'vfork' should not be used.
657*67e74705SXin Li // POS33-C: Do not use vfork().
658*67e74705SXin Li //===----------------------------------------------------------------------===//
659*67e74705SXin Li
checkCall_vfork(const CallExpr * CE,const FunctionDecl * FD)660*67e74705SXin Li void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
661*67e74705SXin Li if (!filter.check_vfork)
662*67e74705SXin Li return;
663*67e74705SXin Li
664*67e74705SXin Li // All calls to vfork() are insecure, issue a warning.
665*67e74705SXin Li PathDiagnosticLocation CELoc =
666*67e74705SXin Li PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
667*67e74705SXin Li BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
668*67e74705SXin Li "Potential insecure implementation-specific behavior in "
669*67e74705SXin Li "call 'vfork'",
670*67e74705SXin Li "Security",
671*67e74705SXin Li "Call to function 'vfork' is insecure as it can lead to "
672*67e74705SXin Li "denial of service situations in the parent process. "
673*67e74705SXin Li "Replace calls to vfork with calls to the safer "
674*67e74705SXin Li "'posix_spawn' function",
675*67e74705SXin Li CELoc, CE->getCallee()->getSourceRange());
676*67e74705SXin Li }
677*67e74705SXin Li
678*67e74705SXin Li //===----------------------------------------------------------------------===//
679*67e74705SXin Li // Check: Should check whether privileges are dropped successfully.
680*67e74705SXin Li // Originally: <rdar://problem/6337132>
681*67e74705SXin Li //===----------------------------------------------------------------------===//
682*67e74705SXin Li
checkUncheckedReturnValue(CallExpr * CE)683*67e74705SXin Li void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
684*67e74705SXin Li if (!filter.check_UncheckedReturn)
685*67e74705SXin Li return;
686*67e74705SXin Li
687*67e74705SXin Li const FunctionDecl *FD = CE->getDirectCallee();
688*67e74705SXin Li if (!FD)
689*67e74705SXin Li return;
690*67e74705SXin Li
691*67e74705SXin Li if (II_setid[0] == nullptr) {
692*67e74705SXin Li static const char * const identifiers[num_setids] = {
693*67e74705SXin Li "setuid", "setgid", "seteuid", "setegid",
694*67e74705SXin Li "setreuid", "setregid"
695*67e74705SXin Li };
696*67e74705SXin Li
697*67e74705SXin Li for (size_t i = 0; i < num_setids; i++)
698*67e74705SXin Li II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
699*67e74705SXin Li }
700*67e74705SXin Li
701*67e74705SXin Li const IdentifierInfo *id = FD->getIdentifier();
702*67e74705SXin Li size_t identifierid;
703*67e74705SXin Li
704*67e74705SXin Li for (identifierid = 0; identifierid < num_setids; identifierid++)
705*67e74705SXin Li if (id == II_setid[identifierid])
706*67e74705SXin Li break;
707*67e74705SXin Li
708*67e74705SXin Li if (identifierid >= num_setids)
709*67e74705SXin Li return;
710*67e74705SXin Li
711*67e74705SXin Li const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
712*67e74705SXin Li if (!FTP)
713*67e74705SXin Li return;
714*67e74705SXin Li
715*67e74705SXin Li // Verify that the function takes one or two arguments (depending on
716*67e74705SXin Li // the function).
717*67e74705SXin Li if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2))
718*67e74705SXin Li return;
719*67e74705SXin Li
720*67e74705SXin Li // The arguments must be integers.
721*67e74705SXin Li for (unsigned i = 0; i < FTP->getNumParams(); i++)
722*67e74705SXin Li if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType())
723*67e74705SXin Li return;
724*67e74705SXin Li
725*67e74705SXin Li // Issue a warning.
726*67e74705SXin Li SmallString<256> buf1;
727*67e74705SXin Li llvm::raw_svector_ostream os1(buf1);
728*67e74705SXin Li os1 << "Return value is not checked in call to '" << *FD << '\'';
729*67e74705SXin Li
730*67e74705SXin Li SmallString<256> buf2;
731*67e74705SXin Li llvm::raw_svector_ostream os2(buf2);
732*67e74705SXin Li os2 << "The return value from the call to '" << *FD
733*67e74705SXin Li << "' is not checked. If an error occurs in '" << *FD
734*67e74705SXin Li << "', the following code may execute with unexpected privileges";
735*67e74705SXin Li
736*67e74705SXin Li PathDiagnosticLocation CELoc =
737*67e74705SXin Li PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
738*67e74705SXin Li BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
739*67e74705SXin Li "Security", os2.str(), CELoc,
740*67e74705SXin Li CE->getCallee()->getSourceRange());
741*67e74705SXin Li }
742*67e74705SXin Li
743*67e74705SXin Li //===----------------------------------------------------------------------===//
744*67e74705SXin Li // SecuritySyntaxChecker
745*67e74705SXin Li //===----------------------------------------------------------------------===//
746*67e74705SXin Li
747*67e74705SXin Li namespace {
748*67e74705SXin Li class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
749*67e74705SXin Li public:
750*67e74705SXin Li ChecksFilter filter;
751*67e74705SXin Li
checkASTCodeBody(const Decl * D,AnalysisManager & mgr,BugReporter & BR) const752*67e74705SXin Li void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
753*67e74705SXin Li BugReporter &BR) const {
754*67e74705SXin Li WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
755*67e74705SXin Li walker.Visit(D->getBody());
756*67e74705SXin Li }
757*67e74705SXin Li };
758*67e74705SXin Li }
759*67e74705SXin Li
760*67e74705SXin Li #define REGISTER_CHECKER(name) \
761*67e74705SXin Li void ento::register##name(CheckerManager &mgr) { \
762*67e74705SXin Li SecuritySyntaxChecker *checker = \
763*67e74705SXin Li mgr.registerChecker<SecuritySyntaxChecker>(); \
764*67e74705SXin Li checker->filter.check_##name = true; \
765*67e74705SXin Li checker->filter.checkName_##name = mgr.getCurrentCheckName(); \
766*67e74705SXin Li }
767*67e74705SXin Li
768*67e74705SXin Li REGISTER_CHECKER(gets)
769*67e74705SXin Li REGISTER_CHECKER(getpw)
770*67e74705SXin Li REGISTER_CHECKER(mkstemp)
771*67e74705SXin Li REGISTER_CHECKER(mktemp)
772*67e74705SXin Li REGISTER_CHECKER(strcpy)
773*67e74705SXin Li REGISTER_CHECKER(rand)
774*67e74705SXin Li REGISTER_CHECKER(vfork)
775*67e74705SXin Li REGISTER_CHECKER(FloatLoopCounter)
776*67e74705SXin Li REGISTER_CHECKER(UncheckedReturn)
777*67e74705SXin Li
778*67e74705SXin Li
779