1 //==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==//
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
7 //===----------------------------------------------------------------------===//
9 // This file defines a set of flow-insensitive security checks.
11 //===----------------------------------------------------------------------===//
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"
24 using namespace clang;
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 ||
39 DefaultBool check_bcmp;
40 DefaultBool check_bcopy;
41 DefaultBool check_bzero;
42 DefaultBool check_gets;
43 DefaultBool check_getpw;
44 DefaultBool check_mktemp;
45 DefaultBool check_mkstemp;
46 DefaultBool check_strcpy;
47 DefaultBool check_DeprecatedOrUnsafeBufferHandling;
48 DefaultBool check_rand;
49 DefaultBool check_vfork;
50 DefaultBool check_FloatLoopCounter;
51 DefaultBool check_UncheckedReturn;
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;
68 class WalkAST : public StmtVisitor<WalkAST> {
70 AnalysisDeclContext* AC;
71 enum { num_setids = 6 };
72 IdentifierInfo *II_setid[num_setids];
75 const ChecksFilter &filter;
78 WalkAST(BugReporter &br, AnalysisDeclContext* ac,
79 const ChecksFilter &f)
80 : BR(br), AC(ac), II_setid(),
81 CheckRand(isArc4RandomAvailable(BR.getContext())),
84 // Statement visitor methods.
85 void VisitCallExpr(CallExpr *CE);
86 void VisitForStmt(ForStmt *S);
87 void VisitCompoundStmt (CompoundStmt *S);
88 void VisitStmt(Stmt *S) { VisitChildren(S); }
90 void VisitChildren(Stmt *S);
93 bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD);
95 typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *);
97 // Checker-specific methods.
98 void checkLoopConditionForFloat(const ForStmt *FS);
99 void checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD);
100 void checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD);
101 void checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD);
102 void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD);
103 void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD);
104 void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD);
105 void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD);
106 void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD);
107 void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD);
108 void checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
109 const FunctionDecl *FD);
110 void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);
111 void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);
112 void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD);
113 void checkUncheckedReturnValue(CallExpr *CE);
115 } // end anonymous namespace
117 //===----------------------------------------------------------------------===//
119 //===----------------------------------------------------------------------===//
121 void WalkAST::VisitChildren(Stmt *S) {
122 for (Stmt *Child : S->children())
127 void WalkAST::VisitCallExpr(CallExpr *CE) {
129 const FunctionDecl *FD = CE->getDirectCallee();
134 // Get the name of the callee. If it's a builtin, strip off the prefix.
135 IdentifierInfo *II = FD->getIdentifier();
136 if (!II) // if no identifier, not a simple C function
138 StringRef Name = II->getName();
139 if (Name.startswith("__builtin_"))
140 Name = Name.substr(10);
142 // Set the evaluation function by switching on the callee name.
143 FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
144 .Case("bcmp", &WalkAST::checkCall_bcmp)
145 .Case("bcopy", &WalkAST::checkCall_bcopy)
146 .Case("bzero", &WalkAST::checkCall_bzero)
147 .Case("gets", &WalkAST::checkCall_gets)
148 .Case("getpw", &WalkAST::checkCall_getpw)
149 .Case("mktemp", &WalkAST::checkCall_mktemp)
150 .Case("mkstemp", &WalkAST::checkCall_mkstemp)
151 .Case("mkdtemp", &WalkAST::checkCall_mkstemp)
152 .Case("mkstemps", &WalkAST::checkCall_mkstemp)
153 .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)
154 .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat)
155 .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf",
156 "vscanf", "vwscanf", "vfscanf", "vfwscanf",
157 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
158 .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf",
159 "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove",
160 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
161 .Cases("strncpy", "strncat", "memset",
162 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
163 .Case("drand48", &WalkAST::checkCall_rand)
164 .Case("erand48", &WalkAST::checkCall_rand)
165 .Case("jrand48", &WalkAST::checkCall_rand)
166 .Case("lrand48", &WalkAST::checkCall_rand)
167 .Case("mrand48", &WalkAST::checkCall_rand)
168 .Case("nrand48", &WalkAST::checkCall_rand)
169 .Case("lcong48", &WalkAST::checkCall_rand)
170 .Case("rand", &WalkAST::checkCall_rand)
171 .Case("rand_r", &WalkAST::checkCall_rand)
172 .Case("random", &WalkAST::checkCall_random)
173 .Case("vfork", &WalkAST::checkCall_vfork)
176 // If the callee isn't defined, it is not of security concern.
177 // Check and evaluate the call.
179 (this->*evalFunction)(CE, FD);
181 // Recurse and check children.
185 void WalkAST::VisitCompoundStmt(CompoundStmt *S) {
186 for (Stmt *Child : S->children())
188 if (CallExpr *CE = dyn_cast<CallExpr>(Child))
189 checkUncheckedReturnValue(CE);
194 void WalkAST::VisitForStmt(ForStmt *FS) {
195 checkLoopConditionForFloat(FS);
197 // Recurse and check children.
201 //===----------------------------------------------------------------------===//
202 // Check: floating point variable used as loop counter.
203 // Originally: <rdar://problem/6336718>
204 // Implements: CERT security coding advisory FLP-30.
205 //===----------------------------------------------------------------------===//
207 // Returns either 'x' or 'y', depending on which one of them is incremented
208 // in 'expr', or nullptr if none of them is incremented.
209 static const DeclRefExpr*
210 getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) {
211 expr = expr->IgnoreParenCasts();
213 if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) {
214 if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
215 B->getOpcode() == BO_Comma))
218 if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y))
221 if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y))
227 if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
228 const NamedDecl *ND = DR->getDecl();
229 return ND == x || ND == y ? DR : nullptr;
232 if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr))
233 return U->isIncrementDecrementOp()
234 ? getIncrementedVar(U->getSubExpr(), x, y) : nullptr;
239 /// CheckLoopConditionForFloat - This check looks for 'for' statements that
240 /// use a floating point variable as a loop counter.
241 /// CERT: FLP30-C, FLP30-CPP.
243 void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
244 if (!filter.check_FloatLoopCounter)
247 // Does the loop have a condition?
248 const Expr *condition = FS->getCond();
253 // Does the loop have an increment?
254 const Expr *increment = FS->getInc();
259 // Strip away '()' and casts.
260 condition = condition->IgnoreParenCasts();
261 increment = increment->IgnoreParenCasts();
263 // Is the loop condition a comparison?
264 const BinaryOperator *B = dyn_cast<BinaryOperator>(condition);
269 // Is this a comparison?
270 if (!(B->isRelationalOp() || B->isEqualityOp()))
273 // Are we comparing variables?
274 const DeclRefExpr *drLHS =
275 dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts());
276 const DeclRefExpr *drRHS =
277 dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts());
279 // Does at least one of the variables have a floating point type?
280 drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : nullptr;
281 drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : nullptr;
283 if (!drLHS && !drRHS)
286 const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : nullptr;
287 const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : nullptr;
289 if (!vdLHS && !vdRHS)
292 // Does either variable appear in increment?
293 const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS);
297 const VarDecl *vdInc = cast<VarDecl>(drInc->getDecl());
298 assert(vdInc && (vdInc == vdLHS || vdInc == vdRHS));
300 // Emit the error. First figure out which DeclRefExpr in the condition
301 // referenced the compared variable.
302 const DeclRefExpr *drCond = vdLHS == vdInc ? drLHS : drRHS;
304 SmallVector<SourceRange, 2> ranges;
305 SmallString<256> sbuf;
306 llvm::raw_svector_ostream os(sbuf);
308 os << "Variable '" << drCond->getDecl()->getName()
309 << "' with floating point type '" << drCond->getType().getAsString()
310 << "' should not be used as a loop counter";
312 ranges.push_back(drCond->getSourceRange());
313 ranges.push_back(drInc->getSourceRange());
315 const char *bugType = "Floating point variable used as loop counter";
317 PathDiagnosticLocation FSLoc =
318 PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);
319 BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
320 bugType, "Security", os.str(),
324 //===----------------------------------------------------------------------===//
325 // Check: Any use of bcmp.
326 // CWE-477: Use of Obsolete Functions
327 // bcmp was deprecated in POSIX.1-2008
328 //===----------------------------------------------------------------------===//
330 void WalkAST::checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD) {
331 if (!filter.check_bcmp)
334 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
338 // Verify that the function takes three arguments.
339 if (FPT->getNumParams() != 3)
342 for (int i = 0; i < 2; i++) {
343 // Verify the first and second argument type is void*.
344 const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
348 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
352 // Verify the third argument type is integer.
353 if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
357 PathDiagnosticLocation CELoc =
358 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
359 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp,
360 "Use of deprecated function in call to 'bcmp()'",
362 "The bcmp() function is obsoleted by memcmp().",
363 CELoc, CE->getCallee()->getSourceRange());
366 //===----------------------------------------------------------------------===//
367 // Check: Any use of bcopy.
368 // CWE-477: Use of Obsolete Functions
369 // bcopy was deprecated in POSIX.1-2008
370 //===----------------------------------------------------------------------===//
372 void WalkAST::checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD) {
373 if (!filter.check_bcopy)
376 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
380 // Verify that the function takes three arguments.
381 if (FPT->getNumParams() != 3)
384 for (int i = 0; i < 2; i++) {
385 // Verify the first and second argument type is void*.
386 const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
390 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
394 // Verify the third argument type is integer.
395 if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
399 PathDiagnosticLocation CELoc =
400 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
401 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy,
402 "Use of deprecated function in call to 'bcopy()'",
404 "The bcopy() function is obsoleted by memcpy() "
406 CELoc, CE->getCallee()->getSourceRange());
409 //===----------------------------------------------------------------------===//
410 // Check: Any use of bzero.
411 // CWE-477: Use of Obsolete Functions
412 // bzero was deprecated in POSIX.1-2008
413 //===----------------------------------------------------------------------===//
415 void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) {
416 if (!filter.check_bzero)
419 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
423 // Verify that the function takes two arguments.
424 if (FPT->getNumParams() != 2)
427 // Verify the first argument type is void*.
428 const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
432 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
435 // Verify the second argument type is integer.
436 if (!FPT->getParamType(1)->isIntegralOrUnscopedEnumerationType())
440 PathDiagnosticLocation CELoc =
441 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
442 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero,
443 "Use of deprecated function in call to 'bzero()'",
445 "The bzero() function is obsoleted by memset().",
446 CELoc, CE->getCallee()->getSourceRange());
450 //===----------------------------------------------------------------------===//
451 // Check: Any use of 'gets' is insecure.
452 // Originally: <rdar://problem/6335715>
453 // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)
454 // CWE-242: Use of Inherently Dangerous Function
455 //===----------------------------------------------------------------------===//
457 void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {
458 if (!filter.check_gets)
461 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
465 // Verify that the function takes a single argument.
466 if (FPT->getNumParams() != 1)
469 // Is the argument a 'char*'?
470 const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
474 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
478 PathDiagnosticLocation CELoc =
479 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
480 BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
481 "Potential buffer overflow in call to 'gets'",
483 "Call to function 'gets' is extremely insecure as it can "
484 "always result in a buffer overflow",
485 CELoc, CE->getCallee()->getSourceRange());
488 //===----------------------------------------------------------------------===//
489 // Check: Any use of 'getpwd' is insecure.
490 // CWE-477: Use of Obsolete Functions
491 //===----------------------------------------------------------------------===//
493 void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {
494 if (!filter.check_getpw)
497 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
501 // Verify that the function takes two arguments.
502 if (FPT->getNumParams() != 2)
505 // Verify the first argument type is integer.
506 if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType())
509 // Verify the second argument type is char*.
510 const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>();
514 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
518 PathDiagnosticLocation CELoc =
519 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
520 BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
521 "Potential buffer overflow in call to 'getpw'",
523 "The getpw() function is dangerous as it may overflow the "
524 "provided buffer. It is obsoleted by getpwuid().",
525 CELoc, CE->getCallee()->getSourceRange());
528 //===----------------------------------------------------------------------===//
529 // Check: Any use of 'mktemp' is insecure. It is obsoleted by mkstemp().
530 // CWE-377: Insecure Temporary File
531 //===----------------------------------------------------------------------===//
533 void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
534 if (!filter.check_mktemp) {
535 // Fall back to the security check of looking for enough 'X's in the
536 // format string, since that is a less severe warning.
537 checkCall_mkstemp(CE, FD);
541 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
545 // Verify that the function takes a single argument.
546 if (FPT->getNumParams() != 1)
549 // Verify that the argument is Pointer Type.
550 const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
554 // Verify that the argument is a 'char*'.
555 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
559 PathDiagnosticLocation CELoc =
560 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
561 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
562 "Potential insecure temporary file in call 'mktemp'",
564 "Call to function 'mktemp' is insecure as it always "
565 "creates or uses insecure temporary file. Use 'mkstemp' "
567 CELoc, CE->getCallee()->getSourceRange());
570 //===----------------------------------------------------------------------===//
571 // Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.
572 //===----------------------------------------------------------------------===//
574 void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
575 if (!filter.check_mkstemp)
578 StringRef Name = FD->getIdentifier()->getName();
579 std::pair<signed, signed> ArgSuffix =
580 llvm::StringSwitch<std::pair<signed, signed> >(Name)
581 .Case("mktemp", std::make_pair(0,-1))
582 .Case("mkstemp", std::make_pair(0,-1))
583 .Case("mkdtemp", std::make_pair(0,-1))
584 .Case("mkstemps", std::make_pair(0,1))
585 .Default(std::make_pair(-1, -1));
587 assert(ArgSuffix.first >= 0 && "Unsupported function");
589 // Check if the number of arguments is consistent with out expectations.
590 unsigned numArgs = CE->getNumArgs();
591 if ((signed) numArgs <= ArgSuffix.first)
594 const StringLiteral *strArg =
595 dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first)
596 ->IgnoreParenImpCasts());
598 // Currently we only handle string literals. It is possible to do better,
599 // either by looking at references to const variables, or by doing real
601 if (!strArg || strArg->getCharByteWidth() != 1)
604 // Count the number of X's, taking into account a possible cutoff suffix.
605 StringRef str = strArg->getString();
607 unsigned n = str.size();
609 // Take into account the suffix.
611 if (ArgSuffix.second >= 0) {
612 const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second);
613 Expr::EvalResult EVResult;
614 if (!suffixEx->EvaluateAsInt(EVResult, BR.getContext()))
616 llvm::APSInt Result = EVResult.Val.getInt();
617 // FIXME: Issue a warning.
618 if (Result.isNegative())
620 suffix = (unsigned) Result.getZExtValue();
621 n = (n > suffix) ? n - suffix : 0;
624 for (unsigned i = 0; i < n; ++i)
625 if (str[i] == 'X') ++numX;
631 PathDiagnosticLocation CELoc =
632 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
633 SmallString<512> buf;
634 llvm::raw_svector_ostream out(buf);
635 out << "Call to '" << Name << "' should have at least 6 'X's in the"
636 " format string to be secure (" << numX << " 'X'";
641 out << ", " << suffix << " character";
644 out << " used as a suffix";
647 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
648 "Insecure temporary file creation", "Security",
649 out.str(), CELoc, strArg->getSourceRange());
652 //===----------------------------------------------------------------------===//
653 // Check: Any use of 'strcpy' is insecure.
655 // CWE-119: Improper Restriction of Operations within
656 // the Bounds of a Memory Buffer
657 //===----------------------------------------------------------------------===//
659 void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
660 if (!filter.check_strcpy)
663 if (!checkCall_strCommon(CE, FD))
666 const auto *Target = CE->getArg(0)->IgnoreImpCasts(),
667 *Source = CE->getArg(1)->IgnoreImpCasts();
669 if (const auto *Array = dyn_cast<ConstantArrayType>(Target->getType())) {
670 uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;
671 if (const auto *String = dyn_cast<StringLiteral>(Source)) {
672 if (ArraySize >= String->getLength() + 1)
678 PathDiagnosticLocation CELoc =
679 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
680 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
681 "Potential insecure memory buffer bounds restriction in "
684 "Call to function 'strcpy' is insecure as it does not "
685 "provide bounding of the memory buffer. Replace "
686 "unbounded copy functions with analogous functions that "
687 "support length arguments such as 'strlcpy'. CWE-119.",
688 CELoc, CE->getCallee()->getSourceRange());
691 //===----------------------------------------------------------------------===//
692 // Check: Any use of 'strcat' is insecure.
694 // CWE-119: Improper Restriction of Operations within
695 // the Bounds of a Memory Buffer
696 //===----------------------------------------------------------------------===//
698 void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
699 if (!filter.check_strcpy)
702 if (!checkCall_strCommon(CE, FD))
706 PathDiagnosticLocation CELoc =
707 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
708 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
709 "Potential insecure memory buffer bounds restriction in "
712 "Call to function 'strcat' is insecure as it does not "
713 "provide bounding of the memory buffer. Replace "
714 "unbounded copy functions with analogous functions that "
715 "support length arguments such as 'strlcat'. CWE-119.",
716 CELoc, CE->getCallee()->getSourceRange());
719 //===----------------------------------------------------------------------===//
720 // Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',
721 // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
722 // 'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf',
723 // 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset'
724 // is deprecated since C11.
726 // Use of 'sprintf', 'vsprintf', 'scanf', 'wscanf','fscanf',
727 // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
728 // 'swscanf', 'vsscanf', 'vswscanf' without buffer limitations
731 // CWE-119: Improper Restriction of Operations within
732 // the Bounds of a Memory Buffer
733 //===----------------------------------------------------------------------===//
735 void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
736 const FunctionDecl *FD) {
737 if (!filter.check_DeprecatedOrUnsafeBufferHandling)
740 if (!BR.getContext().getLangOpts().C11)
743 // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size
745 enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 };
747 StringRef Name = FD->getIdentifier()->getName();
748 if (Name.startswith("__builtin_"))
749 Name = Name.substr(10);
752 llvm::StringSwitch<int>(Name)
753 .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0)
754 .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf",
755 "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1)
756 .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy",
757 "memmove", "memset", "strncpy", "strncat", DEPR_ONLY)
758 .Default(UNKNOWN_CALL);
760 assert(ArgIndex != UNKNOWN_CALL && "Unsupported function");
761 bool BoundsProvided = ArgIndex == DEPR_ONLY;
763 if (!BoundsProvided) {
764 // Currently we only handle (not wide) string literals. It is possible to do
765 // better, either by looking at references to const variables, or by doing
766 // real flow analysis.
768 dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts());
770 FormatString->getString().find("%s") == StringRef::npos &&
771 FormatString->getString().find("%[") == StringRef::npos)
772 BoundsProvided = true;
775 SmallString<128> Buf1;
776 SmallString<512> Buf2;
777 llvm::raw_svector_ostream Out1(Buf1);
778 llvm::raw_svector_ostream Out2(Buf2);
780 Out1 << "Potential insecure memory buffer bounds restriction in call '"
782 Out2 << "Call to function '" << Name
783 << "' is insecure as it does not provide ";
785 if (!BoundsProvided) {
786 Out2 << "bounding of the memory buffer or ";
789 Out2 << "security checks introduced "
790 "in the C11 standard. Replace with analogous functions that "
791 "support length arguments or provides boundary checks such as '"
792 << Name << "_s' in case of C11";
794 PathDiagnosticLocation CELoc =
795 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
796 BR.EmitBasicReport(AC->getDecl(),
797 filter.checkName_DeprecatedOrUnsafeBufferHandling,
798 Out1.str(), "Security", Out2.str(), CELoc,
799 CE->getCallee()->getSourceRange());
802 //===----------------------------------------------------------------------===//
803 // Common check for str* functions with no bounds parameters.
804 //===----------------------------------------------------------------------===//
806 bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) {
807 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
811 // Verify the function takes two arguments, three in the _chk version.
812 int numArgs = FPT->getNumParams();
813 if (numArgs != 2 && numArgs != 3)
816 // Verify the type for both arguments.
817 for (int i = 0; i < 2; i++) {
818 // Verify that the arguments are pointers.
819 const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
823 // Verify that the argument is a 'char*'.
824 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
831 //===----------------------------------------------------------------------===//
832 // Check: Linear congruent random number generators should not be used
833 // Originally: <rdar://problem/63371000>
834 // CWE-338: Use of cryptographically weak prng
835 //===----------------------------------------------------------------------===//
837 void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
838 if (!filter.check_rand || !CheckRand)
841 const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
845 if (FTP->getNumParams() == 1) {
846 // Is the argument an 'unsigned short *'?
847 // (Actually any integer type is allowed.)
848 const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>();
852 if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())
854 } else if (FTP->getNumParams() != 0)
858 SmallString<256> buf1;
859 llvm::raw_svector_ostream os1(buf1);
860 os1 << '\'' << *FD << "' is a poor random number generator";
862 SmallString<256> buf2;
863 llvm::raw_svector_ostream os2(buf2);
864 os2 << "Function '" << *FD
865 << "' is obsolete because it implements a poor random number generator."
866 << " Use 'arc4random' instead";
868 PathDiagnosticLocation CELoc =
869 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
870 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
871 "Security", os2.str(), CELoc,
872 CE->getCallee()->getSourceRange());
875 //===----------------------------------------------------------------------===//
876 // Check: 'random' should not be used
877 // Originally: <rdar://problem/63371000>
878 //===----------------------------------------------------------------------===//
880 void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {
881 if (!CheckRand || !filter.check_rand)
884 const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
888 // Verify that the function takes no argument.
889 if (FTP->getNumParams() != 0)
893 PathDiagnosticLocation CELoc =
894 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
895 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
896 "'random' is not a secure random number generator",
898 "The 'random' function produces a sequence of values that "
899 "an adversary may be able to predict. Use 'arc4random' "
900 "instead", CELoc, CE->getCallee()->getSourceRange());
903 //===----------------------------------------------------------------------===//
904 // Check: 'vfork' should not be used.
905 // POS33-C: Do not use vfork().
906 //===----------------------------------------------------------------------===//
908 void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
909 if (!filter.check_vfork)
912 // All calls to vfork() are insecure, issue a warning.
913 PathDiagnosticLocation CELoc =
914 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
915 BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
916 "Potential insecure implementation-specific behavior in "
919 "Call to function 'vfork' is insecure as it can lead to "
920 "denial of service situations in the parent process. "
921 "Replace calls to vfork with calls to the safer "
922 "'posix_spawn' function",
923 CELoc, CE->getCallee()->getSourceRange());
926 //===----------------------------------------------------------------------===//
927 // Check: Should check whether privileges are dropped successfully.
928 // Originally: <rdar://problem/6337132>
929 //===----------------------------------------------------------------------===//
931 void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
932 if (!filter.check_UncheckedReturn)
935 const FunctionDecl *FD = CE->getDirectCallee();
939 if (II_setid[0] == nullptr) {
940 static const char * const identifiers[num_setids] = {
941 "setuid", "setgid", "seteuid", "setegid",
942 "setreuid", "setregid"
945 for (size_t i = 0; i < num_setids; i++)
946 II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
949 const IdentifierInfo *id = FD->getIdentifier();
952 for (identifierid = 0; identifierid < num_setids; identifierid++)
953 if (id == II_setid[identifierid])
956 if (identifierid >= num_setids)
959 const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
963 // Verify that the function takes one or two arguments (depending on
965 if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2))
968 // The arguments must be integers.
969 for (unsigned i = 0; i < FTP->getNumParams(); i++)
970 if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType())
974 SmallString<256> buf1;
975 llvm::raw_svector_ostream os1(buf1);
976 os1 << "Return value is not checked in call to '" << *FD << '\'';
978 SmallString<256> buf2;
979 llvm::raw_svector_ostream os2(buf2);
980 os2 << "The return value from the call to '" << *FD
981 << "' is not checked. If an error occurs in '" << *FD
982 << "', the following code may execute with unexpected privileges";
984 PathDiagnosticLocation CELoc =
985 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
986 BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
987 "Security", os2.str(), CELoc,
988 CE->getCallee()->getSourceRange());
991 //===----------------------------------------------------------------------===//
992 // SecuritySyntaxChecker
993 //===----------------------------------------------------------------------===//
996 class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
1000 void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
1001 BugReporter &BR) const {
1002 WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
1003 walker.Visit(D->getBody());
1008 void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
1009 mgr.registerChecker<SecuritySyntaxChecker>();
1012 bool ento::shouldRegisterSecuritySyntaxChecker(const LangOptions &LO) {
1016 #define REGISTER_CHECKER(name) \
1017 void ento::register##name(CheckerManager &mgr) { \
1018 SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \
1019 checker->filter.check_##name = true; \
1020 checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \
1023 bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
1025 REGISTER_CHECKER(bcmp)
1026 REGISTER_CHECKER(bcopy)
1027 REGISTER_CHECKER(bzero)
1028 REGISTER_CHECKER(gets)
1029 REGISTER_CHECKER(getpw)
1030 REGISTER_CHECKER(mkstemp)
1031 REGISTER_CHECKER(mktemp)
1032 REGISTER_CHECKER(strcpy)
1033 REGISTER_CHECKER(rand)
1034 REGISTER_CHECKER(vfork)
1035 REGISTER_CHECKER(FloatLoopCounter)
1036 REGISTER_CHECKER(UncheckedReturn)
1037 REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)