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;
52 DefaultBool check_decodeValueOfObjCType;
54 CheckerNameRef checkName_bcmp;
55 CheckerNameRef checkName_bcopy;
56 CheckerNameRef checkName_bzero;
57 CheckerNameRef checkName_gets;
58 CheckerNameRef checkName_getpw;
59 CheckerNameRef checkName_mktemp;
60 CheckerNameRef checkName_mkstemp;
61 CheckerNameRef checkName_strcpy;
62 CheckerNameRef checkName_DeprecatedOrUnsafeBufferHandling;
63 CheckerNameRef checkName_rand;
64 CheckerNameRef checkName_vfork;
65 CheckerNameRef checkName_FloatLoopCounter;
66 CheckerNameRef checkName_UncheckedReturn;
67 CheckerNameRef checkName_decodeValueOfObjCType;
70 class WalkAST : public StmtVisitor<WalkAST> {
72 AnalysisDeclContext* AC;
73 enum { num_setids = 6 };
74 IdentifierInfo *II_setid[num_setids];
77 const ChecksFilter &filter;
80 WalkAST(BugReporter &br, AnalysisDeclContext* ac,
81 const ChecksFilter &f)
82 : BR(br), AC(ac), II_setid(),
83 CheckRand(isArc4RandomAvailable(BR.getContext())),
86 // Statement visitor methods.
87 void VisitCallExpr(CallExpr *CE);
88 void VisitObjCMessageExpr(ObjCMessageExpr *CE);
89 void VisitForStmt(ForStmt *S);
90 void VisitCompoundStmt (CompoundStmt *S);
91 void VisitStmt(Stmt *S) { VisitChildren(S); }
93 void VisitChildren(Stmt *S);
96 bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD);
98 typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *);
99 typedef void (WalkAST::*MsgCheck)(const ObjCMessageExpr *);
101 // Checker-specific methods.
102 void checkLoopConditionForFloat(const ForStmt *FS);
103 void checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD);
104 void checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD);
105 void checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD);
106 void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD);
107 void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD);
108 void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD);
109 void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD);
110 void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD);
111 void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD);
112 void checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
113 const FunctionDecl *FD);
114 void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);
115 void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);
116 void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD);
117 void checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME);
118 void checkUncheckedReturnValue(CallExpr *CE);
120 } // end anonymous namespace
122 //===----------------------------------------------------------------------===//
124 //===----------------------------------------------------------------------===//
126 void WalkAST::VisitChildren(Stmt *S) {
127 for (Stmt *Child : S->children())
132 void WalkAST::VisitCallExpr(CallExpr *CE) {
134 const FunctionDecl *FD = CE->getDirectCallee();
139 // Get the name of the callee. If it's a builtin, strip off the prefix.
140 IdentifierInfo *II = FD->getIdentifier();
141 if (!II) // if no identifier, not a simple C function
143 StringRef Name = II->getName();
144 if (Name.startswith("__builtin_"))
145 Name = Name.substr(10);
147 // Set the evaluation function by switching on the callee name.
148 FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
149 .Case("bcmp", &WalkAST::checkCall_bcmp)
150 .Case("bcopy", &WalkAST::checkCall_bcopy)
151 .Case("bzero", &WalkAST::checkCall_bzero)
152 .Case("gets", &WalkAST::checkCall_gets)
153 .Case("getpw", &WalkAST::checkCall_getpw)
154 .Case("mktemp", &WalkAST::checkCall_mktemp)
155 .Case("mkstemp", &WalkAST::checkCall_mkstemp)
156 .Case("mkdtemp", &WalkAST::checkCall_mkstemp)
157 .Case("mkstemps", &WalkAST::checkCall_mkstemp)
158 .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)
159 .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat)
160 .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf",
161 "vscanf", "vwscanf", "vfscanf", "vfwscanf",
162 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
163 .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf",
164 "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove",
165 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
166 .Cases("strncpy", "strncat", "memset",
167 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
168 .Case("drand48", &WalkAST::checkCall_rand)
169 .Case("erand48", &WalkAST::checkCall_rand)
170 .Case("jrand48", &WalkAST::checkCall_rand)
171 .Case("lrand48", &WalkAST::checkCall_rand)
172 .Case("mrand48", &WalkAST::checkCall_rand)
173 .Case("nrand48", &WalkAST::checkCall_rand)
174 .Case("lcong48", &WalkAST::checkCall_rand)
175 .Case("rand", &WalkAST::checkCall_rand)
176 .Case("rand_r", &WalkAST::checkCall_rand)
177 .Case("random", &WalkAST::checkCall_random)
178 .Case("vfork", &WalkAST::checkCall_vfork)
181 // If the callee isn't defined, it is not of security concern.
182 // Check and evaluate the call.
184 (this->*evalFunction)(CE, FD);
186 // Recurse and check children.
190 void WalkAST::VisitObjCMessageExpr(ObjCMessageExpr *ME) {
191 MsgCheck evalFunction =
192 llvm::StringSwitch<MsgCheck>(ME->getSelector().getAsString())
193 .Case("decodeValueOfObjCType:at:",
194 &WalkAST::checkMsg_decodeValueOfObjCType)
198 (this->*evalFunction)(ME);
200 // Recurse and check children.
204 void WalkAST::VisitCompoundStmt(CompoundStmt *S) {
205 for (Stmt *Child : S->children())
207 if (CallExpr *CE = dyn_cast<CallExpr>(Child))
208 checkUncheckedReturnValue(CE);
213 void WalkAST::VisitForStmt(ForStmt *FS) {
214 checkLoopConditionForFloat(FS);
216 // Recurse and check children.
220 //===----------------------------------------------------------------------===//
221 // Check: floating point variable used as loop counter.
222 // Originally: <rdar://problem/6336718>
223 // Implements: CERT security coding advisory FLP-30.
224 //===----------------------------------------------------------------------===//
226 // Returns either 'x' or 'y', depending on which one of them is incremented
227 // in 'expr', or nullptr if none of them is incremented.
228 static const DeclRefExpr*
229 getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) {
230 expr = expr->IgnoreParenCasts();
232 if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) {
233 if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
234 B->getOpcode() == BO_Comma))
237 if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y))
240 if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y))
246 if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
247 const NamedDecl *ND = DR->getDecl();
248 return ND == x || ND == y ? DR : nullptr;
251 if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr))
252 return U->isIncrementDecrementOp()
253 ? getIncrementedVar(U->getSubExpr(), x, y) : nullptr;
258 /// CheckLoopConditionForFloat - This check looks for 'for' statements that
259 /// use a floating point variable as a loop counter.
260 /// CERT: FLP30-C, FLP30-CPP.
262 void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
263 if (!filter.check_FloatLoopCounter)
266 // Does the loop have a condition?
267 const Expr *condition = FS->getCond();
272 // Does the loop have an increment?
273 const Expr *increment = FS->getInc();
278 // Strip away '()' and casts.
279 condition = condition->IgnoreParenCasts();
280 increment = increment->IgnoreParenCasts();
282 // Is the loop condition a comparison?
283 const BinaryOperator *B = dyn_cast<BinaryOperator>(condition);
288 // Is this a comparison?
289 if (!(B->isRelationalOp() || B->isEqualityOp()))
292 // Are we comparing variables?
293 const DeclRefExpr *drLHS =
294 dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts());
295 const DeclRefExpr *drRHS =
296 dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts());
298 // Does at least one of the variables have a floating point type?
299 drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : nullptr;
300 drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : nullptr;
302 if (!drLHS && !drRHS)
305 const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : nullptr;
306 const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : nullptr;
308 if (!vdLHS && !vdRHS)
311 // Does either variable appear in increment?
312 const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS);
316 const VarDecl *vdInc = cast<VarDecl>(drInc->getDecl());
317 assert(vdInc && (vdInc == vdLHS || vdInc == vdRHS));
319 // Emit the error. First figure out which DeclRefExpr in the condition
320 // referenced the compared variable.
321 const DeclRefExpr *drCond = vdLHS == vdInc ? drLHS : drRHS;
323 SmallVector<SourceRange, 2> ranges;
324 SmallString<256> sbuf;
325 llvm::raw_svector_ostream os(sbuf);
327 os << "Variable '" << drCond->getDecl()->getName()
328 << "' with floating point type '" << drCond->getType().getAsString()
329 << "' should not be used as a loop counter";
331 ranges.push_back(drCond->getSourceRange());
332 ranges.push_back(drInc->getSourceRange());
334 const char *bugType = "Floating point variable used as loop counter";
336 PathDiagnosticLocation FSLoc =
337 PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);
338 BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
339 bugType, "Security", os.str(),
343 //===----------------------------------------------------------------------===//
344 // Check: Any use of bcmp.
345 // CWE-477: Use of Obsolete Functions
346 // bcmp was deprecated in POSIX.1-2008
347 //===----------------------------------------------------------------------===//
349 void WalkAST::checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD) {
350 if (!filter.check_bcmp)
353 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
357 // Verify that the function takes three arguments.
358 if (FPT->getNumParams() != 3)
361 for (int i = 0; i < 2; i++) {
362 // Verify the first and second argument type is void*.
363 const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
367 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
371 // Verify the third argument type is integer.
372 if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
376 PathDiagnosticLocation CELoc =
377 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
378 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp,
379 "Use of deprecated function in call to 'bcmp()'",
381 "The bcmp() function is obsoleted by memcmp().",
382 CELoc, CE->getCallee()->getSourceRange());
385 //===----------------------------------------------------------------------===//
386 // Check: Any use of bcopy.
387 // CWE-477: Use of Obsolete Functions
388 // bcopy was deprecated in POSIX.1-2008
389 //===----------------------------------------------------------------------===//
391 void WalkAST::checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD) {
392 if (!filter.check_bcopy)
395 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
399 // Verify that the function takes three arguments.
400 if (FPT->getNumParams() != 3)
403 for (int i = 0; i < 2; i++) {
404 // Verify the first and second argument type is void*.
405 const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
409 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
413 // Verify the third argument type is integer.
414 if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
418 PathDiagnosticLocation CELoc =
419 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
420 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy,
421 "Use of deprecated function in call to 'bcopy()'",
423 "The bcopy() function is obsoleted by memcpy() "
425 CELoc, CE->getCallee()->getSourceRange());
428 //===----------------------------------------------------------------------===//
429 // Check: Any use of bzero.
430 // CWE-477: Use of Obsolete Functions
431 // bzero was deprecated in POSIX.1-2008
432 //===----------------------------------------------------------------------===//
434 void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) {
435 if (!filter.check_bzero)
438 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
442 // Verify that the function takes two arguments.
443 if (FPT->getNumParams() != 2)
446 // Verify the first argument type is void*.
447 const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
451 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
454 // Verify the second argument type is integer.
455 if (!FPT->getParamType(1)->isIntegralOrUnscopedEnumerationType())
459 PathDiagnosticLocation CELoc =
460 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
461 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero,
462 "Use of deprecated function in call to 'bzero()'",
464 "The bzero() function is obsoleted by memset().",
465 CELoc, CE->getCallee()->getSourceRange());
469 //===----------------------------------------------------------------------===//
470 // Check: Any use of 'gets' is insecure.
471 // Originally: <rdar://problem/6335715>
472 // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)
473 // CWE-242: Use of Inherently Dangerous Function
474 //===----------------------------------------------------------------------===//
476 void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {
477 if (!filter.check_gets)
480 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
484 // Verify that the function takes a single argument.
485 if (FPT->getNumParams() != 1)
488 // Is the argument a 'char*'?
489 const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
493 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
497 PathDiagnosticLocation CELoc =
498 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
499 BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
500 "Potential buffer overflow in call to 'gets'",
502 "Call to function 'gets' is extremely insecure as it can "
503 "always result in a buffer overflow",
504 CELoc, CE->getCallee()->getSourceRange());
507 //===----------------------------------------------------------------------===//
508 // Check: Any use of 'getpwd' is insecure.
509 // CWE-477: Use of Obsolete Functions
510 //===----------------------------------------------------------------------===//
512 void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {
513 if (!filter.check_getpw)
516 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
520 // Verify that the function takes two arguments.
521 if (FPT->getNumParams() != 2)
524 // Verify the first argument type is integer.
525 if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType())
528 // Verify the second argument type is char*.
529 const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>();
533 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
537 PathDiagnosticLocation CELoc =
538 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
539 BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
540 "Potential buffer overflow in call to 'getpw'",
542 "The getpw() function is dangerous as it may overflow the "
543 "provided buffer. It is obsoleted by getpwuid().",
544 CELoc, CE->getCallee()->getSourceRange());
547 //===----------------------------------------------------------------------===//
548 // Check: Any use of 'mktemp' is insecure. It is obsoleted by mkstemp().
549 // CWE-377: Insecure Temporary File
550 //===----------------------------------------------------------------------===//
552 void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
553 if (!filter.check_mktemp) {
554 // Fall back to the security check of looking for enough 'X's in the
555 // format string, since that is a less severe warning.
556 checkCall_mkstemp(CE, FD);
560 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
564 // Verify that the function takes a single argument.
565 if (FPT->getNumParams() != 1)
568 // Verify that the argument is Pointer Type.
569 const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
573 // Verify that the argument is a 'char*'.
574 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
578 PathDiagnosticLocation CELoc =
579 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
580 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
581 "Potential insecure temporary file in call 'mktemp'",
583 "Call to function 'mktemp' is insecure as it always "
584 "creates or uses insecure temporary file. Use 'mkstemp' "
586 CELoc, CE->getCallee()->getSourceRange());
589 //===----------------------------------------------------------------------===//
590 // Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.
591 //===----------------------------------------------------------------------===//
593 void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
594 if (!filter.check_mkstemp)
597 StringRef Name = FD->getIdentifier()->getName();
598 std::pair<signed, signed> ArgSuffix =
599 llvm::StringSwitch<std::pair<signed, signed> >(Name)
600 .Case("mktemp", std::make_pair(0,-1))
601 .Case("mkstemp", std::make_pair(0,-1))
602 .Case("mkdtemp", std::make_pair(0,-1))
603 .Case("mkstemps", std::make_pair(0,1))
604 .Default(std::make_pair(-1, -1));
606 assert(ArgSuffix.first >= 0 && "Unsupported function");
608 // Check if the number of arguments is consistent with out expectations.
609 unsigned numArgs = CE->getNumArgs();
610 if ((signed) numArgs <= ArgSuffix.first)
613 const StringLiteral *strArg =
614 dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first)
615 ->IgnoreParenImpCasts());
617 // Currently we only handle string literals. It is possible to do better,
618 // either by looking at references to const variables, or by doing real
620 if (!strArg || strArg->getCharByteWidth() != 1)
623 // Count the number of X's, taking into account a possible cutoff suffix.
624 StringRef str = strArg->getString();
626 unsigned n = str.size();
628 // Take into account the suffix.
630 if (ArgSuffix.second >= 0) {
631 const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second);
632 Expr::EvalResult EVResult;
633 if (!suffixEx->EvaluateAsInt(EVResult, BR.getContext()))
635 llvm::APSInt Result = EVResult.Val.getInt();
636 // FIXME: Issue a warning.
637 if (Result.isNegative())
639 suffix = (unsigned) Result.getZExtValue();
640 n = (n > suffix) ? n - suffix : 0;
643 for (unsigned i = 0; i < n; ++i)
644 if (str[i] == 'X') ++numX;
650 PathDiagnosticLocation CELoc =
651 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
652 SmallString<512> buf;
653 llvm::raw_svector_ostream out(buf);
654 out << "Call to '" << Name << "' should have at least 6 'X's in the"
655 " format string to be secure (" << numX << " 'X'";
660 out << ", " << suffix << " character";
663 out << " used as a suffix";
666 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
667 "Insecure temporary file creation", "Security",
668 out.str(), CELoc, strArg->getSourceRange());
671 //===----------------------------------------------------------------------===//
672 // Check: Any use of 'strcpy' is insecure.
674 // CWE-119: Improper Restriction of Operations within
675 // the Bounds of a Memory Buffer
676 //===----------------------------------------------------------------------===//
678 void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
679 if (!filter.check_strcpy)
682 if (!checkCall_strCommon(CE, FD))
685 const auto *Target = CE->getArg(0)->IgnoreImpCasts(),
686 *Source = CE->getArg(1)->IgnoreImpCasts();
688 if (const auto *Array = dyn_cast<ConstantArrayType>(Target->getType())) {
689 uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;
690 if (const auto *String = dyn_cast<StringLiteral>(Source)) {
691 if (ArraySize >= String->getLength() + 1)
697 PathDiagnosticLocation CELoc =
698 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
699 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
700 "Potential insecure memory buffer bounds restriction in "
703 "Call to function 'strcpy' is insecure as it does not "
704 "provide bounding of the memory buffer. Replace "
705 "unbounded copy functions with analogous functions that "
706 "support length arguments such as 'strlcpy'. CWE-119.",
707 CELoc, CE->getCallee()->getSourceRange());
710 //===----------------------------------------------------------------------===//
711 // Check: Any use of 'strcat' is insecure.
713 // CWE-119: Improper Restriction of Operations within
714 // the Bounds of a Memory Buffer
715 //===----------------------------------------------------------------------===//
717 void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
718 if (!filter.check_strcpy)
721 if (!checkCall_strCommon(CE, FD))
725 PathDiagnosticLocation CELoc =
726 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
727 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
728 "Potential insecure memory buffer bounds restriction in "
731 "Call to function 'strcat' is insecure as it does not "
732 "provide bounding of the memory buffer. Replace "
733 "unbounded copy functions with analogous functions that "
734 "support length arguments such as 'strlcat'. CWE-119.",
735 CELoc, CE->getCallee()->getSourceRange());
738 //===----------------------------------------------------------------------===//
739 // Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',
740 // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
741 // 'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf',
742 // 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset'
743 // is deprecated since C11.
745 // Use of 'sprintf', 'vsprintf', 'scanf', 'wscanf','fscanf',
746 // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
747 // 'swscanf', 'vsscanf', 'vswscanf' without buffer limitations
750 // CWE-119: Improper Restriction of Operations within
751 // the Bounds of a Memory Buffer
752 //===----------------------------------------------------------------------===//
754 void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
755 const FunctionDecl *FD) {
756 if (!filter.check_DeprecatedOrUnsafeBufferHandling)
759 if (!BR.getContext().getLangOpts().C11)
762 // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size
764 enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 };
766 StringRef Name = FD->getIdentifier()->getName();
767 if (Name.startswith("__builtin_"))
768 Name = Name.substr(10);
771 llvm::StringSwitch<int>(Name)
772 .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0)
773 .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf",
774 "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1)
775 .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy",
776 "memmove", "memset", "strncpy", "strncat", DEPR_ONLY)
777 .Default(UNKNOWN_CALL);
779 assert(ArgIndex != UNKNOWN_CALL && "Unsupported function");
780 bool BoundsProvided = ArgIndex == DEPR_ONLY;
782 if (!BoundsProvided) {
783 // Currently we only handle (not wide) string literals. It is possible to do
784 // better, either by looking at references to const variables, or by doing
785 // real flow analysis.
787 dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts());
789 FormatString->getString().find("%s") == StringRef::npos &&
790 FormatString->getString().find("%[") == StringRef::npos)
791 BoundsProvided = true;
794 SmallString<128> Buf1;
795 SmallString<512> Buf2;
796 llvm::raw_svector_ostream Out1(Buf1);
797 llvm::raw_svector_ostream Out2(Buf2);
799 Out1 << "Potential insecure memory buffer bounds restriction in call '"
801 Out2 << "Call to function '" << Name
802 << "' is insecure as it does not provide ";
804 if (!BoundsProvided) {
805 Out2 << "bounding of the memory buffer or ";
808 Out2 << "security checks introduced "
809 "in the C11 standard. Replace with analogous functions that "
810 "support length arguments or provides boundary checks such as '"
811 << Name << "_s' in case of C11";
813 PathDiagnosticLocation CELoc =
814 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
815 BR.EmitBasicReport(AC->getDecl(),
816 filter.checkName_DeprecatedOrUnsafeBufferHandling,
817 Out1.str(), "Security", Out2.str(), CELoc,
818 CE->getCallee()->getSourceRange());
821 //===----------------------------------------------------------------------===//
822 // Common check for str* functions with no bounds parameters.
823 //===----------------------------------------------------------------------===//
825 bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) {
826 const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
830 // Verify the function takes two arguments, three in the _chk version.
831 int numArgs = FPT->getNumParams();
832 if (numArgs != 2 && numArgs != 3)
835 // Verify the type for both arguments.
836 for (int i = 0; i < 2; i++) {
837 // Verify that the arguments are pointers.
838 const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
842 // Verify that the argument is a 'char*'.
843 if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
850 //===----------------------------------------------------------------------===//
851 // Check: Linear congruent random number generators should not be used
852 // Originally: <rdar://problem/63371000>
853 // CWE-338: Use of cryptographically weak prng
854 //===----------------------------------------------------------------------===//
856 void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
857 if (!filter.check_rand || !CheckRand)
860 const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
864 if (FTP->getNumParams() == 1) {
865 // Is the argument an 'unsigned short *'?
866 // (Actually any integer type is allowed.)
867 const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>();
871 if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())
873 } else if (FTP->getNumParams() != 0)
877 SmallString<256> buf1;
878 llvm::raw_svector_ostream os1(buf1);
879 os1 << '\'' << *FD << "' is a poor random number generator";
881 SmallString<256> buf2;
882 llvm::raw_svector_ostream os2(buf2);
883 os2 << "Function '" << *FD
884 << "' is obsolete because it implements a poor random number generator."
885 << " Use 'arc4random' instead";
887 PathDiagnosticLocation CELoc =
888 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
889 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
890 "Security", os2.str(), CELoc,
891 CE->getCallee()->getSourceRange());
894 //===----------------------------------------------------------------------===//
895 // Check: 'random' should not be used
896 // Originally: <rdar://problem/63371000>
897 //===----------------------------------------------------------------------===//
899 void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {
900 if (!CheckRand || !filter.check_rand)
903 const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
907 // Verify that the function takes no argument.
908 if (FTP->getNumParams() != 0)
912 PathDiagnosticLocation CELoc =
913 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
914 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
915 "'random' is not a secure random number generator",
917 "The 'random' function produces a sequence of values that "
918 "an adversary may be able to predict. Use 'arc4random' "
919 "instead", CELoc, CE->getCallee()->getSourceRange());
922 //===----------------------------------------------------------------------===//
923 // Check: 'vfork' should not be used.
924 // POS33-C: Do not use vfork().
925 //===----------------------------------------------------------------------===//
927 void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
928 if (!filter.check_vfork)
931 // All calls to vfork() are insecure, issue a warning.
932 PathDiagnosticLocation CELoc =
933 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
934 BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
935 "Potential insecure implementation-specific behavior in "
938 "Call to function 'vfork' is insecure as it can lead to "
939 "denial of service situations in the parent process. "
940 "Replace calls to vfork with calls to the safer "
941 "'posix_spawn' function",
942 CELoc, CE->getCallee()->getSourceRange());
945 //===----------------------------------------------------------------------===//
946 // Check: '-decodeValueOfObjCType:at:' should not be used.
947 // It is deprecated in favor of '-decodeValueOfObjCType:at:size:' due to
948 // likelihood of buffer overflows.
949 //===----------------------------------------------------------------------===//
951 void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) {
952 if (!filter.check_decodeValueOfObjCType)
955 // Check availability of the secure alternative:
956 // iOS 11+, macOS 10.13+, tvOS 11+, and watchOS 4.0+
957 // FIXME: We probably shouldn't register the check if it's not available.
958 const TargetInfo &TI = AC->getASTContext().getTargetInfo();
959 const llvm::Triple &T = TI.getTriple();
960 const VersionTuple &VT = TI.getPlatformMinVersion();
962 case llvm::Triple::IOS:
963 if (VT < VersionTuple(11, 0))
966 case llvm::Triple::MacOSX:
967 if (VT < VersionTuple(10, 13))
970 case llvm::Triple::WatchOS:
971 if (VT < VersionTuple(4, 0))
974 case llvm::Triple::TvOS:
975 if (VT < VersionTuple(11, 0))
982 PathDiagnosticLocation MELoc =
983 PathDiagnosticLocation::createBegin(ME, BR.getSourceManager(), AC);
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());
993 //===----------------------------------------------------------------------===//
994 // Check: Should check whether privileges are dropped successfully.
995 // Originally: <rdar://problem/6337132>
996 //===----------------------------------------------------------------------===//
998 void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
999 if (!filter.check_UncheckedReturn)
1002 const FunctionDecl *FD = CE->getDirectCallee();
1006 if (II_setid[0] == nullptr) {
1007 static const char * const identifiers[num_setids] = {
1008 "setuid", "setgid", "seteuid", "setegid",
1009 "setreuid", "setregid"
1012 for (size_t i = 0; i < num_setids; i++)
1013 II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
1016 const IdentifierInfo *id = FD->getIdentifier();
1017 size_t identifierid;
1019 for (identifierid = 0; identifierid < num_setids; identifierid++)
1020 if (id == II_setid[identifierid])
1023 if (identifierid >= num_setids)
1026 const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
1030 // Verify that the function takes one or two arguments (depending on
1032 if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2))
1035 // The arguments must be integers.
1036 for (unsigned i = 0; i < FTP->getNumParams(); i++)
1037 if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType())
1041 SmallString<256> buf1;
1042 llvm::raw_svector_ostream os1(buf1);
1043 os1 << "Return value is not checked in call to '" << *FD << '\'';
1045 SmallString<256> buf2;
1046 llvm::raw_svector_ostream os2(buf2);
1047 os2 << "The return value from the call to '" << *FD
1048 << "' is not checked. If an error occurs in '" << *FD
1049 << "', the following code may execute with unexpected privileges";
1051 PathDiagnosticLocation CELoc =
1052 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
1053 BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
1054 "Security", os2.str(), CELoc,
1055 CE->getCallee()->getSourceRange());
1058 //===----------------------------------------------------------------------===//
1059 // SecuritySyntaxChecker
1060 //===----------------------------------------------------------------------===//
1063 class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
1065 ChecksFilter filter;
1067 void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
1068 BugReporter &BR) const {
1069 WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
1070 walker.Visit(D->getBody());
1075 void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
1076 mgr.registerChecker<SecuritySyntaxChecker>();
1079 bool ento::shouldRegisterSecuritySyntaxChecker(const CheckerManager &mgr) {
1083 #define REGISTER_CHECKER(name) \
1084 void ento::register##name(CheckerManager &mgr) { \
1085 SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \
1086 checker->filter.check_##name = true; \
1087 checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \
1090 bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
1092 REGISTER_CHECKER(bcmp)
1093 REGISTER_CHECKER(bcopy)
1094 REGISTER_CHECKER(bzero)
1095 REGISTER_CHECKER(gets)
1096 REGISTER_CHECKER(getpw)
1097 REGISTER_CHECKER(mkstemp)
1098 REGISTER_CHECKER(mktemp)
1099 REGISTER_CHECKER(strcpy)
1100 REGISTER_CHECKER(rand)
1101 REGISTER_CHECKER(vfork)
1102 REGISTER_CHECKER(FloatLoopCounter)
1103 REGISTER_CHECKER(UncheckedReturn)
1104 REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)
1105 REGISTER_CHECKER(decodeValueOfObjCType)