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;
70 enum { num_setids = 6 };
74 const ChecksFilter &filter;
78 const ChecksFilter &f)
79 : BR(br), AC(ac), II_setid(),
87 void VisitStmt(
Stmt *S) { VisitChildren(S); }
89 void VisitChildren(
Stmt *S);
97 void checkLoopConditionForFloat(
const ForStmt *FS);
98 void checkCall_bcmp(
const CallExpr *CE,
const FunctionDecl *FD);
99 void checkCall_bcopy(
const CallExpr *CE,
const FunctionDecl *FD);
100 void checkCall_bzero(
const CallExpr *CE,
const FunctionDecl *FD);
101 void checkCall_gets(
const CallExpr *CE,
const FunctionDecl *FD);
102 void checkCall_getpw(
const CallExpr *CE,
const FunctionDecl *FD);
103 void checkCall_mktemp(
const CallExpr *CE,
const FunctionDecl *FD);
104 void checkCall_mkstemp(
const CallExpr *CE,
const FunctionDecl *FD);
105 void checkCall_strcpy(
const CallExpr *CE,
const FunctionDecl *FD);
106 void checkCall_strcat(
const CallExpr *CE,
const FunctionDecl *FD);
107 void checkCall_rand(
const CallExpr *CE,
const FunctionDecl *FD);
108 void checkCall_random(
const CallExpr *CE,
const FunctionDecl *FD);
109 void checkCall_vfork(
const CallExpr *CE,
const FunctionDecl *FD);
110 void checkUncheckedReturnValue(CallExpr *CE);
118 void WalkAST::VisitChildren(
Stmt *S) {
124 void WalkAST::VisitCallExpr(
CallExpr *CE) {
135 StringRef Name = II->
getName();
136 if (Name.startswith(
"__builtin_"))
137 Name = Name.substr(10);
140 FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
141 .Case(
"bcmp", &WalkAST::checkCall_bcmp)
142 .Case(
"bcopy", &WalkAST::checkCall_bcopy)
143 .Case(
"bzero", &WalkAST::checkCall_bzero)
144 .Case(
"gets", &WalkAST::checkCall_gets)
145 .Case(
"getpw", &WalkAST::checkCall_getpw)
146 .Case(
"mktemp", &WalkAST::checkCall_mktemp)
147 .Case(
"mkstemp", &WalkAST::checkCall_mkstemp)
148 .Case(
"mkdtemp", &WalkAST::checkCall_mkstemp)
149 .Case(
"mkstemps", &WalkAST::checkCall_mkstemp)
150 .Cases(
"strcpy",
"__strcpy_chk", &WalkAST::checkCall_strcpy)
151 .Cases(
"strcat",
"__strcat_chk", &WalkAST::checkCall_strcat)
152 .Case(
"drand48", &WalkAST::checkCall_rand)
153 .Case(
"erand48", &WalkAST::checkCall_rand)
154 .Case(
"jrand48", &WalkAST::checkCall_rand)
155 .Case(
"lrand48", &WalkAST::checkCall_rand)
156 .Case(
"mrand48", &WalkAST::checkCall_rand)
157 .Case(
"nrand48", &WalkAST::checkCall_rand)
158 .Case(
"lcong48", &WalkAST::checkCall_rand)
159 .Case(
"rand", &WalkAST::checkCall_rand)
160 .Case(
"rand_r", &WalkAST::checkCall_rand)
161 .Case(
"random", &WalkAST::checkCall_random)
162 .Case(
"vfork", &WalkAST::checkCall_vfork)
168 (this->*evalFunction)(CE, FD);
177 if (
CallExpr *CE = dyn_cast<CallExpr>(Child))
178 checkUncheckedReturnValue(CE);
183 void WalkAST::VisitForStmt(
ForStmt *FS) {
184 checkLoopConditionForFloat(FS);
201 if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
202 B->getOpcode() == BO_Comma))
214 if (
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
216 return ND == x || ND == y ? DR :
nullptr;
220 return U->isIncrementDecrementOp()
230 void WalkAST::checkLoopConditionForFloat(
const ForStmt *FS) {
231 if (!filter.check_FloatLoopCounter)
268 drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS :
nullptr;
270 if (!drLHS && !drRHS)
273 const VarDecl *vdLHS = drLHS ? dyn_cast<
VarDecl>(drLHS->getDecl()) :
nullptr;
274 const VarDecl *vdRHS = drRHS ? dyn_cast<
VarDecl>(drRHS->getDecl()) :
nullptr;
276 if (!vdLHS && !vdRHS)
292 llvm::raw_svector_ostream os(sbuf);
294 os <<
"Variable '" << drCond->getDecl()->getName()
295 <<
"' with floating point type '" << drCond->getType().getAsString()
296 <<
"' should not be used as a loop counter";
298 ranges.push_back(drCond->getSourceRange());
301 const char *bugType =
"Floating point variable used as loop counter";
305 BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
306 bugType,
"Security", os.str(),
317 if (!filter.check_bcmp)
328 for (
int i = 0; i < 2; i++) {
345 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp,
346 "Use of deprecated function in call to 'bcmp()'",
348 "The bcmp() function is obsoleted by memcmp().",
359 if (!filter.check_bcopy)
370 for (
int i = 0; i < 2; i++) {
387 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy,
388 "Use of deprecated function in call to 'bcopy()'",
390 "The bcopy() function is obsoleted by memcpy() " 402 if (!filter.check_bzero)
428 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero,
429 "Use of deprecated function in call to 'bzero()'",
431 "The bzero() function is obsoleted by memset().",
444 if (!filter.check_gets)
466 BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
467 "Potential buffer overflow in call to 'gets'",
469 "Call to function 'gets' is extremely insecure as it can " 470 "always result in a buffer overflow",
480 if (!filter.check_getpw)
506 BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
507 "Potential buffer overflow in call to 'getpw'",
509 "The getpw() function is dangerous as it may overflow the " 510 "provided buffer. It is obsoleted by getpwuid().",
520 if (!filter.check_mktemp) {
523 checkCall_mkstemp(CE, FD);
547 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
548 "Potential insecure temporary file in call 'mktemp'",
550 "Call to function 'mktemp' is insecure as it always " 551 "creates or uses insecure temporary file. Use 'mkstemp' " 562 if (!filter.check_mkstemp)
566 std::pair<signed, signed> ArgSuffix =
567 llvm::StringSwitch<std::pair<signed, signed> >(Name)
568 .Case(
"mktemp", std::make_pair(0,-1))
569 .Case(
"mkstemp", std::make_pair(0,-1))
570 .Case(
"mkdtemp", std::make_pair(0,-1))
571 .Case(
"mkstemps", std::make_pair(0,1))
572 .
Default(std::make_pair(-1, -1));
574 assert(ArgSuffix.first >= 0 &&
"Unsupported function");
578 if ((
signed) numArgs <= ArgSuffix.first)
594 unsigned n = str.size();
598 if (ArgSuffix.second >= 0) {
599 const Expr *suffixEx = CE->
getArg((
unsigned)ArgSuffix.second);
604 if (Result.isNegative())
606 suffix = (unsigned) Result.getZExtValue();
607 n = (n > suffix) ? n - suffix : 0;
610 for (
unsigned i = 0; i < n; ++i)
611 if (str[i] ==
'X') ++numX;
620 llvm::raw_svector_ostream out(buf);
621 out <<
"Call to '" << Name <<
"' should have at least 6 'X's in the" 622 " format string to be secure (" << numX <<
" 'X'";
627 out <<
", " << suffix <<
" character";
630 out <<
" used as a suffix";
633 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
634 "Insecure temporary file creation",
"Security",
645 if (!filter.check_strcpy)
648 if (!checkCall_strCommon(CE, FD))
653 if (
const auto *DeclRef = dyn_cast<DeclRefExpr>(
Target))
654 if (
const auto *Array = dyn_cast<ConstantArrayType>(DeclRef->getType())) {
655 uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;
656 if (
const auto *String = dyn_cast<StringLiteral>(Source)) {
657 if (ArraySize >= String->getLength() + 1)
665 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
666 "Potential insecure memory buffer bounds restriction in " 669 "Call to function 'strcpy' is insecure as it does not " 670 "provide bounding of the memory buffer. Replace " 671 "unbounded copy functions with analogous functions that " 672 "support length arguments such as 'strlcpy'. CWE-119.",
683 if (!filter.check_strcpy)
686 if (!checkCall_strCommon(CE, FD))
692 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
693 "Potential insecure memory buffer bounds restriction in " 696 "Call to function 'strcat' is insecure as it does not " 697 "provide bounding of the memory buffer. Replace " 698 "unbounded copy functions with analogous functions that " 699 "support length arguments such as 'strlcat'. CWE-119.",
713 if (numArgs != 2 && numArgs != 3)
717 for (
int i = 0; i < 2; i++) {
738 if (!filter.check_rand || !CheckRand)
759 llvm::raw_svector_ostream os1(buf1);
760 os1 <<
'\'' << *FD <<
"' is a poor random number generator";
763 llvm::raw_svector_ostream os2(buf2);
764 os2 <<
"Function '" << *FD
765 <<
"' is obsolete because it implements a poor random number generator." 766 <<
" Use 'arc4random' instead";
770 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
771 "Security", os2.str(), CELoc,
781 if (!CheckRand || !filter.check_rand)
795 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
796 "'random' is not a secure random number generator",
798 "The 'random' function produces a sequence of values that " 799 "an adversary may be able to predict. Use 'arc4random' " 809 if (!filter.check_vfork)
815 BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
816 "Potential insecure implementation-specific behavior in " 819 "Call to function 'vfork' is insecure as it can lead to " 820 "denial of service situations in the parent process. " 821 "Replace calls to vfork with calls to the safer " 822 "'posix_spawn' function",
831 void WalkAST::checkUncheckedReturnValue(
CallExpr *CE) {
832 if (!filter.check_UncheckedReturn)
839 if (II_setid[0] ==
nullptr) {
840 static const char *
const identifiers[num_setids] = {
841 "setuid",
"setgid",
"seteuid",
"setegid",
842 "setreuid",
"setregid" 845 for (
size_t i = 0; i < num_setids; i++)
846 II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
852 for (identifierid = 0; identifierid < num_setids; identifierid++)
853 if (
id == II_setid[identifierid])
856 if (identifierid >= num_setids)
875 llvm::raw_svector_ostream os1(buf1);
876 os1 <<
"Return value is not checked in call to '" << *FD <<
'\'';
879 llvm::raw_svector_ostream os2(buf2);
880 os2 <<
"The return value from the call to '" << *FD
881 <<
"' is not checked. If an error occurs in '" << *FD
882 <<
"', the following code may execute with unexpected privileges";
886 BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
887 "Security", os2.str(), CELoc,
896 class SecuritySyntaxChecker :
public Checker<check::ASTCodeBody> {
908 #define REGISTER_CHECKER(name) \ 909 void ento::register##name(CheckerManager &mgr) { \ 910 SecuritySyntaxChecker *checker = \ 911 mgr.registerChecker<SecuritySyntaxChecker>(); \ 912 checker->filter.check_##name = true; \ 913 checker->filter.checkName_##name = mgr.getCurrentCheckName(); \
Represents 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...
Represents 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
Get the identifier that names this declaration, if there is one.
Expr * IgnoreImpCasts() LLVM_READONLY
IgnoreImpCasts - Skip past any implicit casts which might surround this expression.
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 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)
This represents a decl that may have a name.