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