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 CheckName checkName_bcmp;
54 CheckName checkName_bcopy;
55 CheckName checkName_bzero;
56 CheckName checkName_gets;
57 CheckName checkName_getpw;
58 CheckName checkName_mktemp;
59 CheckName checkName_mkstemp;
60 CheckName checkName_strcpy;
61 CheckName checkName_DeprecatedOrUnsafeBufferHandling;
62 CheckName checkName_rand;
63 CheckName checkName_vfork;
64 CheckName checkName_FloatLoopCounter;
65 CheckName 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 static const DeclRefExpr*
208 getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) {
209 expr = expr->IgnoreParenCasts();
211 if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) {
212 if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
213 B->getOpcode() == BO_Comma))
216 if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y))
219 if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y))
225 if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
226 const NamedDecl *ND = DR->getDecl();
227 return ND == x || ND == y ? DR : nullptr;
230 if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr))
231 return U->isIncrementDecrementOp()
232 ? getIncrementedVar(U->getSubExpr(), x, y) : nullptr;
237 /// CheckLoopConditionForFloat - This check looks for 'for' statements that
238 /// use a floating point variable as a loop counter.
239 /// CERT: FLP30-C, FLP30-CPP.
241 void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
242 if (!filter.check_FloatLoopCounter)
245 // Does the loop have a condition?
246 const Expr *condition = FS->getCond();
251 // Does the loop have an increment?
252 const Expr *increment = FS->getInc();
257 // Strip away '()' and casts.
258 condition = condition->IgnoreParenCasts();
259 increment = increment->IgnoreParenCasts();
261 // Is the loop condition a comparison?
262 const BinaryOperator *B = dyn_cast<BinaryOperator>(condition);
267 // Is this a comparison?
268 if (!(B->isRelationalOp() || B->isEqualityOp()))
271 // Are we comparing variables?
272 const DeclRefExpr *drLHS =
273 dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts());
274 const DeclRefExpr *drRHS =
275 dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts());
277 // Does at least one of the variables have a floating point type?
278 drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : nullptr;
279 drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : nullptr;
281 if (!drLHS && !drRHS)
284 const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : nullptr;
285 const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : nullptr;
287 if (!vdLHS && !vdRHS)
290 // Does either variable appear in increment?
291 const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS);
296 // Emit the error. First figure out which DeclRefExpr in the condition
297 // referenced the compared variable.
298 assert(drInc->getDecl());
299 const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS;
301 SmallVector<SourceRange, 2> ranges;
302 SmallString<256> sbuf;
303 llvm::raw_svector_ostream os(sbuf);
305 os << "Variable '" << drCond->getDecl()->getName()
306 << "' with floating point type '" << drCond->getType().getAsString()
307 << "' should not be used as a loop counter";
309 ranges.push_back(drCond->getSourceRange());
310 ranges.push_back(drInc->getSourceRange());
312 const char *bugType = "Floating point variable used as loop counter";
314 PathDiagnosticLocation FSLoc =
315 PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);
316 BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
317 bugType, "Security", os.str(),
321 //===----------------------------------------------------------------------===//
322 // Check: Any use of bcmp.
323 // CWE-477: Use of Obsolete Functions
324 // bcmp was deprecated in POSIX.1-2008
325 //===----------------------------------------------------------------------===//
327 void WalkAST::checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD) {
328 if (!filter.check_bcmp)
331 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
335 // Verify that the function takes three arguments.
336 if (FPT->getNumParams() != 3)
339 for (int i = 0; i < 2; i++) {
340 // Verify the first and second argument type is void*.
341 const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
345 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
349 // Verify the third argument type is integer.
350 if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
354 PathDiagnosticLocation CELoc =
355 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
356 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp,
357 "Use of deprecated function in call to 'bcmp()'",
359 "The bcmp() function is obsoleted by memcmp().",
360 CELoc, CE->getCallee()->getSourceRange());
363 //===----------------------------------------------------------------------===//
364 // Check: Any use of bcopy.
365 // CWE-477: Use of Obsolete Functions
366 // bcopy was deprecated in POSIX.1-2008
367 //===----------------------------------------------------------------------===//
369 void WalkAST::checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD) {
370 if (!filter.check_bcopy)
373 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
377 // Verify that the function takes three arguments.
378 if (FPT->getNumParams() != 3)
381 for (int i = 0; i < 2; i++) {
382 // Verify the first and second argument type is void*.
383 const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
387 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
391 // Verify the third argument type is integer.
392 if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
396 PathDiagnosticLocation CELoc =
397 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
398 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy,
399 "Use of deprecated function in call to 'bcopy()'",
401 "The bcopy() function is obsoleted by memcpy() "
403 CELoc, CE->getCallee()->getSourceRange());
406 //===----------------------------------------------------------------------===//
407 // Check: Any use of bzero.
408 // CWE-477: Use of Obsolete Functions
409 // bzero was deprecated in POSIX.1-2008
410 //===----------------------------------------------------------------------===//
412 void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) {
413 if (!filter.check_bzero)
416 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
420 // Verify that the function takes two arguments.
421 if (FPT->getNumParams() != 2)
424 // Verify the first argument type is void*.
425 const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
429 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
432 // Verify the second argument type is integer.
433 if (!FPT->getParamType(1)->isIntegralOrUnscopedEnumerationType())
437 PathDiagnosticLocation CELoc =
438 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
439 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero,
440 "Use of deprecated function in call to 'bzero()'",
442 "The bzero() function is obsoleted by memset().",
443 CELoc, CE->getCallee()->getSourceRange());
447 //===----------------------------------------------------------------------===//
448 // Check: Any use of 'gets' is insecure.
449 // Originally: <rdar://problem/6335715>
450 // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)
451 // CWE-242: Use of Inherently Dangerous Function
452 //===----------------------------------------------------------------------===//
454 void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {
455 if (!filter.check_gets)
458 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
462 // Verify that the function takes a single argument.
463 if (FPT->getNumParams() != 1)
466 // Is the argument a 'char*'?
467 const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
471 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
475 PathDiagnosticLocation CELoc =
476 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
477 BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
478 "Potential buffer overflow in call to 'gets'",
480 "Call to function 'gets' is extremely insecure as it can "
481 "always result in a buffer overflow",
482 CELoc, CE->getCallee()->getSourceRange());
485 //===----------------------------------------------------------------------===//
486 // Check: Any use of 'getpwd' is insecure.
487 // CWE-477: Use of Obsolete Functions
488 //===----------------------------------------------------------------------===//
490 void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {
491 if (!filter.check_getpw)
494 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
498 // Verify that the function takes two arguments.
499 if (FPT->getNumParams() != 2)
502 // Verify the first argument type is integer.
503 if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType())
506 // Verify the second argument type is char*.
507 const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>();
511 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
515 PathDiagnosticLocation CELoc =
516 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
517 BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
518 "Potential buffer overflow in call to 'getpw'",
520 "The getpw() function is dangerous as it may overflow the "
521 "provided buffer. It is obsoleted by getpwuid().",
522 CELoc, CE->getCallee()->getSourceRange());
525 //===----------------------------------------------------------------------===//
526 // Check: Any use of 'mktemp' is insecure. It is obsoleted by mkstemp().
527 // CWE-377: Insecure Temporary File
528 //===----------------------------------------------------------------------===//
530 void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
531 if (!filter.check_mktemp) {
532 // Fall back to the security check of looking for enough 'X's in the
533 // format string, since that is a less severe warning.
534 checkCall_mkstemp(CE, FD);
538 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
542 // Verify that the function takes a single argument.
543 if (FPT->getNumParams() != 1)
546 // Verify that the argument is Pointer Type.
547 const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
551 // Verify that the argument is a 'char*'.
552 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
556 PathDiagnosticLocation CELoc =
557 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
558 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
559 "Potential insecure temporary file in call 'mktemp'",
561 "Call to function 'mktemp' is insecure as it always "
562 "creates or uses insecure temporary file. Use 'mkstemp' "
564 CELoc, CE->getCallee()->getSourceRange());
567 //===----------------------------------------------------------------------===//
568 // Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.
569 //===----------------------------------------------------------------------===//
571 void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
572 if (!filter.check_mkstemp)
575 StringRef Name = FD->getIdentifier()->getName();
576 std::pair<signed, signed> ArgSuffix =
577 llvm::StringSwitch<std::pair<signed, signed> >(Name)
578 .Case("mktemp", std::make_pair(0,-1))
579 .Case("mkstemp", std::make_pair(0,-1))
580 .Case("mkdtemp", std::make_pair(0,-1))
581 .Case("mkstemps", std::make_pair(0,1))
582 .Default(std::make_pair(-1, -1));
584 assert(ArgSuffix.first >= 0 && "Unsupported function");
586 // Check if the number of arguments is consistent with out expectations.
587 unsigned numArgs = CE->getNumArgs();
588 if ((signed) numArgs <= ArgSuffix.first)
591 const StringLiteral *strArg =
592 dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first)
593 ->IgnoreParenImpCasts());
595 // Currently we only handle string literals. It is possible to do better,
596 // either by looking at references to const variables, or by doing real
598 if (!strArg || strArg->getCharByteWidth() != 1)
601 // Count the number of X's, taking into account a possible cutoff suffix.
602 StringRef str = strArg->getString();
604 unsigned n = str.size();
606 // Take into account the suffix.
608 if (ArgSuffix.second >= 0) {
609 const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second);
610 Expr::EvalResult EVResult;
611 if (!suffixEx->EvaluateAsInt(EVResult, BR.getContext()))
613 llvm::APSInt Result = EVResult.Val.getInt();
614 // FIXME: Issue a warning.
615 if (Result.isNegative())
617 suffix = (unsigned) Result.getZExtValue();
618 n = (n > suffix) ? n - suffix : 0;
621 for (unsigned i = 0; i < n; ++i)
622 if (str[i] == 'X') ++numX;
628 PathDiagnosticLocation CELoc =
629 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
630 SmallString<512> buf;
631 llvm::raw_svector_ostream out(buf);
632 out << "Call to '" << Name << "' should have at least 6 'X's in the"
633 " format string to be secure (" << numX << " 'X'";
638 out << ", " << suffix << " character";
641 out << " used as a suffix";
644 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
645 "Insecure temporary file creation", "Security",
646 out.str(), CELoc, strArg->getSourceRange());
649 //===----------------------------------------------------------------------===//
650 // Check: Any use of 'strcpy' is insecure.
652 // CWE-119: Improper Restriction of Operations within
653 // the Bounds of a Memory Buffer
654 //===----------------------------------------------------------------------===//
656 void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
657 if (!filter.check_strcpy)
660 if (!checkCall_strCommon(CE, FD))
663 const auto *Target = CE->getArg(0)->IgnoreImpCasts(),
664 *Source = CE->getArg(1)->IgnoreImpCasts();
666 if (const auto *Array = dyn_cast<ConstantArrayType>(Target->getType())) {
667 uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;
668 if (const auto *String = dyn_cast<StringLiteral>(Source)) {
669 if (ArraySize >= String->getLength() + 1)
675 PathDiagnosticLocation CELoc =
676 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
677 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
678 "Potential insecure memory buffer bounds restriction in "
681 "Call to function 'strcpy' is insecure as it does not "
682 "provide bounding of the memory buffer. Replace "
683 "unbounded copy functions with analogous functions that "
684 "support length arguments such as 'strlcpy'. CWE-119.",
685 CELoc, CE->getCallee()->getSourceRange());
688 //===----------------------------------------------------------------------===//
689 // Check: Any use of 'strcat' is insecure.
691 // CWE-119: Improper Restriction of Operations within
692 // the Bounds of a Memory Buffer
693 //===----------------------------------------------------------------------===//
695 void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
696 if (!filter.check_strcpy)
699 if (!checkCall_strCommon(CE, FD))
703 PathDiagnosticLocation CELoc =
704 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
705 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
706 "Potential insecure memory buffer bounds restriction in "
709 "Call to function 'strcat' is insecure as it does not "
710 "provide bounding of the memory buffer. Replace "
711 "unbounded copy functions with analogous functions that "
712 "support length arguments such as 'strlcat'. CWE-119.",
713 CELoc, CE->getCallee()->getSourceRange());
716 //===----------------------------------------------------------------------===//
717 // Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',
718 // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
719 // 'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf',
720 // 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset'
721 // is deprecated since C11.
723 // Use of 'sprintf', 'vsprintf', 'scanf', 'wscanf','fscanf',
724 // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
725 // 'swscanf', 'vsscanf', 'vswscanf' without buffer limitations
728 // CWE-119: Improper Restriction of Operations within
729 // the Bounds of a Memory Buffer
730 //===----------------------------------------------------------------------===//
732 void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
733 const FunctionDecl *FD) {
734 if (!filter.check_DeprecatedOrUnsafeBufferHandling)
737 if (!BR.getContext().getLangOpts().C11)
740 // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size
742 enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 };
744 StringRef Name = FD->getIdentifier()->getName();
745 if (Name.startswith("__builtin_"))
746 Name = Name.substr(10);
749 llvm::StringSwitch<int>(Name)
750 .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0)
751 .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf",
752 "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1)
753 .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy",
754 "memmove", "memset", "strncpy", "strncat", DEPR_ONLY)
755 .Default(UNKNOWN_CALL);
757 assert(ArgIndex != UNKNOWN_CALL && "Unsupported function");
758 bool BoundsProvided = ArgIndex == DEPR_ONLY;
760 if (!BoundsProvided) {
761 // Currently we only handle (not wide) string literals. It is possible to do
762 // better, either by looking at references to const variables, or by doing
763 // real flow analysis.
765 dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts());
767 FormatString->getString().find("%s") == StringRef::npos &&
768 FormatString->getString().find("%[") == StringRef::npos)
769 BoundsProvided = true;
772 SmallString<128> Buf1;
773 SmallString<512> Buf2;
774 llvm::raw_svector_ostream Out1(Buf1);
775 llvm::raw_svector_ostream Out2(Buf2);
777 Out1 << "Potential insecure memory buffer bounds restriction in call '"
779 Out2 << "Call to function '" << Name
780 << "' is insecure as it does not provide ";
782 if (!BoundsProvided) {
783 Out2 << "bounding of the memory buffer or ";
786 Out2 << "security checks introduced "
787 "in the C11 standard. Replace with analogous functions that "
788 "support length arguments or provides boundary checks such as '"
789 << Name << "_s' in case of C11";
791 PathDiagnosticLocation CELoc =
792 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
793 BR.EmitBasicReport(AC->getDecl(),
794 filter.checkName_DeprecatedOrUnsafeBufferHandling,
795 Out1.str(), "Security", Out2.str(), CELoc,
796 CE->getCallee()->getSourceRange());
799 //===----------------------------------------------------------------------===//
800 // Common check for str* functions with no bounds parameters.
801 //===----------------------------------------------------------------------===//
803 bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) {
804 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
808 // Verify the function takes two arguments, three in the _chk version.
809 int numArgs = FPT->getNumParams();
810 if (numArgs != 2 && numArgs != 3)
813 // Verify the type for both arguments.
814 for (int i = 0; i < 2; i++) {
815 // Verify that the arguments are pointers.
816 const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
820 // Verify that the argument is a 'char*'.
821 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
828 //===----------------------------------------------------------------------===//
829 // Check: Linear congruent random number generators should not be used
830 // Originally: <rdar://problem/63371000>
831 // CWE-338: Use of cryptographically weak prng
832 //===----------------------------------------------------------------------===//
834 void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
835 if (!filter.check_rand || !CheckRand)
838 const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
842 if (FTP->getNumParams() == 1) {
843 // Is the argument an 'unsigned short *'?
844 // (Actually any integer type is allowed.)
845 const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>();
849 if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())
851 } else if (FTP->getNumParams() != 0)
855 SmallString<256> buf1;
856 llvm::raw_svector_ostream os1(buf1);
857 os1 << '\'' << *FD << "' is a poor random number generator";
859 SmallString<256> buf2;
860 llvm::raw_svector_ostream os2(buf2);
861 os2 << "Function '" << *FD
862 << "' is obsolete because it implements a poor random number generator."
863 << " Use 'arc4random' instead";
865 PathDiagnosticLocation CELoc =
866 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
867 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
868 "Security", os2.str(), CELoc,
869 CE->getCallee()->getSourceRange());
872 //===----------------------------------------------------------------------===//
873 // Check: 'random' should not be used
874 // Originally: <rdar://problem/63371000>
875 //===----------------------------------------------------------------------===//
877 void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {
878 if (!CheckRand || !filter.check_rand)
881 const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
885 // Verify that the function takes no argument.
886 if (FTP->getNumParams() != 0)
890 PathDiagnosticLocation CELoc =
891 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
892 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
893 "'random' is not a secure random number generator",
895 "The 'random' function produces a sequence of values that "
896 "an adversary may be able to predict. Use 'arc4random' "
897 "instead", CELoc, CE->getCallee()->getSourceRange());
900 //===----------------------------------------------------------------------===//
901 // Check: 'vfork' should not be used.
902 // POS33-C: Do not use vfork().
903 //===----------------------------------------------------------------------===//
905 void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
906 if (!filter.check_vfork)
909 // All calls to vfork() are insecure, issue a warning.
910 PathDiagnosticLocation CELoc =
911 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
912 BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
913 "Potential insecure implementation-specific behavior in "
916 "Call to function 'vfork' is insecure as it can lead to "
917 "denial of service situations in the parent process. "
918 "Replace calls to vfork with calls to the safer "
919 "'posix_spawn' function",
920 CELoc, CE->getCallee()->getSourceRange());
923 //===----------------------------------------------------------------------===//
924 // Check: Should check whether privileges are dropped successfully.
925 // Originally: <rdar://problem/6337132>
926 //===----------------------------------------------------------------------===//
928 void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
929 if (!filter.check_UncheckedReturn)
932 const FunctionDecl *FD = CE->getDirectCallee();
936 if (II_setid[0] == nullptr) {
937 static const char * const identifiers[num_setids] = {
938 "setuid", "setgid", "seteuid", "setegid",
939 "setreuid", "setregid"
942 for (size_t i = 0; i < num_setids; i++)
943 II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
946 const IdentifierInfo *id = FD->getIdentifier();
949 for (identifierid = 0; identifierid < num_setids; identifierid++)
950 if (id == II_setid[identifierid])
953 if (identifierid >= num_setids)
956 const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
960 // Verify that the function takes one or two arguments (depending on
962 if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2))
965 // The arguments must be integers.
966 for (unsigned i = 0; i < FTP->getNumParams(); i++)
967 if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType())
971 SmallString<256> buf1;
972 llvm::raw_svector_ostream os1(buf1);
973 os1 << "Return value is not checked in call to '" << *FD << '\'';
975 SmallString<256> buf2;
976 llvm::raw_svector_ostream os2(buf2);
977 os2 << "The return value from the call to '" << *FD
978 << "' is not checked. If an error occurs in '" << *FD
979 << "', the following code may execute with unexpected privileges";
981 PathDiagnosticLocation CELoc =
982 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
983 BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
984 "Security", os2.str(), CELoc,
985 CE->getCallee()->getSourceRange());
988 //===----------------------------------------------------------------------===//
989 // SecuritySyntaxChecker
990 //===----------------------------------------------------------------------===//
993 class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
997 void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
998 BugReporter &BR) const {
999 WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
1000 walker.Visit(D->getBody());
1005 void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
1006 mgr.registerChecker<SecuritySyntaxChecker>();
1009 bool ento::shouldRegisterSecuritySyntaxChecker(const LangOptions &LO) {
1013 #define REGISTER_CHECKER(name) \
1014 void ento::register##name(CheckerManager &mgr) { \
1015 SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \
1016 checker->filter.check_##name = true; \
1017 checker->filter.checkName_##name = mgr.getCurrentCheckName(); \
1020 bool ento::shouldRegister##name(const LangOptions &LO) { \
1024 REGISTER_CHECKER(bcmp)
1025 REGISTER_CHECKER(bcopy)
1026 REGISTER_CHECKER(bzero)
1027 REGISTER_CHECKER(gets)
1028 REGISTER_CHECKER(getpw)
1029 REGISTER_CHECKER(mkstemp)
1030 REGISTER_CHECKER(mktemp)
1031 REGISTER_CHECKER(strcpy)
1032 REGISTER_CHECKER(rand)
1033 REGISTER_CHECKER(vfork)
1034 REGISTER_CHECKER(FloatLoopCounter)
1035 REGISTER_CHECKER(UncheckedReturn)
1036 REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)