CheckSecuritySyntaxOnly.cpp revision 360784
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  DefaultBool check_bcmp;
40  DefaultBool check_bcopy;
41  DefaultBool check_bzero;
42  DefaultBool check_gets;
43  DefaultBool check_getpw;
44  DefaultBool check_mktemp;
45  DefaultBool check_mkstemp;
46  DefaultBool check_strcpy;
47  DefaultBool check_DeprecatedOrUnsafeBufferHandling;
48  DefaultBool check_rand;
49  DefaultBool check_vfork;
50  DefaultBool check_FloatLoopCounter;
51  DefaultBool check_UncheckedReturn;
52  DefaultBool check_decodeValueOfObjCType;
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().getAsString()
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 &&
789        FormatString->getString().find("%s") == StringRef::npos &&
790        FormatString->getString().find("%[") == StringRef::npos)
791      BoundsProvided = true;
792  }
793
794  SmallString<128> Buf1;
795  SmallString<512> Buf2;
796  llvm::raw_svector_ostream Out1(Buf1);
797  llvm::raw_svector_ostream Out2(Buf2);
798
799  Out1 << "Potential insecure memory buffer bounds restriction in call '"
800       << Name << "'";
801  Out2 << "Call to function '" << Name
802       << "' is insecure as it does not provide ";
803
804  if (!BoundsProvided) {
805    Out2 << "bounding of the memory buffer or ";
806  }
807
808  Out2 << "security checks introduced "
809          "in the C11 standard. Replace with analogous functions that "
810          "support length arguments or provides boundary checks such as '"
811       << Name << "_s' in case of C11";
812
813  PathDiagnosticLocation CELoc =
814      PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
815  BR.EmitBasicReport(AC->getDecl(),
816                     filter.checkName_DeprecatedOrUnsafeBufferHandling,
817                     Out1.str(), "Security", Out2.str(), CELoc,
818                     CE->getCallee()->getSourceRange());
819}
820
821//===----------------------------------------------------------------------===//
822// Common check for str* functions with no bounds parameters.
823//===----------------------------------------------------------------------===//
824
825bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) {
826  const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
827  if (!FPT)
828    return false;
829
830  // Verify the function takes two arguments, three in the _chk version.
831  int numArgs = FPT->getNumParams();
832  if (numArgs != 2 && numArgs != 3)
833    return false;
834
835  // Verify the type for both arguments.
836  for (int i = 0; i < 2; i++) {
837    // Verify that the arguments are pointers.
838    const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
839    if (!PT)
840      return false;
841
842    // Verify that the argument is a 'char*'.
843    if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
844      return false;
845  }
846
847  return true;
848}
849
850//===----------------------------------------------------------------------===//
851// Check: Linear congruent random number generators should not be used
852// Originally: <rdar://problem/63371000>
853// CWE-338: Use of cryptographically weak prng
854//===----------------------------------------------------------------------===//
855
856void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
857  if (!filter.check_rand || !CheckRand)
858    return;
859
860  const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
861  if (!FTP)
862    return;
863
864  if (FTP->getNumParams() == 1) {
865    // Is the argument an 'unsigned short *'?
866    // (Actually any integer type is allowed.)
867    const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>();
868    if (!PT)
869      return;
870
871    if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())
872      return;
873  } else if (FTP->getNumParams() != 0)
874    return;
875
876  // Issue a warning.
877  SmallString<256> buf1;
878  llvm::raw_svector_ostream os1(buf1);
879  os1 << '\'' << *FD << "' is a poor random number generator";
880
881  SmallString<256> buf2;
882  llvm::raw_svector_ostream os2(buf2);
883  os2 << "Function '" << *FD
884      << "' is obsolete because it implements a poor random number generator."
885      << "  Use 'arc4random' instead";
886
887  PathDiagnosticLocation CELoc =
888    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
889  BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
890                     "Security", os2.str(), CELoc,
891                     CE->getCallee()->getSourceRange());
892}
893
894//===----------------------------------------------------------------------===//
895// Check: 'random' should not be used
896// Originally: <rdar://problem/63371000>
897//===----------------------------------------------------------------------===//
898
899void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {
900  if (!CheckRand || !filter.check_rand)
901    return;
902
903  const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
904  if (!FTP)
905    return;
906
907  // Verify that the function takes no argument.
908  if (FTP->getNumParams() != 0)
909    return;
910
911  // Issue a warning.
912  PathDiagnosticLocation CELoc =
913    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
914  BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
915                     "'random' is not a secure random number generator",
916                     "Security",
917                     "The 'random' function produces a sequence of values that "
918                     "an adversary may be able to predict.  Use 'arc4random' "
919                     "instead", CELoc, CE->getCallee()->getSourceRange());
920}
921
922//===----------------------------------------------------------------------===//
923// Check: 'vfork' should not be used.
924// POS33-C: Do not use vfork().
925//===----------------------------------------------------------------------===//
926
927void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
928  if (!filter.check_vfork)
929    return;
930
931  // All calls to vfork() are insecure, issue a warning.
932  PathDiagnosticLocation CELoc =
933    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
934  BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
935                     "Potential insecure implementation-specific behavior in "
936                     "call 'vfork'",
937                     "Security",
938                     "Call to function 'vfork' is insecure as it can lead to "
939                     "denial of service situations in the parent process. "
940                     "Replace calls to vfork with calls to the safer "
941                     "'posix_spawn' function",
942                     CELoc, CE->getCallee()->getSourceRange());
943}
944
945//===----------------------------------------------------------------------===//
946// Check: '-decodeValueOfObjCType:at:' should not be used.
947// It is deprecated in favor of '-decodeValueOfObjCType:at:size:' due to
948// likelihood of buffer overflows.
949//===----------------------------------------------------------------------===//
950
951void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) {
952  if (!filter.check_decodeValueOfObjCType)
953    return;
954
955  // Check availability of the secure alternative:
956  // iOS 11+, macOS 10.13+, tvOS 11+, and watchOS 4.0+
957  // FIXME: We probably shouldn't register the check if it's not available.
958  const TargetInfo &TI = AC->getASTContext().getTargetInfo();
959  const llvm::Triple &T = TI.getTriple();
960  const VersionTuple &VT = TI.getPlatformMinVersion();
961  switch (T.getOS()) {
962  case llvm::Triple::IOS:
963    if (VT < VersionTuple(11, 0))
964      return;
965    break;
966  case llvm::Triple::MacOSX:
967    if (VT < VersionTuple(10, 13))
968      return;
969    break;
970  case llvm::Triple::WatchOS:
971    if (VT < VersionTuple(4, 0))
972      return;
973    break;
974  case llvm::Triple::TvOS:
975    if (VT < VersionTuple(11, 0))
976      return;
977    break;
978  default:
979    return;
980  }
981
982  PathDiagnosticLocation MELoc =
983      PathDiagnosticLocation::createBegin(ME, BR.getSourceManager(), AC);
984  BR.EmitBasicReport(
985      AC->getDecl(), filter.checkName_decodeValueOfObjCType,
986      "Potential buffer overflow in '-decodeValueOfObjCType:at:'", "Security",
987      "Deprecated method '-decodeValueOfObjCType:at:' is insecure "
988      "as it can lead to potential buffer overflows. Use the safer "
989      "'-decodeValueOfObjCType:at:size:' method.",
990      MELoc, ME->getSourceRange());
991}
992
993//===----------------------------------------------------------------------===//
994// Check: Should check whether privileges are dropped successfully.
995// Originally: <rdar://problem/6337132>
996//===----------------------------------------------------------------------===//
997
998void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
999  if (!filter.check_UncheckedReturn)
1000    return;
1001
1002  const FunctionDecl *FD = CE->getDirectCallee();
1003  if (!FD)
1004    return;
1005
1006  if (II_setid[0] == nullptr) {
1007    static const char * const identifiers[num_setids] = {
1008      "setuid", "setgid", "seteuid", "setegid",
1009      "setreuid", "setregid"
1010    };
1011
1012    for (size_t i = 0; i < num_setids; i++)
1013      II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
1014  }
1015
1016  const IdentifierInfo *id = FD->getIdentifier();
1017  size_t identifierid;
1018
1019  for (identifierid = 0; identifierid < num_setids; identifierid++)
1020    if (id == II_setid[identifierid])
1021      break;
1022
1023  if (identifierid >= num_setids)
1024    return;
1025
1026  const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
1027  if (!FTP)
1028    return;
1029
1030  // Verify that the function takes one or two arguments (depending on
1031  //   the function).
1032  if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2))
1033    return;
1034
1035  // The arguments must be integers.
1036  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
1062namespace {
1063class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
1064public:
1065  ChecksFilter filter;
1066
1067  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
1075void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
1076  mgr.registerChecker<SecuritySyntaxChecker>();
1077}
1078
1079bool ento::shouldRegisterSecuritySyntaxChecker(const LangOptions &LO) {
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 LangOptions &LO) { return true; }
1091
1092REGISTER_CHECKER(bcmp)
1093REGISTER_CHECKER(bcopy)
1094REGISTER_CHECKER(bzero)
1095REGISTER_CHECKER(gets)
1096REGISTER_CHECKER(getpw)
1097REGISTER_CHECKER(mkstemp)
1098REGISTER_CHECKER(mktemp)
1099REGISTER_CHECKER(strcpy)
1100REGISTER_CHECKER(rand)
1101REGISTER_CHECKER(vfork)
1102REGISTER_CHECKER(FloatLoopCounter)
1103REGISTER_CHECKER(UncheckedReturn)
1104REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)
1105REGISTER_CHECKER(decodeValueOfObjCType)
1106