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