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::DragonFly;
64 enum { num_setids = 6 };
68 const ChecksFilter &filter;
72 const ChecksFilter &f)
73 : BR(br), AC(ac), II_setid(),
81 void VisitStmt(
Stmt *S) { VisitChildren(S); }
83 void VisitChildren(
Stmt *S);
91 void checkLoopConditionForFloat(
const ForStmt *FS);
92 void checkCall_gets(
const CallExpr *CE,
const FunctionDecl *FD);
93 void checkCall_getpw(
const CallExpr *CE,
const FunctionDecl *FD);
94 void checkCall_mktemp(
const CallExpr *CE,
const FunctionDecl *FD);
95 void checkCall_mkstemp(
const CallExpr *CE,
const FunctionDecl *FD);
96 void checkCall_strcpy(
const CallExpr *CE,
const FunctionDecl *FD);
97 void checkCall_strcat(
const CallExpr *CE,
const FunctionDecl *FD);
98 void checkCall_rand(
const CallExpr *CE,
const FunctionDecl *FD);
99 void checkCall_random(
const CallExpr *CE,
const FunctionDecl *FD);
100 void checkCall_vfork(
const CallExpr *CE,
const FunctionDecl *FD);
101 void checkUncheckedReturnValue(CallExpr *CE);
109 void WalkAST::VisitChildren(
Stmt *S) {
115 void WalkAST::VisitCallExpr(
CallExpr *CE) {
126 StringRef Name = II->
getName();
127 if (Name.startswith(
"__builtin_"))
128 Name = Name.substr(10);
131 FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
132 .Case(
"gets", &WalkAST::checkCall_gets)
133 .Case(
"getpw", &WalkAST::checkCall_getpw)
134 .Case(
"mktemp", &WalkAST::checkCall_mktemp)
135 .Case(
"mkstemp", &WalkAST::checkCall_mkstemp)
136 .Case(
"mkdtemp", &WalkAST::checkCall_mkstemp)
137 .Case(
"mkstemps", &WalkAST::checkCall_mkstemp)
138 .Cases(
"strcpy",
"__strcpy_chk", &WalkAST::checkCall_strcpy)
139 .Cases(
"strcat",
"__strcat_chk", &WalkAST::checkCall_strcat)
140 .Case(
"drand48", &WalkAST::checkCall_rand)
141 .Case(
"erand48", &WalkAST::checkCall_rand)
142 .Case(
"jrand48", &WalkAST::checkCall_rand)
143 .Case(
"lrand48", &WalkAST::checkCall_rand)
144 .Case(
"mrand48", &WalkAST::checkCall_rand)
145 .Case(
"nrand48", &WalkAST::checkCall_rand)
146 .Case(
"lcong48", &WalkAST::checkCall_rand)
147 .Case(
"rand", &WalkAST::checkCall_rand)
148 .Case(
"rand_r", &WalkAST::checkCall_rand)
149 .Case(
"random", &WalkAST::checkCall_random)
150 .Case(
"vfork", &WalkAST::checkCall_vfork)
156 (this->*evalFunction)(CE, FD);
165 if (
CallExpr *CE = dyn_cast<CallExpr>(Child))
166 checkUncheckedReturnValue(CE);
171 void WalkAST::VisitForStmt(
ForStmt *FS) {
172 checkLoopConditionForFloat(FS);
189 if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
190 B->getOpcode() == BO_Comma))
202 if (
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
204 return ND == x || ND == y ? DR :
nullptr;
208 return U->isIncrementDecrementOp()
218 void WalkAST::checkLoopConditionForFloat(
const ForStmt *FS) {
219 if (!filter.check_FloatLoopCounter)
256 drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS :
nullptr;
258 if (!drLHS && !drRHS)
261 const VarDecl *vdLHS = drLHS ? dyn_cast<
VarDecl>(drLHS->getDecl()) :
nullptr;
262 const VarDecl *vdRHS = drRHS ? dyn_cast<
VarDecl>(drRHS->getDecl()) :
nullptr;
264 if (!vdLHS && !vdRHS)
280 llvm::raw_svector_ostream os(sbuf);
282 os <<
"Variable '" << drCond->getDecl()->getName()
283 <<
"' with floating point type '" << drCond->getType().getAsString()
284 <<
"' should not be used as a loop counter";
286 ranges.push_back(drCond->getSourceRange());
289 const char *bugType =
"Floating point variable used as loop counter";
293 BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
294 bugType,
"Security", os.str(),
306 if (!filter.check_gets)
328 BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
329 "Potential buffer overflow in call to 'gets'",
331 "Call to function 'gets' is extremely insecure as it can " 332 "always result in a buffer overflow",
342 if (!filter.check_getpw)
368 BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
369 "Potential buffer overflow in call to 'getpw'",
371 "The getpw() function is dangerous as it may overflow the " 372 "provided buffer. It is obsoleted by getpwuid().",
382 if (!filter.check_mktemp) {
385 checkCall_mkstemp(CE, FD);
409 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
410 "Potential insecure temporary file in call 'mktemp'",
412 "Call to function 'mktemp' is insecure as it always " 413 "creates or uses insecure temporary file. Use 'mkstemp' " 424 if (!filter.check_mkstemp)
428 std::pair<signed, signed> ArgSuffix =
429 llvm::StringSwitch<std::pair<signed, signed> >(Name)
430 .Case(
"mktemp", std::make_pair(0,-1))
431 .Case(
"mkstemp", std::make_pair(0,-1))
432 .Case(
"mkdtemp", std::make_pair(0,-1))
433 .Case(
"mkstemps", std::make_pair(0,1))
434 .
Default(std::make_pair(-1, -1));
436 assert(ArgSuffix.first >= 0 &&
"Unsupported function");
440 if ((
signed) numArgs <= ArgSuffix.first)
456 unsigned n = str.size();
460 if (ArgSuffix.second >= 0) {
461 const Expr *suffixEx = CE->
getArg((
unsigned)ArgSuffix.second);
466 if (Result.isNegative())
468 suffix = (unsigned) Result.getZExtValue();
469 n = (n > suffix) ? n - suffix : 0;
472 for (
unsigned i = 0; i < n; ++i)
473 if (str[i] ==
'X') ++numX;
482 llvm::raw_svector_ostream out(buf);
483 out <<
"Call to '" << Name <<
"' should have at least 6 'X's in the" 484 " format string to be secure (" << numX <<
" 'X'";
489 out <<
", " << suffix <<
" character";
492 out <<
" used as a suffix";
495 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
496 "Insecure temporary file creation",
"Security",
507 if (!filter.check_strcpy)
510 if (!checkCall_strCommon(CE, FD))
516 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
517 "Potential insecure memory buffer bounds restriction in " 520 "Call to function 'strcpy' is insecure as it does not " 521 "provide bounding of the memory buffer. Replace " 522 "unbounded copy functions with analogous functions that " 523 "support length arguments such as 'strlcpy'. CWE-119.",
534 if (!filter.check_strcpy)
537 if (!checkCall_strCommon(CE, FD))
543 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
544 "Potential insecure memory buffer bounds restriction in " 547 "Call to function 'strcat' is insecure as it does not " 548 "provide bounding of the memory buffer. Replace " 549 "unbounded copy functions with analogous functions that " 550 "support length arguments such as 'strlcat'. CWE-119.",
564 if (numArgs != 2 && numArgs != 3)
568 for (
int i = 0; i < 2; i++) {
589 if (!filter.check_rand || !CheckRand)
610 llvm::raw_svector_ostream os1(buf1);
611 os1 <<
'\'' << *FD <<
"' is a poor random number generator";
614 llvm::raw_svector_ostream os2(buf2);
615 os2 <<
"Function '" << *FD
616 <<
"' is obsolete because it implements a poor random number generator." 617 <<
" Use 'arc4random' instead";
621 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
622 "Security", os2.str(), CELoc,
632 if (!CheckRand || !filter.check_rand)
646 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
647 "'random' is not a secure random number generator",
649 "The 'random' function produces a sequence of values that " 650 "an adversary may be able to predict. Use 'arc4random' " 660 if (!filter.check_vfork)
666 BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
667 "Potential insecure implementation-specific behavior in " 670 "Call to function 'vfork' is insecure as it can lead to " 671 "denial of service situations in the parent process. " 672 "Replace calls to vfork with calls to the safer " 673 "'posix_spawn' function",
682 void WalkAST::checkUncheckedReturnValue(
CallExpr *CE) {
683 if (!filter.check_UncheckedReturn)
690 if (II_setid[0] ==
nullptr) {
691 static const char *
const identifiers[num_setids] = {
692 "setuid",
"setgid",
"seteuid",
"setegid",
693 "setreuid",
"setregid" 696 for (
size_t i = 0; i < num_setids; i++)
697 II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
703 for (identifierid = 0; identifierid < num_setids; identifierid++)
704 if (
id == II_setid[identifierid])
707 if (identifierid >= num_setids)
726 llvm::raw_svector_ostream os1(buf1);
727 os1 <<
"Return value is not checked in call to '" << *FD <<
'\'';
730 llvm::raw_svector_ostream os2(buf2);
731 os2 <<
"The return value from the call to '" << *FD
732 <<
"' is not checked. If an error occurs in '" << *FD
733 <<
"', the following code may execute with unexpected privileges";
737 BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
738 "Security", os2.str(), CELoc,
747 class SecuritySyntaxChecker :
public Checker<check::ASTCodeBody> {
759 #define REGISTER_CHECKER(name) \ 760 void ento::register##name(CheckerManager &mgr) { \ 761 SecuritySyntaxChecker *checker = \ 762 mgr.registerChecker<SecuritySyntaxChecker>(); \ 763 checker->filter.check_##name = true; \ 764 checker->filter.checkName_##name = mgr.getCurrentCheckName(); \
An instance of this class is created to represent a function declaration or definition.
PointerType - C99 6.7.5.1 - Pointer Declarators.
QualType getPointeeType() const
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
Stmt - This represents one statement.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
A helper class which wraps a boolean value set to false by default.
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
bool isRealFloatingType() const
Floating point categories.
Decl - This represents one declaration (or definition), e.g.
const TargetInfo & getTargetInfo() const
bool EvaluateAsInt(llvm::APSInt &Result, const ASTContext &Ctx, SideEffectsKind AllowSideEffects=SE_NoSideEffects) const
EvaluateAsInt - Return true if this is a constant which we can fold and convert to an integer...
VarDecl - An instance of this class is created to represent a variable declaration or definition...
unsigned getNumParams() const
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
const T * getAs() const
Member-template getAs<specific type>'.
IdentifierInfo * getIdentifier() const
getIdentifier - Get the identifier that names this declaration, if there is one.
One of these records is kept for each identifier that is lexed.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
AnalysisDeclContext contains the context data for the function or method under analysis.
unsigned getCharByteWidth() const
static bool isRelationalOp(Opcode Opc)
ForStmt - This represents a 'for (init;cond;inc)' stmt.
static bool isEqualityOp(Opcode Opc)
ASTContext & getContext()
A builtin binary operation expression such as "x + y" or "x <= y".
Expr * IgnoreParenCasts() LLVM_READONLY
IgnoreParenCasts - Ignore parentheses and casts.
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
bool isIntegralOrUnscopedEnumerationType() const
Determine whether this type is an integral or unscoped enumeration type.
StringRef getString() const
CompoundStmt - This represents a group of statements like { stmt stmt }.
Represents a prototype with parameter type info, e.g.
Expr - This represents one expression.
const FunctionProtoType * T
const Expr * getCallee() const
UnaryOperator - This represents the unary-expression's (except sizeof and alignof), the postinc/postdec operators from postfix-expression, and various extensions.
BugReporter is a utility class for generating PathDiagnostics for analysis.
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)
StmtVisitor - This class implements a simple visitor for Stmt subclasses.
#define REGISTER_CHECKER(name)
StringRef getName() const
Return the actual identifier string.
Dataflow Directional Tag Classes.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return 0.
Expr * IgnoreParenImpCasts() LLVM_READONLY
IgnoreParenImpCasts - Ignore parentheses and implicit casts.
Expr * IgnoreParenLValueCasts() LLVM_READONLY
Ignore parentheses and lvalue casts.
QualType getParamType(unsigned i) const
QualType getUnqualifiedType() const
Retrieve the unqualified variant of the given type, removing as little sugar as possible.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
StringLiteral - This represents a string literal expression, e.g.
Defines the clang::TargetInfo interface.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
A reference to a declared variable, function, enum, etc.
static bool isArc4RandomAvailable(const ASTContext &Ctx)
NamedDecl - This represents a decl with a name.