xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp (revision ec0ea6efa1ad229d75c394c1a9b9cac33af2b1d3)
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   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 
70 class 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 
79 public:
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 
126 void WalkAST::VisitChildren(Stmt *S) {
127   for (Stmt *Child : S->children())
128     if (Child)
129       Visit(Child);
130 }
131 
132 void 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 
190 void 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 
204 void 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 
213 void 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.
228 static const DeclRefExpr*
229 getIncrementedVar(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 ///
262 void 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 
349 void 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 
391 void 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 
434 void 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 
476 void 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 
512 void 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 
552 void 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 
593 void 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 
678 void 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 
717 void 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 
754 void 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 
825 bool 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 
856 void 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 
899 void 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 
927 void 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 
951 void 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 
998 void 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 
1062 namespace {
1063 class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
1064 public:
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 
1075 void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
1076   mgr.registerChecker<SecuritySyntaxChecker>();
1077 }
1078 
1079 bool ento::shouldRegisterSecuritySyntaxChecker(const CheckerManager &mgr) {
1080   return true;
1081 }
1082 
1083 #define REGISTER_CHECKER(name)                                                 \
1084   void ento::register##name(CheckerManager &mgr) {                             \
1085     SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>();  \
1086     checker->filter.check_##name = true;                                       \
1087     checker->filter.checkName_##name = mgr.getCurrentCheckerName();            \
1088   }                                                                            \
1089                                                                                \
1090   bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
1091 
1092 REGISTER_CHECKER(bcmp)
1093 REGISTER_CHECKER(bcopy)
1094 REGISTER_CHECKER(bzero)
1095 REGISTER_CHECKER(gets)
1096 REGISTER_CHECKER(getpw)
1097 REGISTER_CHECKER(mkstemp)
1098 REGISTER_CHECKER(mktemp)
1099 REGISTER_CHECKER(strcpy)
1100 REGISTER_CHECKER(rand)
1101 REGISTER_CHECKER(vfork)
1102 REGISTER_CHECKER(FloatLoopCounter)
1103 REGISTER_CHECKER(UncheckedReturn)
1104 REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)
1105 REGISTER_CHECKER(decodeValueOfObjCType)
1106