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 ||
40 DefaultBool check_bcmp;
41 DefaultBool check_bcopy;
42 DefaultBool check_bzero;
43 DefaultBool check_gets;
44 DefaultBool check_getpw;
45 DefaultBool check_mktemp;
46 DefaultBool check_mkstemp;
47 DefaultBool check_strcpy;
48 DefaultBool check_rand;
49 DefaultBool check_vfork;
50 DefaultBool check_FloatLoopCounter;
51 DefaultBool check_UncheckedReturn;
53 CheckName checkName_bcmp;
54 CheckName checkName_bcopy;
55 CheckName checkName_bzero;
56 CheckName checkName_gets;
57 CheckName checkName_getpw;
58 CheckName checkName_mktemp;
59 CheckName checkName_mkstemp;
60 CheckName checkName_strcpy;
61 CheckName checkName_rand;
62 CheckName checkName_vfork;
63 CheckName checkName_FloatLoopCounter;
64 CheckName checkName_UncheckedReturn;
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";
303 PathDiagnosticLocation FSLoc =
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++) {
343 PathDiagnosticLocation CELoc =
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++) {
385 PathDiagnosticLocation CELoc =
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)
426 PathDiagnosticLocation CELoc =
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)
464 PathDiagnosticLocation CELoc =
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)
504 PathDiagnosticLocation CELoc =
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);
545 PathDiagnosticLocation CELoc =
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);
603 llvm::APSInt Result = EVResult.
Val.
getInt();
605 if (Result.isNegative())
607 suffix = (unsigned) Result.getZExtValue();
608 n = (n > suffix) ? n - suffix : 0;
611 for (
unsigned i = 0; i < n; ++i)
612 if (str[i] ==
'X') ++numX;
618 PathDiagnosticLocation CELoc =
621 llvm::raw_svector_ostream out(buf);
622 out <<
"Call to '" << Name <<
"' should have at least 6 'X's in the" 623 " format string to be secure (" << numX <<
" 'X'";
628 out <<
", " << suffix <<
" character";
631 out <<
" used as a suffix";
634 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
635 "Insecure temporary file creation",
"Security",
646 if (!filter.check_strcpy)
649 if (!checkCall_strCommon(CE, FD))
655 if (
const auto *Array = dyn_cast<ConstantArrayType>(
Target->getType())) {
656 uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;
657 if (
const auto *String = dyn_cast<StringLiteral>(Source)) {
658 if (ArraySize >= String->getLength() + 1)
664 PathDiagnosticLocation CELoc =
666 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
667 "Potential insecure memory buffer bounds restriction in " 670 "Call to function 'strcpy' is insecure as it does not " 671 "provide bounding of the memory buffer. Replace " 672 "unbounded copy functions with analogous functions that " 673 "support length arguments such as 'strlcpy'. CWE-119.",
684 if (!filter.check_strcpy)
687 if (!checkCall_strCommon(CE, FD))
691 PathDiagnosticLocation CELoc =
693 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
694 "Potential insecure memory buffer bounds restriction in " 697 "Call to function 'strcat' is insecure as it does not " 698 "provide bounding of the memory buffer. Replace " 699 "unbounded copy functions with analogous functions that " 700 "support length arguments such as 'strlcat'. CWE-119.",
714 if (numArgs != 2 && numArgs != 3)
718 for (
int i = 0; i < 2; i++) {
739 if (!filter.check_rand || !CheckRand)
760 llvm::raw_svector_ostream os1(buf1);
761 os1 <<
'\'' << *FD <<
"' is a poor random number generator";
764 llvm::raw_svector_ostream os2(buf2);
765 os2 <<
"Function '" << *FD
766 <<
"' is obsolete because it implements a poor random number generator." 767 <<
" Use 'arc4random' instead";
769 PathDiagnosticLocation CELoc =
771 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
772 "Security", os2.str(), CELoc,
782 if (!CheckRand || !filter.check_rand)
794 PathDiagnosticLocation CELoc =
796 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
797 "'random' is not a secure random number generator",
799 "The 'random' function produces a sequence of values that " 800 "an adversary may be able to predict. Use 'arc4random' " 810 if (!filter.check_vfork)
814 PathDiagnosticLocation CELoc =
816 BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
817 "Potential insecure implementation-specific behavior in " 820 "Call to function 'vfork' is insecure as it can lead to " 821 "denial of service situations in the parent process. " 822 "Replace calls to vfork with calls to the safer " 823 "'posix_spawn' function",
832 void WalkAST::checkUncheckedReturnValue(
CallExpr *CE) {
833 if (!filter.check_UncheckedReturn)
840 if (II_setid[0] ==
nullptr) {
841 static const char *
const identifiers[num_setids] = {
842 "setuid",
"setgid",
"seteuid",
"setegid",
843 "setreuid",
"setregid" 846 for (
size_t i = 0; i < num_setids; i++)
847 II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
853 for (identifierid = 0; identifierid < num_setids; identifierid++)
854 if (
id == II_setid[identifierid])
857 if (identifierid >= num_setids)
876 llvm::raw_svector_ostream os1(buf1);
877 os1 <<
"Return value is not checked in call to '" << *FD <<
'\'';
880 llvm::raw_svector_ostream os2(buf2);
881 os2 <<
"The return value from the call to '" << *FD
882 <<
"' is not checked. If an error occurs in '" << *FD
883 <<
"', the following code may execute with unexpected privileges";
885 PathDiagnosticLocation CELoc =
887 BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
888 "Security", os2.str(), CELoc,
897 class SecuritySyntaxChecker :
public Checker<check::ASTCodeBody> {
901 void checkASTCodeBody(
const Decl *D, AnalysisManager& mgr,
902 BugReporter &BR)
const {
903 WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
909 #define REGISTER_CHECKER(name) \ 910 void ento::register##name(CheckerManager &mgr) { \ 911 SecuritySyntaxChecker *checker = \ 912 mgr.registerChecker<SecuritySyntaxChecker>(); \ 913 checker->filter.check_##name = true; \ 914 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...
bool EvaluateAsInt(EvalResult &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...
Stmt - This represents one statement.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
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
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)
APValue Val
Val - This is the value the expression can be folded to.
A builtin binary operation expression such as "x + y" or "x <= y".
Expr * IgnoreParenCasts() LLVM_READONLY
IgnoreParenCasts - Ignore parentheses and casts.
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.
This represents one expression.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
UnaryOperator - This represents the unary-expression's (except sizeof and alignof), the postinc/postdec operators from postfix-expression, and various extensions.
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.
EvalResult is a struct with detailed info about an evaluated expression.
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.