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);
92 void checkLoopConditionForFloat(
const ForStmt *FS);
102 void checkUncheckedReturnValue(
CallExpr *CE);
110 void WalkAST::VisitChildren(
Stmt *
S) {
111 for (
Stmt *Child : S->children())
116 void WalkAST::VisitCallExpr(
CallExpr *CE) {
128 if (Name.startswith(
"__builtin_"))
129 Name = Name.substr(10);
132 FnCheck evalFunction = llvm::StringSwitch<FnCheck>(
Name)
133 .Case(
"gets", &WalkAST::checkCall_gets)
134 .Case(
"getpw", &WalkAST::checkCall_getpw)
135 .Case(
"mktemp", &WalkAST::checkCall_mktemp)
136 .Case(
"mkstemp", &WalkAST::checkCall_mkstemp)
137 .Case(
"mkdtemp", &WalkAST::checkCall_mkstemp)
138 .Case(
"mkstemps", &WalkAST::checkCall_mkstemp)
139 .Cases(
"strcpy",
"__strcpy_chk", &WalkAST::checkCall_strcpy)
140 .Cases(
"strcat",
"__strcat_chk", &WalkAST::checkCall_strcat)
141 .Case(
"drand48", &WalkAST::checkCall_rand)
142 .Case(
"erand48", &WalkAST::checkCall_rand)
143 .Case(
"jrand48", &WalkAST::checkCall_rand)
144 .Case(
"lrand48", &WalkAST::checkCall_rand)
145 .Case(
"mrand48", &WalkAST::checkCall_rand)
146 .Case(
"nrand48", &WalkAST::checkCall_rand)
147 .Case(
"lcong48", &WalkAST::checkCall_rand)
148 .Case(
"rand", &WalkAST::checkCall_rand)
149 .Case(
"rand_r", &WalkAST::checkCall_rand)
150 .Case(
"random", &WalkAST::checkCall_random)
151 .Case(
"vfork", &WalkAST::checkCall_vfork)
157 (this->*evalFunction)(CE, FD);
166 if (
CallExpr *CE = dyn_cast<CallExpr>(Child))
167 checkUncheckedReturnValue(CE);
172 void WalkAST::VisitForStmt(
ForStmt *FS) {
173 checkLoopConditionForFloat(FS);
190 if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
191 B->getOpcode() == BO_Comma))
203 if (
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
205 return ND == x || ND == y ? DR :
nullptr;
209 return U->isIncrementDecrementOp()
219 void WalkAST::checkLoopConditionForFloat(
const ForStmt *FS) {
220 if (!filter.check_FloatLoopCounter)
257 drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS :
nullptr;
259 if (!drLHS && !drRHS)
262 const VarDecl *vdLHS = drLHS ? dyn_cast<
VarDecl>(drLHS->getDecl()) :
nullptr;
263 const VarDecl *vdRHS = drRHS ? dyn_cast<
VarDecl>(drRHS->getDecl()) :
nullptr;
265 if (!vdLHS && !vdRHS)
281 llvm::raw_svector_ostream os(sbuf);
283 os <<
"Variable '" << drCond->getDecl()->getName()
284 <<
"' with floating point type '" << drCond->getType().getAsString()
285 <<
"' should not be used as a loop counter";
287 ranges.push_back(drCond->getSourceRange());
288 ranges.push_back(drInc->getSourceRange());
290 const char *bugType =
"Floating point variable used as loop counter";
294 BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
295 bugType,
"Security", os.str(),
307 if (!filter.check_gets)
329 BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
330 "Potential buffer overflow in call to 'gets'",
332 "Call to function 'gets' is extremely insecure as it can "
333 "always result in a buffer overflow",
334 CELoc, CE->
getCallee()->getSourceRange());
343 if (!filter.check_getpw)
369 BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
370 "Potential buffer overflow in call to 'getpw'",
372 "The getpw() function is dangerous as it may overflow the "
373 "provided buffer. It is obsoleted by getpwuid().",
374 CELoc, CE->
getCallee()->getSourceRange());
383 if (!filter.check_mktemp) {
386 checkCall_mkstemp(CE, FD);
410 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
411 "Potential insecure temporary file in call 'mktemp'",
413 "Call to function 'mktemp' is insecure as it always "
414 "creates or uses insecure temporary file. Use 'mkstemp' "
416 CELoc, CE->
getCallee()->getSourceRange());
425 if (!filter.check_mkstemp)
429 std::pair<signed, signed> ArgSuffix =
430 llvm::StringSwitch<std::pair<signed, signed> >(
Name)
431 .Case(
"mktemp", std::make_pair(0,-1))
432 .Case(
"mkstemp", std::make_pair(0,-1))
433 .Case(
"mkdtemp", std::make_pair(0,-1))
434 .Case(
"mkstemps", std::make_pair(0,1))
435 .Default(std::make_pair(-1, -1));
437 assert(ArgSuffix.first >= 0 &&
"Unsupported function");
441 if ((
signed) numArgs <= ArgSuffix.first)
457 unsigned n = str.size();
461 if (ArgSuffix.second >= 0) {
462 const Expr *suffixEx = CE->
getArg((
unsigned)ArgSuffix.second);
467 if (Result.isNegative())
469 suffix = (
unsigned) Result.getZExtValue();
470 n = (n > suffix) ? n - suffix : 0;
473 for (
unsigned i = 0; i < n; ++i)
474 if (str[i] ==
'X') ++numX;
483 llvm::raw_svector_ostream out(buf);
484 out <<
"Call to '" << Name <<
"' should have at least 6 'X's in the"
485 " format string to be secure (" << numX <<
" 'X'";
490 out <<
", " << suffix <<
" character";
493 out <<
" used as a suffix";
496 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
497 "Insecure temporary file creation",
"Security",
498 out.str(), CELoc, strArg->getSourceRange());
508 if (!filter.check_strcpy)
511 if (!checkCall_strCommon(CE, FD))
517 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
518 "Potential insecure memory buffer bounds restriction in "
521 "Call to function 'strcpy' is insecure as it does not "
522 "provide bounding of the memory buffer. Replace "
523 "unbounded copy functions with analogous functions that "
524 "support length arguments such as 'strlcpy'. CWE-119.",
525 CELoc, CE->
getCallee()->getSourceRange());
535 if (!filter.check_strcpy)
538 if (!checkCall_strCommon(CE, FD))
544 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
545 "Potential insecure memory buffer bounds restriction in "
548 "Call to function 'strcat' is insecure as it does not "
549 "provide bounding of the memory buffer. Replace "
550 "unbounded copy functions with analogous functions that "
551 "support length arguments such as 'strlcat'. CWE-119.",
552 CELoc, CE->
getCallee()->getSourceRange());
565 if (numArgs != 2 && numArgs != 3)
569 for (
int i = 0; i < 2; i++) {
590 if (!filter.check_rand || !CheckRand)
611 llvm::raw_svector_ostream os1(buf1);
612 os1 <<
'\'' << *FD <<
"' is a poor random number generator";
615 llvm::raw_svector_ostream os2(buf2);
616 os2 <<
"Function '" << *FD
617 <<
"' is obsolete because it implements a poor random number generator."
618 <<
" Use 'arc4random' instead";
622 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
623 "Security", os2.str(), CELoc,
633 if (!CheckRand || !filter.check_rand)
647 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
648 "'random' is not a secure random number generator",
650 "The 'random' function produces a sequence of values that "
651 "an adversary may be able to predict. Use 'arc4random' "
652 "instead", CELoc, CE->
getCallee()->getSourceRange());
661 if (!filter.check_vfork)
667 BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
668 "Potential insecure implementation-specific behavior in "
671 "Call to function 'vfork' is insecure as it can lead to "
672 "denial of service situations in the parent process. "
673 "Replace calls to vfork with calls to the safer "
674 "'posix_spawn' function",
675 CELoc, CE->
getCallee()->getSourceRange());
683 void WalkAST::checkUncheckedReturnValue(
CallExpr *CE) {
684 if (!filter.check_UncheckedReturn)
691 if (II_setid[0] ==
nullptr) {
692 static const char *
const identifiers[num_setids] = {
693 "setuid",
"setgid",
"seteuid",
"setegid",
694 "setreuid",
"setregid"
697 for (
size_t i = 0; i < num_setids; i++)
698 II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
704 for (identifierid = 0; identifierid < num_setids; identifierid++)
705 if (
id == II_setid[identifierid])
708 if (identifierid >= num_setids)
727 llvm::raw_svector_ostream os1(buf1);
728 os1 <<
"Return value is not checked in call to '" << *FD <<
'\'';
731 llvm::raw_svector_ostream os2(buf2);
732 os2 <<
"The return value from the call to '" << *FD
733 <<
"' is not checked. If an error occurs in '" << *FD
734 <<
"', the following code may execute with unexpected privileges";
738 BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
739 "Security", os2.str(), CELoc,
748 class SecuritySyntaxChecker :
public Checker<check::ASTCodeBody> {
755 walker.Visit(D->getBody());
760 #define REGISTER_CHECKER(name) \
761 void ento::register##name(CheckerManager &mgr) { \
762 SecuritySyntaxChecker *checker = \
763 mgr.registerChecker<SecuritySyntaxChecker>(); \
764 checker->filter.check_##name = true; \
765 checker->filter.checkName_##name = mgr.getCurrentCheckName(); \
FunctionDecl - An instance of this class is created to represent a function declaration or definition...
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
PointerType - C99 6.7.5.1 - Pointer Declarators.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
IdentifierInfo * getIdentifier() const
getIdentifier - Get the identifier that names this declaration, if there is one.
A helper class which wraps a boolean value set to false by default.
VarDecl - An instance of this class is created to represent a variable declaration or definition...
const Expr * getCallee() const
unsigned getNumParams() const
One of these records is kept for each identifier that is lexed.
class LLVM_ALIGNAS(8) DependentTemplateSpecializationType const IdentifierInfo * Name
Represents a template specialization type whose template cannot be resolved, e.g. ...
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.
static bool isRelationalOp(Opcode Opc)
bool isIntegralOrUnscopedEnumerationType() const
Determine whether this type is an integral or unscoped enumeration type.
ForStmt - This represents a 'for (init;cond;inc)' stmt.
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
IgnoreParenCasts - Ignore parentheses and casts.
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
CompoundStmt - This represents a group of statements like { stmt stmt }.
QualType getParamType(unsigned i) const
Represents a prototype with parameter type info, e.g.
bool isRealFloatingType() const
Floating point categories.
Expr - This represents one expression.
StringRef getName() const
Return the actual identifier string.
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...
UnaryOperator - This represents the unary-expression's (except sizeof and alignof), the postinc/postdec operators from postfix-expression, and various extensions.
The result type of a method or function.
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)
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
getNumArgs - Return the number of actual arguments to this call.
Expr * IgnoreParenImpCasts() LLVM_READONLY
IgnoreParenImpCasts - Ignore parentheses and implicit casts.
const T * getAs() const
Member-template getAs<specific type>'.
Expr * IgnoreParenLValueCasts() LLVM_READONLY
Ignore parentheses and lvalue casts.
QualType getUnqualifiedType() const
Retrieve the unqualified variant of the given type, removing as little sugar as possible.
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.