21 #include "llvm/ADT/SmallString.h"
22 #include "llvm/ADT/StringSwitch.h"
23 #include "llvm/Support/raw_ostream.h"
25 using namespace clang;
30 return T.getVendor() == llvm::Triple::Apple ||
31 T.getOS() == llvm::Triple::CloudABI ||
32 T.getOS() == llvm::Triple::FreeBSD ||
33 T.getOS() == llvm::Triple::NetBSD ||
34 T.getOS() == llvm::Triple::OpenBSD ||
35 T.getOS() == llvm::Triple::Bitrig ||
36 T.getOS() == llvm::Triple::DragonFly;
65 enum { num_setids = 6 };
69 const ChecksFilter &filter;
73 const ChecksFilter &f)
74 : BR(br), AC(ac), II_setid(),
82 void VisitStmt(
Stmt *
S) { VisitChildren(S); }
84 void VisitChildren(
Stmt *
S);
89 typedef void (WalkAST::*FnCheck)(
const CallExpr *,
93 void checkLoopConditionForFloat(
const ForStmt *FS);
103 void checkUncheckedReturnValue(
CallExpr *CE);
111 void WalkAST::VisitChildren(
Stmt *
S) {
112 for (
Stmt *Child : S->children())
117 void WalkAST::VisitCallExpr(
CallExpr *CE) {
128 StringRef Name = II->
getName();
129 if (Name.startswith(
"__builtin_"))
130 Name = Name.substr(10);
133 FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
134 .Case(
"gets", &WalkAST::checkCall_gets)
135 .Case(
"getpw", &WalkAST::checkCall_getpw)
136 .Case(
"mktemp", &WalkAST::checkCall_mktemp)
137 .Case(
"mkstemp", &WalkAST::checkCall_mkstemp)
138 .Case(
"mkdtemp", &WalkAST::checkCall_mkstemp)
139 .Case(
"mkstemps", &WalkAST::checkCall_mkstemp)
140 .Cases(
"strcpy",
"__strcpy_chk", &WalkAST::checkCall_strcpy)
141 .Cases(
"strcat",
"__strcat_chk", &WalkAST::checkCall_strcat)
142 .Case(
"drand48", &WalkAST::checkCall_rand)
143 .Case(
"erand48", &WalkAST::checkCall_rand)
144 .Case(
"jrand48", &WalkAST::checkCall_rand)
145 .Case(
"lrand48", &WalkAST::checkCall_rand)
146 .Case(
"mrand48", &WalkAST::checkCall_rand)
147 .Case(
"nrand48", &WalkAST::checkCall_rand)
148 .Case(
"lcong48", &WalkAST::checkCall_rand)
149 .Case(
"rand", &WalkAST::checkCall_rand)
150 .Case(
"rand_r", &WalkAST::checkCall_rand)
151 .Case(
"random", &WalkAST::checkCall_random)
152 .Case(
"vfork", &WalkAST::checkCall_vfork)
158 (this->*evalFunction)(CE, FD);
167 if (
CallExpr *CE = dyn_cast<CallExpr>(Child))
168 checkUncheckedReturnValue(CE);
173 void WalkAST::VisitForStmt(
ForStmt *FS) {
174 checkLoopConditionForFloat(FS);
191 if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
204 if (
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
206 return ND == x || ND == y ? DR :
nullptr;
210 return U->isIncrementDecrementOp()
220 void WalkAST::checkLoopConditionForFloat(
const ForStmt *FS) {
221 if (!filter.check_FloatLoopCounter)
258 drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS :
nullptr;
260 if (!drLHS && !drRHS)
263 const VarDecl *vdLHS = drLHS ? dyn_cast<
VarDecl>(drLHS->getDecl()) :
nullptr;
264 const VarDecl *vdRHS = drRHS ? dyn_cast<
VarDecl>(drRHS->getDecl()) :
nullptr;
266 if (!vdLHS && !vdRHS)
282 llvm::raw_svector_ostream os(sbuf);
284 os <<
"Variable '" << drCond->getDecl()->getName()
285 <<
"' with floating point type '" << drCond->getType().getAsString()
286 <<
"' should not be used as a loop counter";
288 ranges.push_back(drCond->getSourceRange());
289 ranges.push_back(drInc->getSourceRange());
291 const char *bugType =
"Floating point variable used as loop counter";
295 BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
296 bugType,
"Security", os.str(),
308 if (!filter.check_gets)
330 BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
331 "Potential buffer overflow in call to 'gets'",
333 "Call to function 'gets' is extremely insecure as it can "
334 "always result in a buffer overflow",
335 CELoc, CE->
getCallee()->getSourceRange());
344 if (!filter.check_getpw)
370 BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
371 "Potential buffer overflow in call to 'getpw'",
373 "The getpw() function is dangerous as it may overflow the "
374 "provided buffer. It is obsoleted by getpwuid().",
375 CELoc, CE->
getCallee()->getSourceRange());
384 if (!filter.check_mktemp) {
387 checkCall_mkstemp(CE, FD);
411 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
412 "Potential insecure temporary file in call 'mktemp'",
414 "Call to function 'mktemp' is insecure as it always "
415 "creates or uses insecure temporary file. Use 'mkstemp' "
417 CELoc, CE->
getCallee()->getSourceRange());
426 if (!filter.check_mkstemp)
430 std::pair<signed, signed> ArgSuffix =
431 llvm::StringSwitch<std::pair<signed, signed> >(Name)
432 .Case(
"mktemp", std::make_pair(0,-1))
433 .Case(
"mkstemp", std::make_pair(0,-1))
434 .Case(
"mkdtemp", std::make_pair(0,-1))
435 .Case(
"mkstemps", std::make_pair(0,1))
436 .Default(std::make_pair(-1, -1));
438 assert(ArgSuffix.first >= 0 &&
"Unsupported function");
442 if ((
signed) numArgs <= ArgSuffix.first)
458 unsigned n = str.size();
462 if (ArgSuffix.second >= 0) {
463 const Expr *suffixEx = CE->
getArg((
unsigned)ArgSuffix.second);
468 if (Result.isNegative())
470 suffix = (
unsigned) Result.getZExtValue();
471 n = (n > suffix) ? n - suffix : 0;
474 for (
unsigned i = 0; i < n; ++i)
475 if (str[i] ==
'X') ++numX;
484 llvm::raw_svector_ostream out(buf);
485 out <<
"Call to '" << Name <<
"' should have at least 6 'X's in the"
486 " format string to be secure (" << numX <<
" 'X'";
491 out <<
", " << suffix <<
" character";
494 out <<
" used as a suffix";
497 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
498 "Insecure temporary file creation",
"Security",
499 out.str(), CELoc, strArg->getSourceRange());
509 if (!filter.check_strcpy)
512 if (!checkCall_strCommon(CE, FD))
518 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
519 "Potential insecure memory buffer bounds restriction in "
522 "Call to function 'strcpy' is insecure as it does not "
523 "provide bounding of the memory buffer. Replace "
524 "unbounded copy functions with analogous functions that "
525 "support length arguments such as 'strlcpy'. CWE-119.",
526 CELoc, CE->
getCallee()->getSourceRange());
536 if (!filter.check_strcpy)
539 if (!checkCall_strCommon(CE, FD))
545 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
546 "Potential insecure memory buffer bounds restriction in "
549 "Call to function 'strcat' is insecure as it does not "
550 "provide bounding of the memory buffer. Replace "
551 "unbounded copy functions with analogous functions that "
552 "support length arguments such as 'strlcat'. CWE-119.",
553 CELoc, CE->
getCallee()->getSourceRange());
566 if (numArgs != 2 && numArgs != 3)
570 for (
int i = 0; i < 2; i++) {
591 if (!filter.check_rand || !CheckRand)
612 llvm::raw_svector_ostream os1(buf1);
613 os1 <<
'\'' << *FD <<
"' is a poor random number generator";
616 llvm::raw_svector_ostream os2(buf2);
617 os2 <<
"Function '" << *FD
618 <<
"' is obsolete because it implements a poor random number generator."
619 <<
" Use 'arc4random' instead";
623 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
624 "Security", os2.str(), CELoc,
634 if (!CheckRand || !filter.check_rand)
648 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
649 "'random' is not a secure random number generator",
651 "The 'random' function produces a sequence of values that "
652 "an adversary may be able to predict. Use 'arc4random' "
653 "instead", CELoc, CE->
getCallee()->getSourceRange());
662 if (!filter.check_vfork)
668 BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
669 "Potential insecure implementation-specific behavior in "
672 "Call to function 'vfork' is insecure as it can lead to "
673 "denial of service situations in the parent process. "
674 "Replace calls to vfork with calls to the safer "
675 "'posix_spawn' function",
676 CELoc, CE->
getCallee()->getSourceRange());
684 void WalkAST::checkUncheckedReturnValue(
CallExpr *CE) {
685 if (!filter.check_UncheckedReturn)
692 if (II_setid[0] ==
nullptr) {
693 static const char *
const identifiers[num_setids] = {
694 "setuid",
"setgid",
"seteuid",
"setegid",
695 "setreuid",
"setregid"
698 for (
size_t i = 0; i < num_setids; i++)
699 II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
705 for (identifierid = 0; identifierid < num_setids; identifierid++)
706 if (
id == II_setid[identifierid])
709 if (identifierid >= num_setids)
728 llvm::raw_svector_ostream os1(buf1);
729 os1 <<
"Return value is not checked in call to '" << *FD <<
'\'';
732 llvm::raw_svector_ostream os2(buf2);
733 os2 <<
"The return value from the call to '" << *FD
734 <<
"' is not checked. If an error occurs in '" << *FD
735 <<
"', the following code may execute with unexpected privileges";
739 BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
740 "Security", os2.str(), CELoc,
749 class SecuritySyntaxChecker :
public Checker<check::ASTCodeBody> {
761 #define REGISTER_CHECKER(name) \
762 void ento::register##name(CheckerManager &mgr) { \
763 SecuritySyntaxChecker *checker = \
764 mgr.registerChecker<SecuritySyntaxChecker>(); \
765 checker->filter.check_##name = true; \
766 checker->filter.checkName_##name = mgr.getCurrentCheckName(); \
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
IdentifierInfo * getIdentifier() const
A helper class which wraps a boolean value set to false by default.
const Expr * getCallee() const
unsigned getNumParams() const
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
static bool isRelationalOp(Opcode Opc)
bool isIntegralOrUnscopedEnumerationType() const
Determine whether this type is an integral or unscoped enumeration type.
static bool isEqualityOp(Opcode Opc)
const TargetInfo & getTargetInfo() const
A builtin binary operation expression such as "x + y" or "x <= y".
Expr * IgnoreParenCasts() LLVM_READONLY
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
QualType getParamType(unsigned i) const
bool isRealFloatingType() const
Floating point categories.
StringRef getName() const
Return the actual identifier string.
bool EvaluateAsInt(llvm::APSInt &Result, const ASTContext &Ctx, SideEffectsKind AllowSideEffects=SE_NoSideEffects) const
The result type of a method or function.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
static const DeclRefExpr * getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y)
#define REGISTER_CHECKER(name)
virtual Stmt * getBody() const
unsigned getCharByteWidth() const
QualType getPointeeType() const
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return 0.
StringRef getString() const
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
unsigned getNumArgs() const
Expr * IgnoreParenImpCasts() LLVM_READONLY
static __inline__ uint32_t uint32_t y
Expr * IgnoreParenLValueCasts() LLVM_READONLY
QualType getUnqualifiedType() const
Retrieve the unqualified variant of the given type, removing as little sugar as possible.
Defines the clang::TargetInfo interface.
A reference to a declared variable, function, enum, etc. [C99 6.5.1p2].
static bool isArc4RandomAvailable(const ASTContext &Ctx)