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