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