xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp (revision cb14a3fe5122c879eae1fb480ed7ce82a699ddb6)
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.isOSFreeBSD() ||
31          T.isOSNetBSD() ||
32          T.isOSOpenBSD() ||
33          T.isOSDragonFly();
34 }
35 
36 namespace {
37 struct ChecksFilter {
38   bool check_bcmp = false;
39   bool check_bcopy = false;
40   bool check_bzero = false;
41   bool check_gets = false;
42   bool check_getpw = false;
43   bool check_mktemp = false;
44   bool check_mkstemp = false;
45   bool check_strcpy = false;
46   bool check_DeprecatedOrUnsafeBufferHandling = false;
47   bool check_rand = false;
48   bool check_vfork = false;
49   bool check_FloatLoopCounter = false;
50   bool check_UncheckedReturn = false;
51   bool check_decodeValueOfObjCType = false;
52 
53   CheckerNameRef checkName_bcmp;
54   CheckerNameRef checkName_bcopy;
55   CheckerNameRef checkName_bzero;
56   CheckerNameRef checkName_gets;
57   CheckerNameRef checkName_getpw;
58   CheckerNameRef checkName_mktemp;
59   CheckerNameRef checkName_mkstemp;
60   CheckerNameRef checkName_strcpy;
61   CheckerNameRef checkName_DeprecatedOrUnsafeBufferHandling;
62   CheckerNameRef checkName_rand;
63   CheckerNameRef checkName_vfork;
64   CheckerNameRef checkName_FloatLoopCounter;
65   CheckerNameRef checkName_UncheckedReturn;
66   CheckerNameRef checkName_decodeValueOfObjCType;
67 };
68 
69 class WalkAST : public StmtVisitor<WalkAST> {
70   BugReporter &BR;
71   AnalysisDeclContext* AC;
72   enum { num_setids = 6 };
73   IdentifierInfo *II_setid[num_setids];
74 
75   const bool CheckRand;
76   const ChecksFilter &filter;
77 
78 public:
79   WalkAST(BugReporter &br, AnalysisDeclContext* ac,
80           const ChecksFilter &f)
81   : BR(br), AC(ac), II_setid(),
82     CheckRand(isArc4RandomAvailable(BR.getContext())),
83     filter(f) {}
84 
85   // Statement visitor methods.
86   void VisitCallExpr(CallExpr *CE);
87   void VisitObjCMessageExpr(ObjCMessageExpr *CE);
88   void VisitForStmt(ForStmt *S);
89   void VisitCompoundStmt (CompoundStmt *S);
90   void VisitStmt(Stmt *S) { VisitChildren(S); }
91 
92   void VisitChildren(Stmt *S);
93 
94   // Helpers.
95   bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD);
96 
97   typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *);
98   typedef void (WalkAST::*MsgCheck)(const ObjCMessageExpr *);
99 
100   // Checker-specific methods.
101   void checkLoopConditionForFloat(const ForStmt *FS);
102   void checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD);
103   void checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD);
104   void checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD);
105   void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD);
106   void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD);
107   void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD);
108   void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD);
109   void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD);
110   void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD);
111   void checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
112                                              const FunctionDecl *FD);
113   void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);
114   void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);
115   void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD);
116   void checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME);
117   void checkUncheckedReturnValue(CallExpr *CE);
118 };
119 } // end anonymous namespace
120 
121 //===----------------------------------------------------------------------===//
122 // AST walking.
123 //===----------------------------------------------------------------------===//
124 
125 void WalkAST::VisitChildren(Stmt *S) {
126   for (Stmt *Child : S->children())
127     if (Child)
128       Visit(Child);
129 }
130 
131 void WalkAST::VisitCallExpr(CallExpr *CE) {
132   // Get the callee.
133   const FunctionDecl *FD = CE->getDirectCallee();
134 
135   if (!FD)
136     return;
137 
138   // Get the name of the callee. If it's a builtin, strip off the prefix.
139   IdentifierInfo *II = FD->getIdentifier();
140   if (!II)   // if no identifier, not a simple C function
141     return;
142   StringRef Name = II->getName();
143   if (Name.starts_with("__builtin_"))
144     Name = Name.substr(10);
145 
146   // Set the evaluation function by switching on the callee name.
147   FnCheck evalFunction =
148       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", "fprintf",
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. Most man pages literally says this.
470 //
471 // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)
472 // CWE-242: Use of Inherently Dangerous Function
473 //===----------------------------------------------------------------------===//
474 
475 void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {
476   if (!filter.check_gets)
477     return;
478 
479   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
480   if (!FPT)
481     return;
482 
483   // Verify that the function takes a single argument.
484   if (FPT->getNumParams() != 1)
485     return;
486 
487   // Is the argument a 'char*'?
488   const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
489   if (!PT)
490     return;
491 
492   if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
493     return;
494 
495   // Issue a warning.
496   PathDiagnosticLocation CELoc =
497     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
498   BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
499                      "Potential buffer overflow in call to 'gets'",
500                      "Security",
501                      "Call to function 'gets' is extremely insecure as it can "
502                      "always result in a buffer overflow",
503                      CELoc, CE->getCallee()->getSourceRange());
504 }
505 
506 //===----------------------------------------------------------------------===//
507 // Check: Any use of 'getpwd' is insecure.
508 // CWE-477: Use of Obsolete Functions
509 //===----------------------------------------------------------------------===//
510 
511 void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {
512   if (!filter.check_getpw)
513     return;
514 
515   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
516   if (!FPT)
517     return;
518 
519   // Verify that the function takes two arguments.
520   if (FPT->getNumParams() != 2)
521     return;
522 
523   // Verify the first argument type is integer.
524   if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType())
525     return;
526 
527   // Verify the second argument type is char*.
528   const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>();
529   if (!PT)
530     return;
531 
532   if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
533     return;
534 
535   // Issue a warning.
536   PathDiagnosticLocation CELoc =
537     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
538   BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
539                      "Potential buffer overflow in call to 'getpw'",
540                      "Security",
541                      "The getpw() function is dangerous as it may overflow the "
542                      "provided buffer. It is obsoleted by getpwuid().",
543                      CELoc, CE->getCallee()->getSourceRange());
544 }
545 
546 //===----------------------------------------------------------------------===//
547 // Check: Any use of 'mktemp' is insecure.  It is obsoleted by mkstemp().
548 // CWE-377: Insecure Temporary File
549 //===----------------------------------------------------------------------===//
550 
551 void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
552   if (!filter.check_mktemp) {
553     // Fall back to the security check of looking for enough 'X's in the
554     // format string, since that is a less severe warning.
555     checkCall_mkstemp(CE, FD);
556     return;
557   }
558 
559   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
560   if(!FPT)
561     return;
562 
563   // Verify that the function takes a single argument.
564   if (FPT->getNumParams() != 1)
565     return;
566 
567   // Verify that the argument is Pointer Type.
568   const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
569   if (!PT)
570     return;
571 
572   // Verify that the argument is a 'char*'.
573   if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
574     return;
575 
576   // Issue a warning.
577   PathDiagnosticLocation CELoc =
578     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
579   BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
580                      "Potential insecure temporary file in call 'mktemp'",
581                      "Security",
582                      "Call to function 'mktemp' is insecure as it always "
583                      "creates or uses insecure temporary file.  Use 'mkstemp' "
584                      "instead",
585                      CELoc, CE->getCallee()->getSourceRange());
586 }
587 
588 //===----------------------------------------------------------------------===//
589 // Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.
590 //===----------------------------------------------------------------------===//
591 
592 void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
593   if (!filter.check_mkstemp)
594     return;
595 
596   StringRef Name = FD->getIdentifier()->getName();
597   std::pair<signed, signed> ArgSuffix =
598     llvm::StringSwitch<std::pair<signed, signed> >(Name)
599       .Case("mktemp", std::make_pair(0,-1))
600       .Case("mkstemp", std::make_pair(0,-1))
601       .Case("mkdtemp", std::make_pair(0,-1))
602       .Case("mkstemps", std::make_pair(0,1))
603       .Default(std::make_pair(-1, -1));
604 
605   assert(ArgSuffix.first >= 0 && "Unsupported function");
606 
607   // Check if the number of arguments is consistent with out expectations.
608   unsigned numArgs = CE->getNumArgs();
609   if ((signed) numArgs <= ArgSuffix.first)
610     return;
611 
612   const StringLiteral *strArg =
613     dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first)
614                               ->IgnoreParenImpCasts());
615 
616   // Currently we only handle string literals.  It is possible to do better,
617   // either by looking at references to const variables, or by doing real
618   // flow analysis.
619   if (!strArg || strArg->getCharByteWidth() != 1)
620     return;
621 
622   // Count the number of X's, taking into account a possible cutoff suffix.
623   StringRef str = strArg->getString();
624   unsigned numX = 0;
625   unsigned n = str.size();
626 
627   // Take into account the suffix.
628   unsigned suffix = 0;
629   if (ArgSuffix.second >= 0) {
630     const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second);
631     Expr::EvalResult EVResult;
632     if (!suffixEx->EvaluateAsInt(EVResult, BR.getContext()))
633       return;
634     llvm::APSInt Result = EVResult.Val.getInt();
635     // FIXME: Issue a warning.
636     if (Result.isNegative())
637       return;
638     suffix = (unsigned) Result.getZExtValue();
639     n = (n > suffix) ? n - suffix : 0;
640   }
641 
642   for (unsigned i = 0; i < n; ++i)
643     if (str[i] == 'X') ++numX;
644 
645   if (numX >= 6)
646     return;
647 
648   // Issue a warning.
649   PathDiagnosticLocation CELoc =
650     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
651   SmallString<512> buf;
652   llvm::raw_svector_ostream out(buf);
653   out << "Call to '" << Name << "' should have at least 6 'X's in the"
654     " format string to be secure (" << numX << " 'X'";
655   if (numX != 1)
656     out << 's';
657   out << " seen";
658   if (suffix) {
659     out << ", " << suffix << " character";
660     if (suffix > 1)
661       out << 's';
662     out << " used as a suffix";
663   }
664   out << ')';
665   BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
666                      "Insecure temporary file creation", "Security",
667                      out.str(), CELoc, strArg->getSourceRange());
668 }
669 
670 //===----------------------------------------------------------------------===//
671 // Check: Any use of 'strcpy' is insecure.
672 //
673 // CWE-119: Improper Restriction of Operations within
674 // the Bounds of a Memory Buffer
675 //===----------------------------------------------------------------------===//
676 
677 void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
678   if (!filter.check_strcpy)
679     return;
680 
681   if (!checkCall_strCommon(CE, FD))
682     return;
683 
684   const auto *Target = CE->getArg(0)->IgnoreImpCasts(),
685              *Source = CE->getArg(1)->IgnoreImpCasts();
686 
687   if (const auto *Array = dyn_cast<ConstantArrayType>(Target->getType())) {
688     uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;
689     if (const auto *String = dyn_cast<StringLiteral>(Source)) {
690       if (ArraySize >= String->getLength() + 1)
691         return;
692     }
693   }
694 
695   // Issue a warning.
696   PathDiagnosticLocation CELoc =
697     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
698   BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
699                      "Potential insecure memory buffer bounds restriction in "
700                      "call 'strcpy'",
701                      "Security",
702                      "Call to function 'strcpy' is insecure as it does not "
703                      "provide bounding of the memory buffer. Replace "
704                      "unbounded copy functions with analogous functions that "
705                      "support length arguments such as 'strlcpy'. CWE-119.",
706                      CELoc, CE->getCallee()->getSourceRange());
707 }
708 
709 //===----------------------------------------------------------------------===//
710 // Check: Any use of 'strcat' is insecure.
711 //
712 // CWE-119: Improper Restriction of Operations within
713 // the Bounds of a Memory Buffer
714 //===----------------------------------------------------------------------===//
715 
716 void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
717   if (!filter.check_strcpy)
718     return;
719 
720   if (!checkCall_strCommon(CE, FD))
721     return;
722 
723   // Issue a warning.
724   PathDiagnosticLocation CELoc =
725     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
726   BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
727                      "Potential insecure memory buffer bounds restriction in "
728                      "call 'strcat'",
729                      "Security",
730                      "Call to function 'strcat' is insecure as it does not "
731                      "provide bounding of the memory buffer. Replace "
732                      "unbounded copy functions with analogous functions that "
733                      "support length arguments such as 'strlcat'. CWE-119.",
734                      CELoc, CE->getCallee()->getSourceRange());
735 }
736 
737 //===----------------------------------------------------------------------===//
738 // Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',
739 //        'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
740 //        'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf',
741 //        'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset',
742 //        'fprintf' is deprecated since C11.
743 //
744 //        Use of 'sprintf', 'fprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',
745 //        'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
746 //        'swscanf', 'vsscanf', 'vswscanf' without buffer limitations
747 //        is insecure.
748 //
749 // CWE-119: Improper Restriction of Operations within
750 // the Bounds of a Memory Buffer
751 //===----------------------------------------------------------------------===//
752 
753 void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
754                                                     const FunctionDecl *FD) {
755   if (!filter.check_DeprecatedOrUnsafeBufferHandling)
756     return;
757 
758   if (!BR.getContext().getLangOpts().C11)
759     return;
760 
761   // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size
762   // restrictions).
763   enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 };
764 
765   StringRef Name = FD->getIdentifier()->getName();
766   if (Name.starts_with("__builtin_"))
767     Name = Name.substr(10);
768 
769   int ArgIndex =
770       llvm::StringSwitch<int>(Name)
771           .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0)
772           .Cases("fscanf", "fwscanf", "vfscanf", "vfwscanf", "sscanf",
773                  "swscanf", "vsscanf", "vswscanf", 1)
774           .Cases("sprintf", "vsprintf", "fprintf", 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 // i.e. rand(), random().
852 //
853 // E. Bach, "Efficient prediction of Marsaglia-Zaman random number generators,"
854 // in IEEE Transactions on Information Theory, vol. 44, no. 3, pp. 1253-1257,
855 // May 1998, https://doi.org/10.1109/18.669305
856 //
857 // CWE-338: Use of cryptographically weak prng
858 //===----------------------------------------------------------------------===//
859 
860 void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
861   if (!filter.check_rand || !CheckRand)
862     return;
863 
864   const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
865   if (!FTP)
866     return;
867 
868   if (FTP->getNumParams() == 1) {
869     // Is the argument an 'unsigned short *'?
870     // (Actually any integer type is allowed.)
871     const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>();
872     if (!PT)
873       return;
874 
875     if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())
876       return;
877   } else if (FTP->getNumParams() != 0)
878     return;
879 
880   // Issue a warning.
881   SmallString<256> buf1;
882   llvm::raw_svector_ostream os1(buf1);
883   os1 << '\'' << *FD << "' is a poor random number generator";
884 
885   SmallString<256> buf2;
886   llvm::raw_svector_ostream os2(buf2);
887   os2 << "Function '" << *FD
888       << "' is obsolete because it implements a poor random number generator."
889       << "  Use 'arc4random' instead";
890 
891   PathDiagnosticLocation CELoc =
892     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
893   BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
894                      "Security", os2.str(), CELoc,
895                      CE->getCallee()->getSourceRange());
896 }
897 
898 // See justification for rand().
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: The caller should always verify that the privileges
995 // were dropped successfully.
996 //
997 // Some library functions, like setuid() and setgid(), should always be used
998 // with a check of the return value to verify that the function completed
999 // successfully.  If the drop fails, the software will continue to run
1000 // with the raised privileges, which might provide additional access
1001 // to unprivileged users.
1002 //
1003 // (Note that this check predates __attribute__((warn_unused_result)).
1004 // Do we still need it now that we have a compiler warning for this?
1005 // Are these standard functions already annotated this way?)
1006 //===----------------------------------------------------------------------===//
1007 
1008 void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
1009   if (!filter.check_UncheckedReturn)
1010     return;
1011 
1012   const FunctionDecl *FD = CE->getDirectCallee();
1013   if (!FD)
1014     return;
1015 
1016   if (II_setid[0] == nullptr) {
1017     static const char * const identifiers[num_setids] = {
1018       "setuid", "setgid", "seteuid", "setegid",
1019       "setreuid", "setregid"
1020     };
1021 
1022     for (size_t i = 0; i < num_setids; i++)
1023       II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
1024   }
1025 
1026   const IdentifierInfo *id = FD->getIdentifier();
1027   size_t identifierid;
1028 
1029   for (identifierid = 0; identifierid < num_setids; identifierid++)
1030     if (id == II_setid[identifierid])
1031       break;
1032 
1033   if (identifierid >= num_setids)
1034     return;
1035 
1036   const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
1037   if (!FTP)
1038     return;
1039 
1040   // Verify that the function takes one or two arguments (depending on
1041   //   the function).
1042   if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2))
1043     return;
1044 
1045   // The arguments must be integers.
1046   for (unsigned i = 0; i < FTP->getNumParams(); i++)
1047     if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType())
1048       return;
1049 
1050   // Issue a warning.
1051   SmallString<256> buf1;
1052   llvm::raw_svector_ostream os1(buf1);
1053   os1 << "Return value is not checked in call to '" << *FD << '\'';
1054 
1055   SmallString<256> buf2;
1056   llvm::raw_svector_ostream os2(buf2);
1057   os2 << "The return value from the call to '" << *FD
1058       << "' is not checked.  If an error occurs in '" << *FD
1059       << "', the following code may execute with unexpected privileges";
1060 
1061   PathDiagnosticLocation CELoc =
1062     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
1063   BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
1064                      "Security", os2.str(), CELoc,
1065                      CE->getCallee()->getSourceRange());
1066 }
1067 
1068 //===----------------------------------------------------------------------===//
1069 // SecuritySyntaxChecker
1070 //===----------------------------------------------------------------------===//
1071 
1072 namespace {
1073 class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
1074 public:
1075   ChecksFilter filter;
1076 
1077   void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
1078                         BugReporter &BR) const {
1079     WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
1080     walker.Visit(D->getBody());
1081   }
1082 };
1083 }
1084 
1085 void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
1086   mgr.registerChecker<SecuritySyntaxChecker>();
1087 }
1088 
1089 bool ento::shouldRegisterSecuritySyntaxChecker(const CheckerManager &mgr) {
1090   return true;
1091 }
1092 
1093 #define REGISTER_CHECKER(name)                                                 \
1094   void ento::register##name(CheckerManager &mgr) {                             \
1095     SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>();  \
1096     checker->filter.check_##name = true;                                       \
1097     checker->filter.checkName_##name = mgr.getCurrentCheckerName();            \
1098   }                                                                            \
1099                                                                                \
1100   bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
1101 
1102 REGISTER_CHECKER(bcmp)
1103 REGISTER_CHECKER(bcopy)
1104 REGISTER_CHECKER(bzero)
1105 REGISTER_CHECKER(gets)
1106 REGISTER_CHECKER(getpw)
1107 REGISTER_CHECKER(mkstemp)
1108 REGISTER_CHECKER(mktemp)
1109 REGISTER_CHECKER(strcpy)
1110 REGISTER_CHECKER(rand)
1111 REGISTER_CHECKER(vfork)
1112 REGISTER_CHECKER(FloatLoopCounter)
1113 REGISTER_CHECKER(UncheckedReturn)
1114 REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)
1115 REGISTER_CHECKER(decodeValueOfObjCType)
1116