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 
isArc4RandomAvailable(const ASTContext & Ctx)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:
WalkAST(BugReporter & br,AnalysisDeclContext * ac,const ChecksFilter & f)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);
VisitStmt(Stmt * 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 
VisitChildren(Stmt * S)125 void WalkAST::VisitChildren(Stmt *S) {
126   for (Stmt *Child : S->children())
127     if (Child)
128       Visit(Child);
129 }
130 
VisitCallExpr(CallExpr * CE)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   Name.consume_front("__builtin_");
144 
145   // Set the evaluation function by switching on the callee name.
146   FnCheck evalFunction =
147       llvm::StringSwitch<FnCheck>(Name)
148           .Case("bcmp", &WalkAST::checkCall_bcmp)
149           .Case("bcopy", &WalkAST::checkCall_bcopy)
150           .Case("bzero", &WalkAST::checkCall_bzero)
151           .Case("gets", &WalkAST::checkCall_gets)
152           .Case("getpw", &WalkAST::checkCall_getpw)
153           .Case("mktemp", &WalkAST::checkCall_mktemp)
154           .Case("mkstemp", &WalkAST::checkCall_mkstemp)
155           .Case("mkdtemp", &WalkAST::checkCall_mkstemp)
156           .Case("mkstemps", &WalkAST::checkCall_mkstemp)
157           .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)
158           .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat)
159           .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf",
160                  "vscanf", "vwscanf", "vfscanf", "vfwscanf",
161                  &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
162           .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf",
163                  "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove",
164                  &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
165           .Cases("strncpy", "strncat", "memset", "fprintf",
166                  &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
167           .Case("drand48", &WalkAST::checkCall_rand)
168           .Case("erand48", &WalkAST::checkCall_rand)
169           .Case("jrand48", &WalkAST::checkCall_rand)
170           .Case("lrand48", &WalkAST::checkCall_rand)
171           .Case("mrand48", &WalkAST::checkCall_rand)
172           .Case("nrand48", &WalkAST::checkCall_rand)
173           .Case("lcong48", &WalkAST::checkCall_rand)
174           .Case("rand", &WalkAST::checkCall_rand)
175           .Case("rand_r", &WalkAST::checkCall_rand)
176           .Case("random", &WalkAST::checkCall_random)
177           .Case("vfork", &WalkAST::checkCall_vfork)
178           .Default(nullptr);
179 
180   // If the callee isn't defined, it is not of security concern.
181   // Check and evaluate the call.
182   if (evalFunction)
183     (this->*evalFunction)(CE, FD);
184 
185   // Recurse and check children.
186   VisitChildren(CE);
187 }
188 
VisitObjCMessageExpr(ObjCMessageExpr * ME)189 void WalkAST::VisitObjCMessageExpr(ObjCMessageExpr *ME) {
190   MsgCheck evalFunction =
191       llvm::StringSwitch<MsgCheck>(ME->getSelector().getAsString())
192           .Case("decodeValueOfObjCType:at:",
193                 &WalkAST::checkMsg_decodeValueOfObjCType)
194           .Default(nullptr);
195 
196   if (evalFunction)
197     (this->*evalFunction)(ME);
198 
199   // Recurse and check children.
200   VisitChildren(ME);
201 }
202 
VisitCompoundStmt(CompoundStmt * S)203 void WalkAST::VisitCompoundStmt(CompoundStmt *S) {
204   for (Stmt *Child : S->children())
205     if (Child) {
206       if (CallExpr *CE = dyn_cast<CallExpr>(Child))
207         checkUncheckedReturnValue(CE);
208       Visit(Child);
209     }
210 }
211 
VisitForStmt(ForStmt * FS)212 void WalkAST::VisitForStmt(ForStmt *FS) {
213   checkLoopConditionForFloat(FS);
214 
215   // Recurse and check children.
216   VisitChildren(FS);
217 }
218 
219 //===----------------------------------------------------------------------===//
220 // Check: floating point variable used as loop counter.
221 // Implements: CERT security coding advisory FLP-30.
222 //===----------------------------------------------------------------------===//
223 
224 // Returns either 'x' or 'y', depending on which one of them is incremented
225 // in 'expr', or nullptr if none of them is incremented.
226 static const DeclRefExpr*
getIncrementedVar(const Expr * expr,const VarDecl * x,const VarDecl * y)227 getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) {
228   expr = expr->IgnoreParenCasts();
229 
230   if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) {
231     if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
232           B->getOpcode() == BO_Comma))
233       return nullptr;
234 
235     if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y))
236       return lhs;
237 
238     if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y))
239       return rhs;
240 
241     return nullptr;
242   }
243 
244   if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
245     const NamedDecl *ND = DR->getDecl();
246     return ND == x || ND == y ? DR : nullptr;
247   }
248 
249   if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr))
250     return U->isIncrementDecrementOp()
251       ? getIncrementedVar(U->getSubExpr(), x, y) : nullptr;
252 
253   return nullptr;
254 }
255 
256 /// CheckLoopConditionForFloat - This check looks for 'for' statements that
257 ///  use a floating point variable as a loop counter.
258 ///  CERT: FLP30-C, FLP30-CPP.
259 ///
checkLoopConditionForFloat(const ForStmt * FS)260 void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
261   if (!filter.check_FloatLoopCounter)
262     return;
263 
264   // Does the loop have a condition?
265   const Expr *condition = FS->getCond();
266 
267   if (!condition)
268     return;
269 
270   // Does the loop have an increment?
271   const Expr *increment = FS->getInc();
272 
273   if (!increment)
274     return;
275 
276   // Strip away '()' and casts.
277   condition = condition->IgnoreParenCasts();
278   increment = increment->IgnoreParenCasts();
279 
280   // Is the loop condition a comparison?
281   const BinaryOperator *B = dyn_cast<BinaryOperator>(condition);
282 
283   if (!B)
284     return;
285 
286   // Is this a comparison?
287   if (!(B->isRelationalOp() || B->isEqualityOp()))
288     return;
289 
290   // Are we comparing variables?
291   const DeclRefExpr *drLHS =
292     dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts());
293   const DeclRefExpr *drRHS =
294     dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts());
295 
296   // Does at least one of the variables have a floating point type?
297   drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : nullptr;
298   drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : nullptr;
299 
300   if (!drLHS && !drRHS)
301     return;
302 
303   const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : nullptr;
304   const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : nullptr;
305 
306   if (!vdLHS && !vdRHS)
307     return;
308 
309   // Does either variable appear in increment?
310   const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS);
311   if (!drInc)
312     return;
313 
314   const VarDecl *vdInc = cast<VarDecl>(drInc->getDecl());
315   assert(vdInc && (vdInc == vdLHS || vdInc == vdRHS));
316 
317   // Emit the error.  First figure out which DeclRefExpr in the condition
318   // referenced the compared variable.
319   const DeclRefExpr *drCond = vdLHS == vdInc ? drLHS : drRHS;
320 
321   SmallVector<SourceRange, 2> ranges;
322   SmallString<256> sbuf;
323   llvm::raw_svector_ostream os(sbuf);
324 
325   os << "Variable '" << drCond->getDecl()->getName()
326      << "' with floating point type '" << drCond->getType()
327      << "' should not be used as a loop counter";
328 
329   ranges.push_back(drCond->getSourceRange());
330   ranges.push_back(drInc->getSourceRange());
331 
332   const char *bugType = "Floating point variable used as loop counter";
333 
334   PathDiagnosticLocation FSLoc =
335     PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);
336   BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
337                      bugType, "Security", os.str(),
338                      FSLoc, ranges);
339 }
340 
341 //===----------------------------------------------------------------------===//
342 // Check: Any use of bcmp.
343 // CWE-477: Use of Obsolete Functions
344 // bcmp was deprecated in POSIX.1-2008
345 //===----------------------------------------------------------------------===//
346 
checkCall_bcmp(const CallExpr * CE,const FunctionDecl * FD)347 void WalkAST::checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD) {
348   if (!filter.check_bcmp)
349     return;
350 
351   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
352   if (!FPT)
353     return;
354 
355   // Verify that the function takes three arguments.
356   if (FPT->getNumParams() != 3)
357     return;
358 
359   for (int i = 0; i < 2; i++) {
360     // Verify the first and second argument type is void*.
361     const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
362     if (!PT)
363       return;
364 
365     if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
366       return;
367   }
368 
369   // Verify the third argument type is integer.
370   if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
371     return;
372 
373   // Issue a warning.
374   PathDiagnosticLocation CELoc =
375     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
376   BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp,
377                      "Use of deprecated function in call to 'bcmp()'",
378                      "Security",
379                      "The bcmp() function is obsoleted by memcmp().",
380                      CELoc, CE->getCallee()->getSourceRange());
381 }
382 
383 //===----------------------------------------------------------------------===//
384 // Check: Any use of bcopy.
385 // CWE-477: Use of Obsolete Functions
386 // bcopy was deprecated in POSIX.1-2008
387 //===----------------------------------------------------------------------===//
388 
checkCall_bcopy(const CallExpr * CE,const FunctionDecl * FD)389 void WalkAST::checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD) {
390   if (!filter.check_bcopy)
391     return;
392 
393   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
394   if (!FPT)
395     return;
396 
397   // Verify that the function takes three arguments.
398   if (FPT->getNumParams() != 3)
399     return;
400 
401   for (int i = 0; i < 2; i++) {
402     // Verify the first and second argument type is void*.
403     const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
404     if (!PT)
405       return;
406 
407     if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
408       return;
409   }
410 
411   // Verify the third argument type is integer.
412   if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
413     return;
414 
415   // Issue a warning.
416   PathDiagnosticLocation CELoc =
417     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
418   BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy,
419                      "Use of deprecated function in call to 'bcopy()'",
420                      "Security",
421                      "The bcopy() function is obsoleted by memcpy() "
422                      "or memmove().",
423                      CELoc, CE->getCallee()->getSourceRange());
424 }
425 
426 //===----------------------------------------------------------------------===//
427 // Check: Any use of bzero.
428 // CWE-477: Use of Obsolete Functions
429 // bzero was deprecated in POSIX.1-2008
430 //===----------------------------------------------------------------------===//
431 
checkCall_bzero(const CallExpr * CE,const FunctionDecl * FD)432 void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) {
433   if (!filter.check_bzero)
434     return;
435 
436   const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
437   if (!FPT)
438     return;
439 
440   // Verify that the function takes two arguments.
441   if (FPT->getNumParams() != 2)
442     return;
443 
444   // Verify the first argument type is void*.
445   const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
446   if (!PT)
447     return;
448 
449   if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
450     return;
451 
452   // Verify the second argument type is integer.
453   if (!FPT->getParamType(1)->isIntegralOrUnscopedEnumerationType())
454     return;
455 
456   // Issue a warning.
457   PathDiagnosticLocation CELoc =
458     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
459   BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero,
460                      "Use of deprecated function in call to 'bzero()'",
461                      "Security",
462                      "The bzero() function is obsoleted by memset().",
463                      CELoc, CE->getCallee()->getSourceRange());
464 }
465 
466 
467 //===----------------------------------------------------------------------===//
468 // Check: Any use of 'gets' is insecure. Most man pages literally says this.
469 //
470 // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)
471 // CWE-242: Use of Inherently Dangerous Function
472 //===----------------------------------------------------------------------===//
473 
checkCall_gets(const CallExpr * CE,const FunctionDecl * FD)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 
checkCall_getpw(const CallExpr * CE,const FunctionDecl * FD)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 
checkCall_mktemp(const CallExpr * CE,const FunctionDecl * FD)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 
checkCall_mkstemp(const CallExpr * CE,const FunctionDecl * FD)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 
checkCall_strcpy(const CallExpr * CE,const FunctionDecl * FD)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 
checkCall_strcat(const CallExpr * CE,const FunctionDecl * FD)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 //        'fprintf' is deprecated since C11.
742 //
743 //        Use of 'sprintf', 'fprintf', '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 
checkDeprecatedOrUnsafeBufferHandling(const CallExpr * CE,const FunctionDecl * FD)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   Name.consume_front("__builtin_");
766 
767   int ArgIndex =
768       llvm::StringSwitch<int>(Name)
769           .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0)
770           .Cases("fscanf", "fwscanf", "vfscanf", "vfwscanf", "sscanf",
771                  "swscanf", "vsscanf", "vswscanf", 1)
772           .Cases("sprintf", "vsprintf", "fprintf", 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 
checkCall_strCommon(const CallExpr * CE,const FunctionDecl * FD)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 // i.e. rand(), random().
850 //
851 // E. Bach, "Efficient prediction of Marsaglia-Zaman random number generators,"
852 // in IEEE Transactions on Information Theory, vol. 44, no. 3, pp. 1253-1257,
853 // May 1998, https://doi.org/10.1109/18.669305
854 //
855 // CWE-338: Use of cryptographically weak prng
856 //===----------------------------------------------------------------------===//
857 
checkCall_rand(const CallExpr * CE,const FunctionDecl * FD)858 void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
859   if (!filter.check_rand || !CheckRand)
860     return;
861 
862   const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
863   if (!FTP)
864     return;
865 
866   if (FTP->getNumParams() == 1) {
867     // Is the argument an 'unsigned short *'?
868     // (Actually any integer type is allowed.)
869     const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>();
870     if (!PT)
871       return;
872 
873     if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())
874       return;
875   } else if (FTP->getNumParams() != 0)
876     return;
877 
878   // Issue a warning.
879   SmallString<256> buf1;
880   llvm::raw_svector_ostream os1(buf1);
881   os1 << '\'' << *FD << "' is a poor random number generator";
882 
883   SmallString<256> buf2;
884   llvm::raw_svector_ostream os2(buf2);
885   os2 << "Function '" << *FD
886       << "' is obsolete because it implements a poor random number generator."
887       << "  Use 'arc4random' instead";
888 
889   PathDiagnosticLocation CELoc =
890     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
891   BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
892                      "Security", os2.str(), CELoc,
893                      CE->getCallee()->getSourceRange());
894 }
895 
896 // See justification for rand().
checkCall_random(const CallExpr * CE,const FunctionDecl * FD)897 void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {
898   if (!CheckRand || !filter.check_rand)
899     return;
900 
901   const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
902   if (!FTP)
903     return;
904 
905   // Verify that the function takes no argument.
906   if (FTP->getNumParams() != 0)
907     return;
908 
909   // Issue a warning.
910   PathDiagnosticLocation CELoc =
911     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
912   BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
913                      "'random' is not a secure random number generator",
914                      "Security",
915                      "The 'random' function produces a sequence of values that "
916                      "an adversary may be able to predict.  Use 'arc4random' "
917                      "instead", CELoc, CE->getCallee()->getSourceRange());
918 }
919 
920 //===----------------------------------------------------------------------===//
921 // Check: 'vfork' should not be used.
922 // POS33-C: Do not use vfork().
923 //===----------------------------------------------------------------------===//
924 
checkCall_vfork(const CallExpr * CE,const FunctionDecl * FD)925 void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
926   if (!filter.check_vfork)
927     return;
928 
929   // All calls to vfork() are insecure, issue a warning.
930   PathDiagnosticLocation CELoc =
931     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
932   BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
933                      "Potential insecure implementation-specific behavior in "
934                      "call 'vfork'",
935                      "Security",
936                      "Call to function 'vfork' is insecure as it can lead to "
937                      "denial of service situations in the parent process. "
938                      "Replace calls to vfork with calls to the safer "
939                      "'posix_spawn' function",
940                      CELoc, CE->getCallee()->getSourceRange());
941 }
942 
943 //===----------------------------------------------------------------------===//
944 // Check: '-decodeValueOfObjCType:at:' should not be used.
945 // It is deprecated in favor of '-decodeValueOfObjCType:at:size:' due to
946 // likelihood of buffer overflows.
947 //===----------------------------------------------------------------------===//
948 
checkMsg_decodeValueOfObjCType(const ObjCMessageExpr * ME)949 void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) {
950   if (!filter.check_decodeValueOfObjCType)
951     return;
952 
953   // Check availability of the secure alternative:
954   // iOS 11+, macOS 10.13+, tvOS 11+, and watchOS 4.0+
955   // FIXME: We probably shouldn't register the check if it's not available.
956   const TargetInfo &TI = AC->getASTContext().getTargetInfo();
957   const llvm::Triple &T = TI.getTriple();
958   const VersionTuple &VT = TI.getPlatformMinVersion();
959   switch (T.getOS()) {
960   case llvm::Triple::IOS:
961     if (VT < VersionTuple(11, 0))
962       return;
963     break;
964   case llvm::Triple::MacOSX:
965     if (VT < VersionTuple(10, 13))
966       return;
967     break;
968   case llvm::Triple::WatchOS:
969     if (VT < VersionTuple(4, 0))
970       return;
971     break;
972   case llvm::Triple::TvOS:
973     if (VT < VersionTuple(11, 0))
974       return;
975     break;
976   case llvm::Triple::XROS:
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 
checkUncheckedReturnValue(CallExpr * CE)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 
checkASTCodeBody(const Decl * D,AnalysisManager & mgr,BugReporter & BR) const1077   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 
registerSecuritySyntaxChecker(CheckerManager & mgr)1085 void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
1086   mgr.registerChecker<SecuritySyntaxChecker>();
1087 }
1088 
shouldRegisterSecuritySyntaxChecker(const CheckerManager & mgr)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