1*0b57cec5SDimitry Andric //==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric //  This file defines a set of flow-insensitive security checks.
10*0b57cec5SDimitry Andric //
11*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
12*0b57cec5SDimitry Andric 
13*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14*0b57cec5SDimitry Andric #include "clang/AST/StmtVisitor.h"
15*0b57cec5SDimitry Andric #include "clang/Analysis/AnalysisDeclContext.h"
16*0b57cec5SDimitry Andric #include "clang/Basic/TargetInfo.h"
17*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
18*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
19*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
20*0b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
21*0b57cec5SDimitry Andric #include "llvm/ADT/StringSwitch.h"
22*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
23*0b57cec5SDimitry Andric 
24*0b57cec5SDimitry Andric using namespace clang;
25*0b57cec5SDimitry Andric using namespace ento;
26*0b57cec5SDimitry Andric 
isArc4RandomAvailable(const ASTContext & Ctx)27*0b57cec5SDimitry Andric static bool isArc4RandomAvailable(const ASTContext &Ctx) {
28*0b57cec5SDimitry Andric   const llvm::Triple &T = Ctx.getTargetInfo().getTriple();
29*0b57cec5SDimitry Andric   return T.getVendor() == llvm::Triple::Apple ||
30*0b57cec5SDimitry Andric          T.getOS() == llvm::Triple::CloudABI ||
31*0b57cec5SDimitry Andric          T.isOSFreeBSD() ||
32*0b57cec5SDimitry Andric          T.isOSNetBSD() ||
33*0b57cec5SDimitry Andric          T.isOSOpenBSD() ||
34*0b57cec5SDimitry Andric          T.isOSDragonFly();
35*0b57cec5SDimitry Andric }
36*0b57cec5SDimitry Andric 
37*0b57cec5SDimitry Andric namespace {
38*0b57cec5SDimitry Andric struct ChecksFilter {
39*0b57cec5SDimitry Andric   DefaultBool check_bcmp;
40*0b57cec5SDimitry Andric   DefaultBool check_bcopy;
41*0b57cec5SDimitry Andric   DefaultBool check_bzero;
42*0b57cec5SDimitry Andric   DefaultBool check_gets;
43*0b57cec5SDimitry Andric   DefaultBool check_getpw;
44*0b57cec5SDimitry Andric   DefaultBool check_mktemp;
45*0b57cec5SDimitry Andric   DefaultBool check_mkstemp;
46*0b57cec5SDimitry Andric   DefaultBool check_strcpy;
47*0b57cec5SDimitry Andric   DefaultBool check_DeprecatedOrUnsafeBufferHandling;
48*0b57cec5SDimitry Andric   DefaultBool check_rand;
49*0b57cec5SDimitry Andric   DefaultBool check_vfork;
50*0b57cec5SDimitry Andric   DefaultBool check_FloatLoopCounter;
51*0b57cec5SDimitry Andric   DefaultBool check_UncheckedReturn;
52*0b57cec5SDimitry Andric   DefaultBool check_decodeValueOfObjCType;
53*0b57cec5SDimitry Andric 
54*0b57cec5SDimitry Andric   CheckerNameRef checkName_bcmp;
55*0b57cec5SDimitry Andric   CheckerNameRef checkName_bcopy;
56*0b57cec5SDimitry Andric   CheckerNameRef checkName_bzero;
57*0b57cec5SDimitry Andric   CheckerNameRef checkName_gets;
58*0b57cec5SDimitry Andric   CheckerNameRef checkName_getpw;
59*0b57cec5SDimitry Andric   CheckerNameRef checkName_mktemp;
60*0b57cec5SDimitry Andric   CheckerNameRef checkName_mkstemp;
61*0b57cec5SDimitry Andric   CheckerNameRef checkName_strcpy;
62*0b57cec5SDimitry Andric   CheckerNameRef checkName_DeprecatedOrUnsafeBufferHandling;
63*0b57cec5SDimitry Andric   CheckerNameRef checkName_rand;
64*0b57cec5SDimitry Andric   CheckerNameRef checkName_vfork;
65*0b57cec5SDimitry Andric   CheckerNameRef checkName_FloatLoopCounter;
66*0b57cec5SDimitry Andric   CheckerNameRef checkName_UncheckedReturn;
67*0b57cec5SDimitry Andric   CheckerNameRef checkName_decodeValueOfObjCType;
68*0b57cec5SDimitry Andric };
69*0b57cec5SDimitry Andric 
70*0b57cec5SDimitry Andric class WalkAST : public StmtVisitor<WalkAST> {
71*0b57cec5SDimitry Andric   BugReporter &BR;
72*0b57cec5SDimitry Andric   AnalysisDeclContext* AC;
73*0b57cec5SDimitry Andric   enum { num_setids = 6 };
74*0b57cec5SDimitry Andric   IdentifierInfo *II_setid[num_setids];
75*0b57cec5SDimitry Andric 
76*0b57cec5SDimitry Andric   const bool CheckRand;
77*0b57cec5SDimitry Andric   const ChecksFilter &filter;
78*0b57cec5SDimitry Andric 
79*0b57cec5SDimitry Andric public:
WalkAST(BugReporter & br,AnalysisDeclContext * ac,const ChecksFilter & f)80*0b57cec5SDimitry Andric   WalkAST(BugReporter &br, AnalysisDeclContext* ac,
81*0b57cec5SDimitry Andric           const ChecksFilter &f)
82*0b57cec5SDimitry Andric   : BR(br), AC(ac), II_setid(),
83*0b57cec5SDimitry Andric     CheckRand(isArc4RandomAvailable(BR.getContext())),
84*0b57cec5SDimitry Andric     filter(f) {}
85*0b57cec5SDimitry Andric 
86*0b57cec5SDimitry Andric   // Statement visitor methods.
87*0b57cec5SDimitry Andric   void VisitCallExpr(CallExpr *CE);
88*0b57cec5SDimitry Andric   void VisitObjCMessageExpr(ObjCMessageExpr *CE);
89*0b57cec5SDimitry Andric   void VisitForStmt(ForStmt *S);
90*0b57cec5SDimitry Andric   void VisitCompoundStmt (CompoundStmt *S);
VisitStmt(Stmt * S)91*0b57cec5SDimitry Andric   void VisitStmt(Stmt *S) { VisitChildren(S); }
92*0b57cec5SDimitry Andric 
93*0b57cec5SDimitry Andric   void VisitChildren(Stmt *S);
94*0b57cec5SDimitry Andric 
95*0b57cec5SDimitry Andric   // Helpers.
96*0b57cec5SDimitry Andric   bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD);
97*0b57cec5SDimitry Andric 
98*0b57cec5SDimitry Andric   typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *);
99*0b57cec5SDimitry Andric   typedef void (WalkAST::*MsgCheck)(const ObjCMessageExpr *);
100*0b57cec5SDimitry Andric 
101*0b57cec5SDimitry Andric   // Checker-specific methods.
102*0b57cec5SDimitry Andric   void checkLoopConditionForFloat(const ForStmt *FS);
103*0b57cec5SDimitry Andric   void checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD);
104*0b57cec5SDimitry Andric   void checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD);
105*0b57cec5SDimitry Andric   void checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD);
106*0b57cec5SDimitry Andric   void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD);
107*0b57cec5SDimitry Andric   void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD);
108*0b57cec5SDimitry Andric   void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD);
109*0b57cec5SDimitry Andric   void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD);
110*0b57cec5SDimitry Andric   void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD);
111*0b57cec5SDimitry Andric   void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD);
112*0b57cec5SDimitry Andric   void checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
113*0b57cec5SDimitry Andric                                              const FunctionDecl *FD);
114*0b57cec5SDimitry Andric   void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);
115*0b57cec5SDimitry Andric   void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);
116*0b57cec5SDimitry Andric   void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD);
117*0b57cec5SDimitry Andric   void checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME);
118*0b57cec5SDimitry Andric   void checkUncheckedReturnValue(CallExpr *CE);
119*0b57cec5SDimitry Andric };
120*0b57cec5SDimitry Andric } // end anonymous namespace
121*0b57cec5SDimitry Andric 
122*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
123*0b57cec5SDimitry Andric // AST walking.
124*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
125*0b57cec5SDimitry Andric 
VisitChildren(Stmt * S)126*0b57cec5SDimitry Andric void WalkAST::VisitChildren(Stmt *S) {
127*0b57cec5SDimitry Andric   for (Stmt *Child : S->children())
128*0b57cec5SDimitry Andric     if (Child)
129*0b57cec5SDimitry Andric       Visit(Child);
130*0b57cec5SDimitry Andric }
131*0b57cec5SDimitry Andric 
VisitCallExpr(CallExpr * CE)132*0b57cec5SDimitry Andric void WalkAST::VisitCallExpr(CallExpr *CE) {
133*0b57cec5SDimitry Andric   // Get the callee.
134*0b57cec5SDimitry Andric   const FunctionDecl *FD = CE->getDirectCallee();
135*0b57cec5SDimitry Andric 
136*0b57cec5SDimitry Andric   if (!FD)
137*0b57cec5SDimitry Andric     return;
138*0b57cec5SDimitry Andric 
139*0b57cec5SDimitry Andric   // Get the name of the callee. If it's a builtin, strip off the prefix.
140*0b57cec5SDimitry Andric   IdentifierInfo *II = FD->getIdentifier();
141*0b57cec5SDimitry Andric   if (!II)   // if no identifier, not a simple C function
142*0b57cec5SDimitry Andric     return;
143*0b57cec5SDimitry Andric   StringRef Name = II->getName();
144*0b57cec5SDimitry Andric   if (Name.startswith("__builtin_"))
145*0b57cec5SDimitry Andric     Name = Name.substr(10);
146*0b57cec5SDimitry Andric 
147*0b57cec5SDimitry Andric   // Set the evaluation function by switching on the callee name.
148*0b57cec5SDimitry Andric   FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
149*0b57cec5SDimitry Andric     .Case("bcmp", &WalkAST::checkCall_bcmp)
150*0b57cec5SDimitry Andric     .Case("bcopy", &WalkAST::checkCall_bcopy)
151*0b57cec5SDimitry Andric     .Case("bzero", &WalkAST::checkCall_bzero)
152*0b57cec5SDimitry Andric     .Case("gets", &WalkAST::checkCall_gets)
153*0b57cec5SDimitry Andric     .Case("getpw", &WalkAST::checkCall_getpw)
154*0b57cec5SDimitry Andric     .Case("mktemp", &WalkAST::checkCall_mktemp)
155*0b57cec5SDimitry Andric     .Case("mkstemp", &WalkAST::checkCall_mkstemp)
156*0b57cec5SDimitry Andric     .Case("mkdtemp", &WalkAST::checkCall_mkstemp)
157*0b57cec5SDimitry Andric     .Case("mkstemps", &WalkAST::checkCall_mkstemp)
158*0b57cec5SDimitry Andric     .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)
159*0b57cec5SDimitry Andric     .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat)
160*0b57cec5SDimitry Andric     .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf",
161*0b57cec5SDimitry Andric            "vscanf", "vwscanf", "vfscanf", "vfwscanf",
162*0b57cec5SDimitry Andric            &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
163*0b57cec5SDimitry Andric     .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf",
164*0b57cec5SDimitry Andric            "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove",
165*0b57cec5SDimitry Andric            &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
166*0b57cec5SDimitry Andric     .Cases("strncpy", "strncat", "memset",
167*0b57cec5SDimitry Andric            &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
168*0b57cec5SDimitry Andric     .Case("drand48", &WalkAST::checkCall_rand)
169*0b57cec5SDimitry Andric     .Case("erand48", &WalkAST::checkCall_rand)
170*0b57cec5SDimitry Andric     .Case("jrand48", &WalkAST::checkCall_rand)
171*0b57cec5SDimitry Andric     .Case("lrand48", &WalkAST::checkCall_rand)
172*0b57cec5SDimitry Andric     .Case("mrand48", &WalkAST::checkCall_rand)
173*0b57cec5SDimitry Andric     .Case("nrand48", &WalkAST::checkCall_rand)
174*0b57cec5SDimitry Andric     .Case("lcong48", &WalkAST::checkCall_rand)
175*0b57cec5SDimitry Andric     .Case("rand", &WalkAST::checkCall_rand)
176*0b57cec5SDimitry Andric     .Case("rand_r", &WalkAST::checkCall_rand)
177*0b57cec5SDimitry Andric     .Case("random", &WalkAST::checkCall_random)
178*0b57cec5SDimitry Andric     .Case("vfork", &WalkAST::checkCall_vfork)
179*0b57cec5SDimitry Andric     .Default(nullptr);
180*0b57cec5SDimitry Andric 
181*0b57cec5SDimitry Andric   // If the callee isn't defined, it is not of security concern.
182*0b57cec5SDimitry Andric   // Check and evaluate the call.
183*0b57cec5SDimitry Andric   if (evalFunction)
184*0b57cec5SDimitry Andric     (this->*evalFunction)(CE, FD);
185*0b57cec5SDimitry Andric 
186*0b57cec5SDimitry Andric   // Recurse and check children.
187*0b57cec5SDimitry Andric   VisitChildren(CE);
188*0b57cec5SDimitry Andric }
189*0b57cec5SDimitry Andric 
VisitObjCMessageExpr(ObjCMessageExpr * ME)190*0b57cec5SDimitry Andric void WalkAST::VisitObjCMessageExpr(ObjCMessageExpr *ME) {
191*0b57cec5SDimitry Andric   MsgCheck evalFunction =
192*0b57cec5SDimitry Andric       llvm::StringSwitch<MsgCheck>(ME->getSelector().getAsString())
193*0b57cec5SDimitry Andric           .Case("decodeValueOfObjCType:at:",
194*0b57cec5SDimitry Andric                 &WalkAST::checkMsg_decodeValueOfObjCType)
195*0b57cec5SDimitry Andric           .Default(nullptr);
196*0b57cec5SDimitry Andric 
197*0b57cec5SDimitry Andric   if (evalFunction)
198*0b57cec5SDimitry Andric     (this->*evalFunction)(ME);
199*0b57cec5SDimitry Andric 
200*0b57cec5SDimitry Andric   // Recurse and check children.
201*0b57cec5SDimitry Andric   VisitChildren(ME);
202*0b57cec5SDimitry Andric }
203*0b57cec5SDimitry Andric 
VisitCompoundStmt(CompoundStmt * S)204*0b57cec5SDimitry Andric void WalkAST::VisitCompoundStmt(CompoundStmt *S) {
205*0b57cec5SDimitry Andric   for (Stmt *Child : S->children())
206*0b57cec5SDimitry Andric     if (Child) {
207*0b57cec5SDimitry Andric       if (CallExpr *CE = dyn_cast<CallExpr>(Child))
208*0b57cec5SDimitry Andric         checkUncheckedReturnValue(CE);
209*0b57cec5SDimitry Andric       Visit(Child);
210*0b57cec5SDimitry Andric     }
211*0b57cec5SDimitry Andric }
212*0b57cec5SDimitry Andric 
VisitForStmt(ForStmt * FS)213*0b57cec5SDimitry Andric void WalkAST::VisitForStmt(ForStmt *FS) {
214*0b57cec5SDimitry Andric   checkLoopConditionForFloat(FS);
215*0b57cec5SDimitry Andric 
216*0b57cec5SDimitry Andric   // Recurse and check children.
217*0b57cec5SDimitry Andric   VisitChildren(FS);
218*0b57cec5SDimitry Andric }
219*0b57cec5SDimitry Andric 
220*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
221*0b57cec5SDimitry Andric // Check: floating point variable used as loop counter.
222*0b57cec5SDimitry Andric // Originally: <rdar://problem/6336718>
223*0b57cec5SDimitry Andric // Implements: CERT security coding advisory FLP-30.
224*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
225*0b57cec5SDimitry Andric 
226*0b57cec5SDimitry Andric // Returns either 'x' or 'y', depending on which one of them is incremented
227*0b57cec5SDimitry Andric // in 'expr', or nullptr if none of them is incremented.
228*0b57cec5SDimitry Andric static const DeclRefExpr*
getIncrementedVar(const Expr * expr,const VarDecl * x,const VarDecl * y)229*0b57cec5SDimitry Andric getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) {
230*0b57cec5SDimitry Andric   expr = expr->IgnoreParenCasts();
231*0b57cec5SDimitry Andric 
232*0b57cec5SDimitry Andric   if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) {
233*0b57cec5SDimitry Andric     if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
234*0b57cec5SDimitry Andric           B->getOpcode() == BO_Comma))
235*0b57cec5SDimitry Andric       return nullptr;
236*0b57cec5SDimitry Andric 
237*0b57cec5SDimitry Andric     if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y))
238*0b57cec5SDimitry Andric       return lhs;
239*0b57cec5SDimitry Andric 
240*0b57cec5SDimitry Andric     if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y))
241*0b57cec5SDimitry Andric       return rhs;
242*0b57cec5SDimitry Andric 
243*0b57cec5SDimitry Andric     return nullptr;
244*0b57cec5SDimitry Andric   }
245*0b57cec5SDimitry Andric 
246*0b57cec5SDimitry Andric   if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
247*0b57cec5SDimitry Andric     const NamedDecl *ND = DR->getDecl();
248*0b57cec5SDimitry Andric     return ND == x || ND == y ? DR : nullptr;
249*0b57cec5SDimitry Andric   }
250*0b57cec5SDimitry Andric 
251*0b57cec5SDimitry Andric   if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr))
252*0b57cec5SDimitry Andric     return U->isIncrementDecrementOp()
253*0b57cec5SDimitry Andric       ? getIncrementedVar(U->getSubExpr(), x, y) : nullptr;
254*0b57cec5SDimitry Andric 
255*0b57cec5SDimitry Andric   return nullptr;
256*0b57cec5SDimitry Andric }
257*0b57cec5SDimitry Andric 
258*0b57cec5SDimitry Andric /// CheckLoopConditionForFloat - This check looks for 'for' statements that
259*0b57cec5SDimitry Andric ///  use a floating point variable as a loop counter.
260*0b57cec5SDimitry Andric ///  CERT: FLP30-C, FLP30-CPP.
261*0b57cec5SDimitry Andric ///
checkLoopConditionForFloat(const ForStmt * FS)262*0b57cec5SDimitry Andric void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
263*0b57cec5SDimitry Andric   if (!filter.check_FloatLoopCounter)
264*0b57cec5SDimitry Andric     return;
265*0b57cec5SDimitry Andric 
266*0b57cec5SDimitry Andric   // Does the loop have a condition?
267*0b57cec5SDimitry Andric   const Expr *condition = FS->getCond();
268*0b57cec5SDimitry Andric 
269*0b57cec5SDimitry Andric   if (!condition)
270*0b57cec5SDimitry Andric     return;
271*0b57cec5SDimitry Andric 
272*0b57cec5SDimitry Andric   // Does the loop have an increment?
273*0b57cec5SDimitry Andric   const Expr *increment = FS->getInc();
274*0b57cec5SDimitry Andric 
275*0b57cec5SDimitry Andric   if (!increment)
276*0b57cec5SDimitry Andric     return;
277*0b57cec5SDimitry Andric 
278*0b57cec5SDimitry Andric   // Strip away '()' and casts.
279*0b57cec5SDimitry Andric   condition = condition->IgnoreParenCasts();
280*0b57cec5SDimitry Andric   increment = increment->IgnoreParenCasts();
281*0b57cec5SDimitry Andric 
282*0b57cec5SDimitry Andric   // Is the loop condition a comparison?
283*0b57cec5SDimitry Andric   const BinaryOperator *B = dyn_cast<BinaryOperator>(condition);
284*0b57cec5SDimitry Andric 
285*0b57cec5SDimitry Andric   if (!B)
286*0b57cec5SDimitry Andric     return;
287*0b57cec5SDimitry Andric 
288*0b57cec5SDimitry Andric   // Is this a comparison?
289*0b57cec5SDimitry Andric   if (!(B->isRelationalOp() || B->isEqualityOp()))
290*0b57cec5SDimitry Andric     return;
291*0b57cec5SDimitry Andric 
292*0b57cec5SDimitry Andric   // Are we comparing variables?
293*0b57cec5SDimitry Andric   const DeclRefExpr *drLHS =
294*0b57cec5SDimitry Andric     dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts());
295*0b57cec5SDimitry Andric   const DeclRefExpr *drRHS =
296*0b57cec5SDimitry Andric     dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts());
297*0b57cec5SDimitry Andric 
298*0b57cec5SDimitry Andric   // Does at least one of the variables have a floating point type?
299*0b57cec5SDimitry Andric   drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : nullptr;
300*0b57cec5SDimitry Andric   drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : nullptr;
301*0b57cec5SDimitry Andric 
302*0b57cec5SDimitry Andric   if (!drLHS && !drRHS)
303*0b57cec5SDimitry Andric     return;
304*0b57cec5SDimitry Andric 
305*0b57cec5SDimitry Andric   const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : nullptr;
306*0b57cec5SDimitry Andric   const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : nullptr;
307*0b57cec5SDimitry Andric 
308*0b57cec5SDimitry Andric   if (!vdLHS && !vdRHS)
309*0b57cec5SDimitry Andric     return;
310*0b57cec5SDimitry Andric 
311*0b57cec5SDimitry Andric   // Does either variable appear in increment?
312*0b57cec5SDimitry Andric   const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS);
313*0b57cec5SDimitry Andric   if (!drInc)
314*0b57cec5SDimitry Andric     return;
315*0b57cec5SDimitry Andric 
316*0b57cec5SDimitry Andric   const VarDecl *vdInc = cast<VarDecl>(drInc->getDecl());
317*0b57cec5SDimitry Andric   assert(vdInc && (vdInc == vdLHS || vdInc == vdRHS));
318*0b57cec5SDimitry Andric 
319*0b57cec5SDimitry Andric   // Emit the error.  First figure out which DeclRefExpr in the condition
320*0b57cec5SDimitry Andric   // referenced the compared variable.
321*0b57cec5SDimitry Andric   const DeclRefExpr *drCond = vdLHS == vdInc ? drLHS : drRHS;
322*0b57cec5SDimitry Andric 
323*0b57cec5SDimitry Andric   SmallVector<SourceRange, 2> ranges;
324*0b57cec5SDimitry Andric   SmallString<256> sbuf;
325*0b57cec5SDimitry Andric   llvm::raw_svector_ostream os(sbuf);
326*0b57cec5SDimitry Andric 
327*0b57cec5SDimitry Andric   os << "Variable '" << drCond->getDecl()->getName()
328*0b57cec5SDimitry Andric      << "' with floating point type '" << drCond->getType().getAsString()
329*0b57cec5SDimitry Andric      << "' should not be used as a loop counter";
330*0b57cec5SDimitry Andric 
331*0b57cec5SDimitry Andric   ranges.push_back(drCond->getSourceRange());
332*0b57cec5SDimitry Andric   ranges.push_back(drInc->getSourceRange());
333*0b57cec5SDimitry Andric 
334*0b57cec5SDimitry Andric   const char *bugType = "Floating point variable used as loop counter";
335*0b57cec5SDimitry Andric 
336*0b57cec5SDimitry Andric   PathDiagnosticLocation FSLoc =
337*0b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);
338*0b57cec5SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
339*0b57cec5SDimitry Andric                      bugType, "Security", os.str(),
340*0b57cec5SDimitry Andric                      FSLoc, ranges);
341*0b57cec5SDimitry Andric }
342*0b57cec5SDimitry Andric 
343*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
344*0b57cec5SDimitry Andric // Check: Any use of bcmp.
345*0b57cec5SDimitry Andric // CWE-477: Use of Obsolete Functions
346*0b57cec5SDimitry Andric // bcmp was deprecated in POSIX.1-2008
347*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
348*0b57cec5SDimitry Andric 
checkCall_bcmp(const CallExpr * CE,const FunctionDecl * FD)349*0b57cec5SDimitry Andric void WalkAST::checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD) {
350*0b57cec5SDimitry Andric   if (!filter.check_bcmp)
351*0b57cec5SDimitry Andric     return;
352*0b57cec5SDimitry Andric 
353*0b57cec5SDimitry Andric   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
354*0b57cec5SDimitry Andric   if (!FPT)
355*0b57cec5SDimitry Andric     return;
356*0b57cec5SDimitry Andric 
357*0b57cec5SDimitry Andric   // Verify that the function takes three arguments.
358*0b57cec5SDimitry Andric   if (FPT->getNumParams() != 3)
359*0b57cec5SDimitry Andric     return;
360*0b57cec5SDimitry Andric 
361*0b57cec5SDimitry Andric   for (int i = 0; i < 2; i++) {
362*0b57cec5SDimitry Andric     // Verify the first and second argument type is void*.
363*0b57cec5SDimitry Andric     const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
364*0b57cec5SDimitry Andric     if (!PT)
365*0b57cec5SDimitry Andric       return;
366*0b57cec5SDimitry Andric 
367*0b57cec5SDimitry Andric     if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
368*0b57cec5SDimitry Andric       return;
369*0b57cec5SDimitry Andric   }
370*0b57cec5SDimitry Andric 
371*0b57cec5SDimitry Andric   // Verify the third argument type is integer.
372*0b57cec5SDimitry Andric   if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
373*0b57cec5SDimitry Andric     return;
374*0b57cec5SDimitry Andric 
375*0b57cec5SDimitry Andric   // Issue a warning.
376*0b57cec5SDimitry Andric   PathDiagnosticLocation CELoc =
377*0b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
378*0b57cec5SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp,
379*0b57cec5SDimitry Andric                      "Use of deprecated function in call to 'bcmp()'",
380*0b57cec5SDimitry Andric                      "Security",
381*0b57cec5SDimitry Andric                      "The bcmp() function is obsoleted by memcmp().",
382*0b57cec5SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
383*0b57cec5SDimitry Andric }
384*0b57cec5SDimitry Andric 
385*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
386*0b57cec5SDimitry Andric // Check: Any use of bcopy.
387*0b57cec5SDimitry Andric // CWE-477: Use of Obsolete Functions
388*0b57cec5SDimitry Andric // bcopy was deprecated in POSIX.1-2008
389*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
390*0b57cec5SDimitry Andric 
checkCall_bcopy(const CallExpr * CE,const FunctionDecl * FD)391*0b57cec5SDimitry Andric void WalkAST::checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD) {
392*0b57cec5SDimitry Andric   if (!filter.check_bcopy)
393*0b57cec5SDimitry Andric     return;
394*0b57cec5SDimitry Andric 
395*0b57cec5SDimitry Andric   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
396*0b57cec5SDimitry Andric   if (!FPT)
397*0b57cec5SDimitry Andric     return;
398*0b57cec5SDimitry Andric 
399*0b57cec5SDimitry Andric   // Verify that the function takes three arguments.
400*0b57cec5SDimitry Andric   if (FPT->getNumParams() != 3)
401*0b57cec5SDimitry Andric     return;
402*0b57cec5SDimitry Andric 
403*0b57cec5SDimitry Andric   for (int i = 0; i < 2; i++) {
404*0b57cec5SDimitry Andric     // Verify the first and second argument type is void*.
405*0b57cec5SDimitry Andric     const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
406*0b57cec5SDimitry Andric     if (!PT)
407*0b57cec5SDimitry Andric       return;
408*0b57cec5SDimitry Andric 
409*0b57cec5SDimitry Andric     if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
410*0b57cec5SDimitry Andric       return;
411*0b57cec5SDimitry Andric   }
412*0b57cec5SDimitry Andric 
413*0b57cec5SDimitry Andric   // Verify the third argument type is integer.
414*0b57cec5SDimitry Andric   if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
415*0b57cec5SDimitry Andric     return;
416*0b57cec5SDimitry Andric 
417*0b57cec5SDimitry Andric   // Issue a warning.
418*0b57cec5SDimitry Andric   PathDiagnosticLocation CELoc =
419*0b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
420*0b57cec5SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy,
421*0b57cec5SDimitry Andric                      "Use of deprecated function in call to 'bcopy()'",
422*0b57cec5SDimitry Andric                      "Security",
423*0b57cec5SDimitry Andric                      "The bcopy() function is obsoleted by memcpy() "
424*0b57cec5SDimitry Andric                      "or memmove().",
425*0b57cec5SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
426*0b57cec5SDimitry Andric }
427*0b57cec5SDimitry Andric 
428*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
429*0b57cec5SDimitry Andric // Check: Any use of bzero.
430*0b57cec5SDimitry Andric // CWE-477: Use of Obsolete Functions
431*0b57cec5SDimitry Andric // bzero was deprecated in POSIX.1-2008
432*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
433*0b57cec5SDimitry Andric 
checkCall_bzero(const CallExpr * CE,const FunctionDecl * FD)434*0b57cec5SDimitry Andric void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) {
435*0b57cec5SDimitry Andric   if (!filter.check_bzero)
436*0b57cec5SDimitry Andric     return;
437*0b57cec5SDimitry Andric 
438*0b57cec5SDimitry Andric   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
439*0b57cec5SDimitry Andric   if (!FPT)
440*0b57cec5SDimitry Andric     return;
441*0b57cec5SDimitry Andric 
442*0b57cec5SDimitry Andric   // Verify that the function takes two arguments.
443*0b57cec5SDimitry Andric   if (FPT->getNumParams() != 2)
444*0b57cec5SDimitry Andric     return;
445*0b57cec5SDimitry Andric 
446*0b57cec5SDimitry Andric   // Verify the first argument type is void*.
447*0b57cec5SDimitry Andric   const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
448*0b57cec5SDimitry Andric   if (!PT)
449*0b57cec5SDimitry Andric     return;
450*0b57cec5SDimitry Andric 
451*0b57cec5SDimitry Andric   if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
452*0b57cec5SDimitry Andric     return;
453*0b57cec5SDimitry Andric 
454*0b57cec5SDimitry Andric   // Verify the second argument type is integer.
455*0b57cec5SDimitry Andric   if (!FPT->getParamType(1)->isIntegralOrUnscopedEnumerationType())
456*0b57cec5SDimitry Andric     return;
457*0b57cec5SDimitry Andric 
458*0b57cec5SDimitry Andric   // Issue a warning.
459*0b57cec5SDimitry Andric   PathDiagnosticLocation CELoc =
460*0b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
461*0b57cec5SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero,
462*0b57cec5SDimitry Andric                      "Use of deprecated function in call to 'bzero()'",
463*0b57cec5SDimitry Andric                      "Security",
464*0b57cec5SDimitry Andric                      "The bzero() function is obsoleted by memset().",
465*0b57cec5SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
466*0b57cec5SDimitry Andric }
467*0b57cec5SDimitry Andric 
468*0b57cec5SDimitry Andric 
469*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
470*0b57cec5SDimitry Andric // Check: Any use of 'gets' is insecure.
471*0b57cec5SDimitry Andric // Originally: <rdar://problem/6335715>
472*0b57cec5SDimitry Andric // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)
473*0b57cec5SDimitry Andric // CWE-242: Use of Inherently Dangerous Function
474*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
475*0b57cec5SDimitry Andric 
checkCall_gets(const CallExpr * CE,const FunctionDecl * FD)476*0b57cec5SDimitry Andric void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {
477*0b57cec5SDimitry Andric   if (!filter.check_gets)
478*0b57cec5SDimitry Andric     return;
479*0b57cec5SDimitry Andric 
480*0b57cec5SDimitry Andric   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
481*0b57cec5SDimitry Andric   if (!FPT)
482*0b57cec5SDimitry Andric     return;
483*0b57cec5SDimitry Andric 
484*0b57cec5SDimitry Andric   // Verify that the function takes a single argument.
485*0b57cec5SDimitry Andric   if (FPT->getNumParams() != 1)
486*0b57cec5SDimitry Andric     return;
487*0b57cec5SDimitry Andric 
488*0b57cec5SDimitry Andric   // Is the argument a 'char*'?
489*0b57cec5SDimitry Andric   const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
490*0b57cec5SDimitry Andric   if (!PT)
491*0b57cec5SDimitry Andric     return;
492*0b57cec5SDimitry Andric 
493*0b57cec5SDimitry Andric   if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
494*0b57cec5SDimitry Andric     return;
495*0b57cec5SDimitry Andric 
496*0b57cec5SDimitry Andric   // Issue a warning.
497*0b57cec5SDimitry Andric   PathDiagnosticLocation CELoc =
498*0b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
499*0b57cec5SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
500*0b57cec5SDimitry Andric                      "Potential buffer overflow in call to 'gets'",
501*0b57cec5SDimitry Andric                      "Security",
502*0b57cec5SDimitry Andric                      "Call to function 'gets' is extremely insecure as it can "
503*0b57cec5SDimitry Andric                      "always result in a buffer overflow",
504*0b57cec5SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
505*0b57cec5SDimitry Andric }
506*0b57cec5SDimitry Andric 
507*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
508*0b57cec5SDimitry Andric // Check: Any use of 'getpwd' is insecure.
509*0b57cec5SDimitry Andric // CWE-477: Use of Obsolete Functions
510*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
511*0b57cec5SDimitry Andric 
checkCall_getpw(const CallExpr * CE,const FunctionDecl * FD)512*0b57cec5SDimitry Andric void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {
513*0b57cec5SDimitry Andric   if (!filter.check_getpw)
514*0b57cec5SDimitry Andric     return;
515*0b57cec5SDimitry Andric 
516*0b57cec5SDimitry Andric   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
517*0b57cec5SDimitry Andric   if (!FPT)
518*0b57cec5SDimitry Andric     return;
519*0b57cec5SDimitry Andric 
520*0b57cec5SDimitry Andric   // Verify that the function takes two arguments.
521*0b57cec5SDimitry Andric   if (FPT->getNumParams() != 2)
522*0b57cec5SDimitry Andric     return;
523*0b57cec5SDimitry Andric 
524*0b57cec5SDimitry Andric   // Verify the first argument type is integer.
525*0b57cec5SDimitry Andric   if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType())
526*0b57cec5SDimitry Andric     return;
527*0b57cec5SDimitry Andric 
528*0b57cec5SDimitry Andric   // Verify the second argument type is char*.
529*0b57cec5SDimitry Andric   const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>();
530*0b57cec5SDimitry Andric   if (!PT)
531*0b57cec5SDimitry Andric     return;
532*0b57cec5SDimitry Andric 
533*0b57cec5SDimitry Andric   if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
534*0b57cec5SDimitry Andric     return;
535*0b57cec5SDimitry Andric 
536*0b57cec5SDimitry Andric   // Issue a warning.
537*0b57cec5SDimitry Andric   PathDiagnosticLocation CELoc =
538*0b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
539*0b57cec5SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
540*0b57cec5SDimitry Andric                      "Potential buffer overflow in call to 'getpw'",
541*0b57cec5SDimitry Andric                      "Security",
542*0b57cec5SDimitry Andric                      "The getpw() function is dangerous as it may overflow the "
543*0b57cec5SDimitry Andric                      "provided buffer. It is obsoleted by getpwuid().",
544*0b57cec5SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
545*0b57cec5SDimitry Andric }
546*0b57cec5SDimitry Andric 
547*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
548*0b57cec5SDimitry Andric // Check: Any use of 'mktemp' is insecure.  It is obsoleted by mkstemp().
549*0b57cec5SDimitry Andric // CWE-377: Insecure Temporary File
550*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
551*0b57cec5SDimitry Andric 
checkCall_mktemp(const CallExpr * CE,const FunctionDecl * FD)552*0b57cec5SDimitry Andric void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
553*0b57cec5SDimitry Andric   if (!filter.check_mktemp) {
554*0b57cec5SDimitry Andric     // Fall back to the security check of looking for enough 'X's in the
555*0b57cec5SDimitry Andric     // format string, since that is a less severe warning.
556*0b57cec5SDimitry Andric     checkCall_mkstemp(CE, FD);
557*0b57cec5SDimitry Andric     return;
558*0b57cec5SDimitry Andric   }
559*0b57cec5SDimitry Andric 
560*0b57cec5SDimitry Andric   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
561*0b57cec5SDimitry Andric   if(!FPT)
562*0b57cec5SDimitry Andric     return;
563*0b57cec5SDimitry Andric 
564*0b57cec5SDimitry Andric   // Verify that the function takes a single argument.
565*0b57cec5SDimitry Andric   if (FPT->getNumParams() != 1)
566*0b57cec5SDimitry Andric     return;
567*0b57cec5SDimitry Andric 
568*0b57cec5SDimitry Andric   // Verify that the argument is Pointer Type.
569*0b57cec5SDimitry Andric   const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
570*0b57cec5SDimitry Andric   if (!PT)
571*0b57cec5SDimitry Andric     return;
572*0b57cec5SDimitry Andric 
573*0b57cec5SDimitry Andric   // Verify that the argument is a 'char*'.
574*0b57cec5SDimitry Andric   if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
575*0b57cec5SDimitry Andric     return;
576*0b57cec5SDimitry Andric 
577*0b57cec5SDimitry Andric   // Issue a warning.
578*0b57cec5SDimitry Andric   PathDiagnosticLocation CELoc =
579*0b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
580*0b57cec5SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
581*0b57cec5SDimitry Andric                      "Potential insecure temporary file in call 'mktemp'",
582*0b57cec5SDimitry Andric                      "Security",
583*0b57cec5SDimitry Andric                      "Call to function 'mktemp' is insecure as it always "
584*0b57cec5SDimitry Andric                      "creates or uses insecure temporary file.  Use 'mkstemp' "
585*0b57cec5SDimitry Andric                      "instead",
586*0b57cec5SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
587*0b57cec5SDimitry Andric }
588*0b57cec5SDimitry Andric 
589*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
590*0b57cec5SDimitry Andric // Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.
591*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
592*0b57cec5SDimitry Andric 
checkCall_mkstemp(const CallExpr * CE,const FunctionDecl * FD)593*0b57cec5SDimitry Andric void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
594*0b57cec5SDimitry Andric   if (!filter.check_mkstemp)
595*0b57cec5SDimitry Andric     return;
596*0b57cec5SDimitry Andric 
597*0b57cec5SDimitry Andric   StringRef Name = FD->getIdentifier()->getName();
598*0b57cec5SDimitry Andric   std::pair<signed, signed> ArgSuffix =
599*0b57cec5SDimitry Andric     llvm::StringSwitch<std::pair<signed, signed> >(Name)
600*0b57cec5SDimitry Andric       .Case("mktemp", std::make_pair(0,-1))
601*0b57cec5SDimitry Andric       .Case("mkstemp", std::make_pair(0,-1))
602*0b57cec5SDimitry Andric       .Case("mkdtemp", std::make_pair(0,-1))
603*0b57cec5SDimitry Andric       .Case("mkstemps", std::make_pair(0,1))
604*0b57cec5SDimitry Andric       .Default(std::make_pair(-1, -1));
605*0b57cec5SDimitry Andric 
606*0b57cec5SDimitry Andric   assert(ArgSuffix.first >= 0 && "Unsupported function");
607*0b57cec5SDimitry Andric 
608*0b57cec5SDimitry Andric   // Check if the number of arguments is consistent with out expectations.
609*0b57cec5SDimitry Andric   unsigned numArgs = CE->getNumArgs();
610*0b57cec5SDimitry Andric   if ((signed) numArgs <= ArgSuffix.first)
611*0b57cec5SDimitry Andric     return;
612*0b57cec5SDimitry Andric 
613*0b57cec5SDimitry Andric   const StringLiteral *strArg =
614*0b57cec5SDimitry Andric     dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first)
615*0b57cec5SDimitry Andric                               ->IgnoreParenImpCasts());
616*0b57cec5SDimitry Andric 
617*0b57cec5SDimitry Andric   // Currently we only handle string literals.  It is possible to do better,
618*0b57cec5SDimitry Andric   // either by looking at references to const variables, or by doing real
619*0b57cec5SDimitry Andric   // flow analysis.
620*0b57cec5SDimitry Andric   if (!strArg || strArg->getCharByteWidth() != 1)
621*0b57cec5SDimitry Andric     return;
622*0b57cec5SDimitry Andric 
623*0b57cec5SDimitry Andric   // Count the number of X's, taking into account a possible cutoff suffix.
624*0b57cec5SDimitry Andric   StringRef str = strArg->getString();
625*0b57cec5SDimitry Andric   unsigned numX = 0;
626*0b57cec5SDimitry Andric   unsigned n = str.size();
627*0b57cec5SDimitry Andric 
628*0b57cec5SDimitry Andric   // Take into account the suffix.
629*0b57cec5SDimitry Andric   unsigned suffix = 0;
630*0b57cec5SDimitry Andric   if (ArgSuffix.second >= 0) {
631*0b57cec5SDimitry Andric     const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second);
632*0b57cec5SDimitry Andric     Expr::EvalResult EVResult;
633*0b57cec5SDimitry Andric     if (!suffixEx->EvaluateAsInt(EVResult, BR.getContext()))
634*0b57cec5SDimitry Andric       return;
635*0b57cec5SDimitry Andric     llvm::APSInt Result = EVResult.Val.getInt();
636*0b57cec5SDimitry Andric     // FIXME: Issue a warning.
637*0b57cec5SDimitry Andric     if (Result.isNegative())
638*0b57cec5SDimitry Andric       return;
639*0b57cec5SDimitry Andric     suffix = (unsigned) Result.getZExtValue();
640*0b57cec5SDimitry Andric     n = (n > suffix) ? n - suffix : 0;
641*0b57cec5SDimitry Andric   }
642*0b57cec5SDimitry Andric 
643*0b57cec5SDimitry Andric   for (unsigned i = 0; i < n; ++i)
644*0b57cec5SDimitry Andric     if (str[i] == 'X') ++numX;
645*0b57cec5SDimitry Andric 
646*0b57cec5SDimitry Andric   if (numX >= 6)
647*0b57cec5SDimitry Andric     return;
648*0b57cec5SDimitry Andric 
649*0b57cec5SDimitry Andric   // Issue a warning.
650*0b57cec5SDimitry Andric   PathDiagnosticLocation CELoc =
651*0b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
652*0b57cec5SDimitry Andric   SmallString<512> buf;
653*0b57cec5SDimitry Andric   llvm::raw_svector_ostream out(buf);
654*0b57cec5SDimitry Andric   out << "Call to '" << Name << "' should have at least 6 'X's in the"
655*0b57cec5SDimitry Andric     " format string to be secure (" << numX << " 'X'";
656*0b57cec5SDimitry Andric   if (numX != 1)
657*0b57cec5SDimitry Andric     out << 's';
658*0b57cec5SDimitry Andric   out << " seen";
659*0b57cec5SDimitry Andric   if (suffix) {
660*0b57cec5SDimitry Andric     out << ", " << suffix << " character";
661*0b57cec5SDimitry Andric     if (suffix > 1)
662*0b57cec5SDimitry Andric       out << 's';
663*0b57cec5SDimitry Andric     out << " used as a suffix";
664*0b57cec5SDimitry Andric   }
665*0b57cec5SDimitry Andric   out << ')';
666*0b57cec5SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
667*0b57cec5SDimitry Andric                      "Insecure temporary file creation", "Security",
668*0b57cec5SDimitry Andric                      out.str(), CELoc, strArg->getSourceRange());
669*0b57cec5SDimitry Andric }
670*0b57cec5SDimitry Andric 
671*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
672*0b57cec5SDimitry Andric // Check: Any use of 'strcpy' is insecure.
673*0b57cec5SDimitry Andric //
674*0b57cec5SDimitry Andric // CWE-119: Improper Restriction of Operations within
675*0b57cec5SDimitry Andric // the Bounds of a Memory Buffer
676*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
677*0b57cec5SDimitry Andric 
checkCall_strcpy(const CallExpr * CE,const FunctionDecl * FD)678*0b57cec5SDimitry Andric void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
679*0b57cec5SDimitry Andric   if (!filter.check_strcpy)
680*0b57cec5SDimitry Andric     return;
681*0b57cec5SDimitry Andric 
682*0b57cec5SDimitry Andric   if (!checkCall_strCommon(CE, FD))
683*0b57cec5SDimitry Andric     return;
684*0b57cec5SDimitry Andric 
685*0b57cec5SDimitry Andric   const auto *Target = CE->getArg(0)->IgnoreImpCasts(),
686*0b57cec5SDimitry Andric              *Source = CE->getArg(1)->IgnoreImpCasts();
687*0b57cec5SDimitry Andric 
688*0b57cec5SDimitry Andric   if (const auto *Array = dyn_cast<ConstantArrayType>(Target->getType())) {
689*0b57cec5SDimitry Andric     uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;
690*0b57cec5SDimitry Andric     if (const auto *String = dyn_cast<StringLiteral>(Source)) {
691*0b57cec5SDimitry Andric       if (ArraySize >= String->getLength() + 1)
692*0b57cec5SDimitry Andric         return;
693*0b57cec5SDimitry Andric     }
694*0b57cec5SDimitry Andric   }
695*0b57cec5SDimitry Andric 
696*0b57cec5SDimitry Andric   // Issue a warning.
697*0b57cec5SDimitry Andric   PathDiagnosticLocation CELoc =
698*0b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
699*0b57cec5SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
700*0b57cec5SDimitry Andric                      "Potential insecure memory buffer bounds restriction in "
701*0b57cec5SDimitry Andric                      "call 'strcpy'",
702*0b57cec5SDimitry Andric                      "Security",
703*0b57cec5SDimitry Andric                      "Call to function 'strcpy' is insecure as it does not "
704*0b57cec5SDimitry Andric                      "provide bounding of the memory buffer. Replace "
705*0b57cec5SDimitry Andric                      "unbounded copy functions with analogous functions that "
706*0b57cec5SDimitry Andric                      "support length arguments such as 'strlcpy'. CWE-119.",
707*0b57cec5SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
708*0b57cec5SDimitry Andric }
709*0b57cec5SDimitry Andric 
710*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
711*0b57cec5SDimitry Andric // Check: Any use of 'strcat' is insecure.
712*0b57cec5SDimitry Andric //
713*0b57cec5SDimitry Andric // CWE-119: Improper Restriction of Operations within
714*0b57cec5SDimitry Andric // the Bounds of a Memory Buffer
715*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
716*0b57cec5SDimitry Andric 
checkCall_strcat(const CallExpr * CE,const FunctionDecl * FD)717*0b57cec5SDimitry Andric void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
718*0b57cec5SDimitry Andric   if (!filter.check_strcpy)
719*0b57cec5SDimitry Andric     return;
720*0b57cec5SDimitry Andric 
721*0b57cec5SDimitry Andric   if (!checkCall_strCommon(CE, FD))
722*0b57cec5SDimitry Andric     return;
723*0b57cec5SDimitry Andric 
724*0b57cec5SDimitry Andric   // Issue a warning.
725*0b57cec5SDimitry Andric   PathDiagnosticLocation CELoc =
726*0b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
727*0b57cec5SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
728*0b57cec5SDimitry Andric                      "Potential insecure memory buffer bounds restriction in "
729*0b57cec5SDimitry Andric                      "call 'strcat'",
730*0b57cec5SDimitry Andric                      "Security",
731*0b57cec5SDimitry Andric                      "Call to function 'strcat' is insecure as it does not "
732*0b57cec5SDimitry Andric                      "provide bounding of the memory buffer. Replace "
733*0b57cec5SDimitry Andric                      "unbounded copy functions with analogous functions that "
734*0b57cec5SDimitry Andric                      "support length arguments such as 'strlcat'. CWE-119.",
735*0b57cec5SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
736*0b57cec5SDimitry Andric }
737*0b57cec5SDimitry Andric 
738*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
739*0b57cec5SDimitry Andric // Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',
740*0b57cec5SDimitry Andric //        'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
741*0b57cec5SDimitry Andric //        'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf',
742*0b57cec5SDimitry Andric //        'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset'
743*0b57cec5SDimitry Andric //        is deprecated since C11.
744*0b57cec5SDimitry Andric //
745*0b57cec5SDimitry Andric //        Use of 'sprintf', 'vsprintf', 'scanf', 'wscanf','fscanf',
746*0b57cec5SDimitry Andric //        'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
747*0b57cec5SDimitry Andric //        'swscanf', 'vsscanf', 'vswscanf' without buffer limitations
748*0b57cec5SDimitry Andric //        is insecure.
749*0b57cec5SDimitry Andric //
750*0b57cec5SDimitry Andric // CWE-119: Improper Restriction of Operations within
751*0b57cec5SDimitry Andric // the Bounds of a Memory Buffer
752*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
753*0b57cec5SDimitry Andric 
checkDeprecatedOrUnsafeBufferHandling(const CallExpr * CE,const FunctionDecl * FD)754*0b57cec5SDimitry Andric void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
755*0b57cec5SDimitry Andric                                                     const FunctionDecl *FD) {
756*0b57cec5SDimitry Andric   if (!filter.check_DeprecatedOrUnsafeBufferHandling)
757*0b57cec5SDimitry Andric     return;
758*0b57cec5SDimitry Andric 
759*0b57cec5SDimitry Andric   if (!BR.getContext().getLangOpts().C11)
760*0b57cec5SDimitry Andric     return;
761*0b57cec5SDimitry Andric 
762*0b57cec5SDimitry Andric   // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size
763*0b57cec5SDimitry Andric   // restrictions).
764*0b57cec5SDimitry Andric   enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 };
765*0b57cec5SDimitry Andric 
766*0b57cec5SDimitry Andric   StringRef Name = FD->getIdentifier()->getName();
767*0b57cec5SDimitry Andric   if (Name.startswith("__builtin_"))
768*0b57cec5SDimitry Andric     Name = Name.substr(10);
769*0b57cec5SDimitry Andric 
770*0b57cec5SDimitry Andric   int ArgIndex =
771*0b57cec5SDimitry Andric       llvm::StringSwitch<int>(Name)
772*0b57cec5SDimitry Andric           .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0)
773*0b57cec5SDimitry Andric           .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf",
774*0b57cec5SDimitry Andric                  "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1)
775*0b57cec5SDimitry Andric           .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy",
776*0b57cec5SDimitry Andric                  "memmove", "memset", "strncpy", "strncat", DEPR_ONLY)
777*0b57cec5SDimitry Andric           .Default(UNKNOWN_CALL);
778*0b57cec5SDimitry Andric 
779*0b57cec5SDimitry Andric   assert(ArgIndex != UNKNOWN_CALL && "Unsupported function");
780*0b57cec5SDimitry Andric   bool BoundsProvided = ArgIndex == DEPR_ONLY;
781*0b57cec5SDimitry Andric 
782*0b57cec5SDimitry Andric   if (!BoundsProvided) {
783*0b57cec5SDimitry Andric     // Currently we only handle (not wide) string literals. It is possible to do
784*0b57cec5SDimitry Andric     // better, either by looking at references to const variables, or by doing
785*0b57cec5SDimitry Andric     // real flow analysis.
786*0b57cec5SDimitry Andric     auto FormatString =
787*0b57cec5SDimitry Andric         dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts());
788*0b57cec5SDimitry Andric     if (FormatString &&
789*0b57cec5SDimitry Andric         FormatString->getString().find("%s") == StringRef::npos &&
790*0b57cec5SDimitry Andric         FormatString->getString().find("%[") == StringRef::npos)
791*0b57cec5SDimitry Andric       BoundsProvided = true;
792*0b57cec5SDimitry Andric   }
793*0b57cec5SDimitry Andric 
794*0b57cec5SDimitry Andric   SmallString<128> Buf1;
795*0b57cec5SDimitry Andric   SmallString<512> Buf2;
796*0b57cec5SDimitry Andric   llvm::raw_svector_ostream Out1(Buf1);
797*0b57cec5SDimitry Andric   llvm::raw_svector_ostream Out2(Buf2);
798*0b57cec5SDimitry Andric 
799*0b57cec5SDimitry Andric   Out1 << "Potential insecure memory buffer bounds restriction in call '"
800*0b57cec5SDimitry Andric        << Name << "'";
801*0b57cec5SDimitry Andric   Out2 << "Call to function '" << Name
802*0b57cec5SDimitry Andric        << "' is insecure as it does not provide ";
803*0b57cec5SDimitry Andric 
804*0b57cec5SDimitry Andric   if (!BoundsProvided) {
805*0b57cec5SDimitry Andric     Out2 << "bounding of the memory buffer or ";
806*0b57cec5SDimitry Andric   }
807*0b57cec5SDimitry Andric 
808*0b57cec5SDimitry Andric   Out2 << "security checks introduced "
809*0b57cec5SDimitry Andric           "in the C11 standard. Replace with analogous functions that "
810*0b57cec5SDimitry Andric           "support length arguments or provides boundary checks such as '"
811*0b57cec5SDimitry Andric        << Name << "_s' in case of C11";
812*0b57cec5SDimitry Andric 
813*0b57cec5SDimitry Andric   PathDiagnosticLocation CELoc =
814*0b57cec5SDimitry Andric       PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
815*0b57cec5SDimitry Andric   BR.EmitBasicReport(AC->getDecl(),
816*0b57cec5SDimitry Andric                      filter.checkName_DeprecatedOrUnsafeBufferHandling,
817*0b57cec5SDimitry Andric                      Out1.str(), "Security", Out2.str(), CELoc,
818*0b57cec5SDimitry Andric                      CE->getCallee()->getSourceRange());
819*0b57cec5SDimitry Andric }
820*0b57cec5SDimitry Andric 
821*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
822*0b57cec5SDimitry Andric // Common check for str* functions with no bounds parameters.
823*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
824*0b57cec5SDimitry Andric 
checkCall_strCommon(const CallExpr * CE,const FunctionDecl * FD)825*0b57cec5SDimitry Andric bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) {
826*0b57cec5SDimitry Andric   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
827*0b57cec5SDimitry Andric   if (!FPT)
828*0b57cec5SDimitry Andric     return false;
829*0b57cec5SDimitry Andric 
830*0b57cec5SDimitry Andric   // Verify the function takes two arguments, three in the _chk version.
831*0b57cec5SDimitry Andric   int numArgs = FPT->getNumParams();
832*0b57cec5SDimitry Andric   if (numArgs != 2 && numArgs != 3)
833*0b57cec5SDimitry Andric     return false;
834*0b57cec5SDimitry Andric 
835*0b57cec5SDimitry Andric   // Verify the type for both arguments.
836*0b57cec5SDimitry Andric   for (int i = 0; i < 2; i++) {
837*0b57cec5SDimitry Andric     // Verify that the arguments are pointers.
838*0b57cec5SDimitry Andric     const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
839*0b57cec5SDimitry Andric     if (!PT)
840*0b57cec5SDimitry Andric       return false;
841*0b57cec5SDimitry Andric 
842*0b57cec5SDimitry Andric     // Verify that the argument is a 'char*'.
843*0b57cec5SDimitry Andric     if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
844*0b57cec5SDimitry Andric       return false;
845*0b57cec5SDimitry Andric   }
846*0b57cec5SDimitry Andric 
847*0b57cec5SDimitry Andric   return true;
848*0b57cec5SDimitry Andric }
849*0b57cec5SDimitry Andric 
850*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
851*0b57cec5SDimitry Andric // Check: Linear congruent random number generators should not be used
852*0b57cec5SDimitry Andric // Originally: <rdar://problem/63371000>
853*0b57cec5SDimitry Andric // CWE-338: Use of cryptographically weak prng
854*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
855*0b57cec5SDimitry Andric 
checkCall_rand(const CallExpr * CE,const FunctionDecl * FD)856*0b57cec5SDimitry Andric void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
857*0b57cec5SDimitry Andric   if (!filter.check_rand || !CheckRand)
858*0b57cec5SDimitry Andric     return;
859*0b57cec5SDimitry Andric 
860*0b57cec5SDimitry Andric   const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
861*0b57cec5SDimitry Andric   if (!FTP)
862*0b57cec5SDimitry Andric     return;
863*0b57cec5SDimitry Andric 
864*0b57cec5SDimitry Andric   if (FTP->getNumParams() == 1) {
865*0b57cec5SDimitry Andric     // Is the argument an 'unsigned short *'?
866*0b57cec5SDimitry Andric     // (Actually any integer type is allowed.)
867*0b57cec5SDimitry Andric     const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>();
868*0b57cec5SDimitry Andric     if (!PT)
869*0b57cec5SDimitry Andric       return;
870*0b57cec5SDimitry Andric 
871*0b57cec5SDimitry Andric     if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())
872*0b57cec5SDimitry Andric       return;
873*0b57cec5SDimitry Andric   } else if (FTP->getNumParams() != 0)
874*0b57cec5SDimitry Andric     return;
875*0b57cec5SDimitry Andric 
876*0b57cec5SDimitry Andric   // Issue a warning.
877*0b57cec5SDimitry Andric   SmallString<256> buf1;
878*0b57cec5SDimitry Andric   llvm::raw_svector_ostream os1(buf1);
879*0b57cec5SDimitry Andric   os1 << '\'' << *FD << "' is a poor random number generator";
880*0b57cec5SDimitry Andric 
881*0b57cec5SDimitry Andric   SmallString<256> buf2;
882*0b57cec5SDimitry Andric   llvm::raw_svector_ostream os2(buf2);
883*0b57cec5SDimitry Andric   os2 << "Function '" << *FD
884*0b57cec5SDimitry Andric       << "' is obsolete because it implements a poor random number generator."
885*0b57cec5SDimitry Andric       << "  Use 'arc4random' instead";
886*0b57cec5SDimitry Andric 
887*0b57cec5SDimitry Andric   PathDiagnosticLocation CELoc =
888*0b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
889*0b57cec5SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
890*0b57cec5SDimitry Andric                      "Security", os2.str(), CELoc,
891*0b57cec5SDimitry Andric                      CE->getCallee()->getSourceRange());
892*0b57cec5SDimitry Andric }
893*0b57cec5SDimitry Andric 
894*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
895*0b57cec5SDimitry Andric // Check: 'random' should not be used
896*0b57cec5SDimitry Andric // Originally: <rdar://problem/63371000>
897*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
898*0b57cec5SDimitry Andric 
checkCall_random(const CallExpr * CE,const FunctionDecl * FD)899*0b57cec5SDimitry Andric void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {
900*0b57cec5SDimitry Andric   if (!CheckRand || !filter.check_rand)
901*0b57cec5SDimitry Andric     return;
902*0b57cec5SDimitry Andric 
903*0b57cec5SDimitry Andric   const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
904*0b57cec5SDimitry Andric   if (!FTP)
905*0b57cec5SDimitry Andric     return;
906*0b57cec5SDimitry Andric 
907*0b57cec5SDimitry Andric   // Verify that the function takes no argument.
908*0b57cec5SDimitry Andric   if (FTP->getNumParams() != 0)
909*0b57cec5SDimitry Andric     return;
910*0b57cec5SDimitry Andric 
911*0b57cec5SDimitry Andric   // Issue a warning.
912*0b57cec5SDimitry Andric   PathDiagnosticLocation CELoc =
913*0b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
914*0b57cec5SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
915*0b57cec5SDimitry Andric                      "'random' is not a secure random number generator",
916*0b57cec5SDimitry Andric                      "Security",
917*0b57cec5SDimitry Andric                      "The 'random' function produces a sequence of values that "
918*0b57cec5SDimitry Andric                      "an adversary may be able to predict.  Use 'arc4random' "
919*0b57cec5SDimitry Andric                      "instead", CELoc, CE->getCallee()->getSourceRange());
920*0b57cec5SDimitry Andric }
921*0b57cec5SDimitry Andric 
922*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
923*0b57cec5SDimitry Andric // Check: 'vfork' should not be used.
924*0b57cec5SDimitry Andric // POS33-C: Do not use vfork().
925*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
926*0b57cec5SDimitry Andric 
checkCall_vfork(const CallExpr * CE,const FunctionDecl * FD)927*0b57cec5SDimitry Andric void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
928*0b57cec5SDimitry Andric   if (!filter.check_vfork)
929*0b57cec5SDimitry Andric     return;
930*0b57cec5SDimitry Andric 
931*0b57cec5SDimitry Andric   // All calls to vfork() are insecure, issue a warning.
932*0b57cec5SDimitry Andric   PathDiagnosticLocation CELoc =
933*0b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
934*0b57cec5SDimitry Andric   BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
935*0b57cec5SDimitry Andric                      "Potential insecure implementation-specific behavior in "
936*0b57cec5SDimitry Andric                      "call 'vfork'",
937*0b57cec5SDimitry Andric                      "Security",
938*0b57cec5SDimitry Andric                      "Call to function 'vfork' is insecure as it can lead to "
939*0b57cec5SDimitry Andric                      "denial of service situations in the parent process. "
940*0b57cec5SDimitry Andric                      "Replace calls to vfork with calls to the safer "
941*0b57cec5SDimitry Andric                      "'posix_spawn' function",
942*0b57cec5SDimitry Andric                      CELoc, CE->getCallee()->getSourceRange());
943*0b57cec5SDimitry Andric }
944*0b57cec5SDimitry Andric 
945*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
946*0b57cec5SDimitry Andric // Check: '-decodeValueOfObjCType:at:' should not be used.
947*0b57cec5SDimitry Andric // It is deprecated in favor of '-decodeValueOfObjCType:at:size:' due to
948*0b57cec5SDimitry Andric // likelihood of buffer overflows.
949*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
950*0b57cec5SDimitry Andric 
checkMsg_decodeValueOfObjCType(const ObjCMessageExpr * ME)951*0b57cec5SDimitry Andric void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) {
952*0b57cec5SDimitry Andric   if (!filter.check_decodeValueOfObjCType)
953*0b57cec5SDimitry Andric     return;
954*0b57cec5SDimitry Andric 
955*0b57cec5SDimitry Andric   // Check availability of the secure alternative:
956*0b57cec5SDimitry Andric   // iOS 11+, macOS 10.13+, tvOS 11+, and watchOS 4.0+
957*0b57cec5SDimitry Andric   // FIXME: We probably shouldn't register the check if it's not available.
958*0b57cec5SDimitry Andric   const TargetInfo &TI = AC->getASTContext().getTargetInfo();
959*0b57cec5SDimitry Andric   const llvm::Triple &T = TI.getTriple();
960*0b57cec5SDimitry Andric   const VersionTuple &VT = TI.getPlatformMinVersion();
961*0b57cec5SDimitry Andric   switch (T.getOS()) {
962*0b57cec5SDimitry Andric   case llvm::Triple::IOS:
963*0b57cec5SDimitry Andric     if (VT < VersionTuple(11, 0))
964*0b57cec5SDimitry Andric       return;
965*0b57cec5SDimitry Andric     break;
966*0b57cec5SDimitry Andric   case llvm::Triple::MacOSX:
967*0b57cec5SDimitry Andric     if (VT < VersionTuple(10, 13))
968*0b57cec5SDimitry Andric       return;
969*0b57cec5SDimitry Andric     break;
970*0b57cec5SDimitry Andric   case llvm::Triple::WatchOS:
971*0b57cec5SDimitry Andric     if (VT < VersionTuple(4, 0))
972*0b57cec5SDimitry Andric       return;
973*0b57cec5SDimitry Andric     break;
974*0b57cec5SDimitry Andric   case llvm::Triple::TvOS:
975*0b57cec5SDimitry Andric     if (VT < VersionTuple(11, 0))
976*0b57cec5SDimitry Andric       return;
977*0b57cec5SDimitry Andric     break;
978*0b57cec5SDimitry Andric   default:
979*0b57cec5SDimitry Andric     return;
980*0b57cec5SDimitry Andric   }
981*0b57cec5SDimitry Andric 
982*0b57cec5SDimitry Andric   PathDiagnosticLocation MELoc =
983*0b57cec5SDimitry Andric       PathDiagnosticLocation::createBegin(ME, BR.getSourceManager(), AC);
984*0b57cec5SDimitry Andric   BR.EmitBasicReport(
985*0b57cec5SDimitry Andric       AC->getDecl(), filter.checkName_decodeValueOfObjCType,
986*0b57cec5SDimitry Andric       "Potential buffer overflow in '-decodeValueOfObjCType:at:'", "Security",
987*0b57cec5SDimitry Andric       "Deprecated method '-decodeValueOfObjCType:at:' is insecure "
988*0b57cec5SDimitry Andric       "as it can lead to potential buffer overflows. Use the safer "
989*0b57cec5SDimitry Andric       "'-decodeValueOfObjCType:at:size:' method.",
990*0b57cec5SDimitry Andric       MELoc, ME->getSourceRange());
991*0b57cec5SDimitry Andric }
992*0b57cec5SDimitry Andric 
993*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
994*0b57cec5SDimitry Andric // Check: Should check whether privileges are dropped successfully.
995*0b57cec5SDimitry Andric // Originally: <rdar://problem/6337132>
996*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
997*0b57cec5SDimitry Andric 
checkUncheckedReturnValue(CallExpr * CE)998*0b57cec5SDimitry Andric void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
999*0b57cec5SDimitry Andric   if (!filter.check_UncheckedReturn)
1000*0b57cec5SDimitry Andric     return;
1001*0b57cec5SDimitry Andric 
1002*0b57cec5SDimitry Andric   const FunctionDecl *FD = CE->getDirectCallee();
1003*0b57cec5SDimitry Andric   if (!FD)
1004*0b57cec5SDimitry Andric     return;
1005*0b57cec5SDimitry Andric 
1006*0b57cec5SDimitry Andric   if (II_setid[0] == nullptr) {
1007*0b57cec5SDimitry Andric     static const char * const identifiers[num_setids] = {
1008*0b57cec5SDimitry Andric       "setuid", "setgid", "seteuid", "setegid",
1009*0b57cec5SDimitry Andric       "setreuid", "setregid"
1010*0b57cec5SDimitry Andric     };
1011*0b57cec5SDimitry Andric 
1012*0b57cec5SDimitry Andric     for (size_t i = 0; i < num_setids; i++)
1013*0b57cec5SDimitry Andric       II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
1014*0b57cec5SDimitry Andric   }
1015*0b57cec5SDimitry Andric 
1016*0b57cec5SDimitry Andric   const IdentifierInfo *id = FD->getIdentifier();
1017*0b57cec5SDimitry Andric   size_t identifierid;
1018*0b57cec5SDimitry Andric 
1019*0b57cec5SDimitry Andric   for (identifierid = 0; identifierid < num_setids; identifierid++)
1020*0b57cec5SDimitry Andric     if (id == II_setid[identifierid])
1021*0b57cec5SDimitry Andric       break;
1022*0b57cec5SDimitry Andric 
1023*0b57cec5SDimitry Andric   if (identifierid >= num_setids)
1024*0b57cec5SDimitry Andric     return;
1025*0b57cec5SDimitry Andric 
1026*0b57cec5SDimitry Andric   const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
1027*0b57cec5SDimitry Andric   if (!FTP)
1028*0b57cec5SDimitry Andric     return;
1029*0b57cec5SDimitry Andric 
1030*0b57cec5SDimitry Andric   // Verify that the function takes one or two arguments (depending on
1031*0b57cec5SDimitry Andric   //   the function).
1032*0b57cec5SDimitry Andric   if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2))
1033*0b57cec5SDimitry Andric     return;
1034*0b57cec5SDimitry Andric 
1035*0b57cec5SDimitry Andric   // The arguments must be integers.
1036*0b57cec5SDimitry Andric   for (unsigned i = 0; i < FTP->getNumParams(); i++)
1037     if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType())
1038       return;
1039 
1040   // Issue a warning.
1041   SmallString<256> buf1;
1042   llvm::raw_svector_ostream os1(buf1);
1043   os1 << "Return value is not checked in call to '" << *FD << '\'';
1044 
1045   SmallString<256> buf2;
1046   llvm::raw_svector_ostream os2(buf2);
1047   os2 << "The return value from the call to '" << *FD
1048       << "' is not checked.  If an error occurs in '" << *FD
1049       << "', the following code may execute with unexpected privileges";
1050 
1051   PathDiagnosticLocation CELoc =
1052     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
1053   BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
1054                      "Security", os2.str(), CELoc,
1055                      CE->getCallee()->getSourceRange());
1056 }
1057 
1058 //===----------------------------------------------------------------------===//
1059 // SecuritySyntaxChecker
1060 //===----------------------------------------------------------------------===//
1061 
1062 namespace {
1063 class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
1064 public:
1065   ChecksFilter filter;
1066 
checkASTCodeBody(const Decl * D,AnalysisManager & mgr,BugReporter & BR) const1067   void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
1068                         BugReporter &BR) const {
1069     WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
1070     walker.Visit(D->getBody());
1071   }
1072 };
1073 }
1074 
registerSecuritySyntaxChecker(CheckerManager & mgr)1075 void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
1076   mgr.registerChecker<SecuritySyntaxChecker>();
1077 }
1078 
shouldRegisterSecuritySyntaxChecker(const CheckerManager & mgr)1079 bool ento::shouldRegisterSecuritySyntaxChecker(const CheckerManager &mgr) {
1080   return true;
1081 }
1082 
1083 #define REGISTER_CHECKER(name)                                                 \
1084   void ento::register##name(CheckerManager &mgr) {                             \
1085     SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>();  \
1086     checker->filter.check_##name = true;                                       \
1087     checker->filter.checkName_##name = mgr.getCurrentCheckerName();            \
1088   }                                                                            \
1089                                                                                \
1090   bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
1091 
1092 REGISTER_CHECKER(bcmp)
1093 REGISTER_CHECKER(bcopy)
1094 REGISTER_CHECKER(bzero)
1095 REGISTER_CHECKER(gets)
1096 REGISTER_CHECKER(getpw)
1097 REGISTER_CHECKER(mkstemp)
1098 REGISTER_CHECKER(mktemp)
1099 REGISTER_CHECKER(strcpy)
1100 REGISTER_CHECKER(rand)
1101 REGISTER_CHECKER(vfork)
1102 REGISTER_CHECKER(FloatLoopCounter)
1103 REGISTER_CHECKER(UncheckedReturn)
1104 REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)
1105 REGISTER_CHECKER(decodeValueOfObjCType)
1106