20 #include "llvm/ADT/SmallString.h" 21 #include "llvm/ADT/StringSwitch.h" 22 #include "llvm/Support/raw_ostream.h" 24 using namespace clang;
29 return T.getVendor() == llvm::Triple::Apple ||
30 T.getOS() == llvm::Triple::CloudABI ||
39 DefaultBool check_bcmp;
40 DefaultBool check_bcopy;
41 DefaultBool check_bzero;
42 DefaultBool check_gets;
43 DefaultBool check_getpw;
44 DefaultBool check_mktemp;
45 DefaultBool check_mkstemp;
46 DefaultBool check_strcpy;
47 DefaultBool check_DeprecatedOrUnsafeBufferHandling;
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_DeprecatedOrUnsafeBufferHandling;
62 CheckName checkName_rand;
63 CheckName checkName_vfork;
64 CheckName checkName_FloatLoopCounter;
65 CheckName checkName_UncheckedReturn;
71 enum { num_setids = 6 };
75 const ChecksFilter &filter;
79 const ChecksFilter &f)
80 : BR(br), AC(ac), II_setid(),
88 void VisitStmt(
Stmt *S) { VisitChildren(S); }
90 void VisitChildren(
Stmt *S);
98 void checkLoopConditionForFloat(
const ForStmt *FS);
99 void checkCall_bcmp(
const CallExpr *CE,
const FunctionDecl *FD);
100 void checkCall_bcopy(
const CallExpr *CE,
const FunctionDecl *FD);
101 void checkCall_bzero(
const CallExpr *CE,
const FunctionDecl *FD);
102 void checkCall_gets(
const CallExpr *CE,
const FunctionDecl *FD);
103 void checkCall_getpw(
const CallExpr *CE,
const FunctionDecl *FD);
104 void checkCall_mktemp(
const CallExpr *CE,
const FunctionDecl *FD);
105 void checkCall_mkstemp(
const CallExpr *CE,
const FunctionDecl *FD);
106 void checkCall_strcpy(
const CallExpr *CE,
const FunctionDecl *FD);
107 void checkCall_strcat(
const CallExpr *CE,
const FunctionDecl *FD);
108 void checkDeprecatedOrUnsafeBufferHandling(
const CallExpr *CE,
110 void checkCall_rand(
const CallExpr *CE,
const FunctionDecl *FD);
111 void checkCall_random(
const CallExpr *CE,
const FunctionDecl *FD);
112 void checkCall_vfork(
const CallExpr *CE,
const FunctionDecl *FD);
113 void checkUncheckedReturnValue(CallExpr *CE);
121 void WalkAST::VisitChildren(
Stmt *S) {
127 void WalkAST::VisitCallExpr(
CallExpr *CE) {
138 StringRef Name = II->
getName();
139 if (Name.startswith(
"__builtin_"))
140 Name = Name.substr(10);
143 FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
144 .Case(
"bcmp", &WalkAST::checkCall_bcmp)
145 .Case(
"bcopy", &WalkAST::checkCall_bcopy)
146 .Case(
"bzero", &WalkAST::checkCall_bzero)
147 .Case(
"gets", &WalkAST::checkCall_gets)
148 .Case(
"getpw", &WalkAST::checkCall_getpw)
149 .Case(
"mktemp", &WalkAST::checkCall_mktemp)
150 .Case(
"mkstemp", &WalkAST::checkCall_mkstemp)
151 .Case(
"mkdtemp", &WalkAST::checkCall_mkstemp)
152 .Case(
"mkstemps", &WalkAST::checkCall_mkstemp)
153 .Cases(
"strcpy",
"__strcpy_chk", &WalkAST::checkCall_strcpy)
154 .Cases(
"strcat",
"__strcat_chk", &WalkAST::checkCall_strcat)
155 .Cases(
"sprintf",
"vsprintf",
"scanf",
"wscanf",
"fscanf",
"fwscanf",
156 "vscanf",
"vwscanf",
"vfscanf",
"vfwscanf",
157 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
158 .Cases(
"sscanf",
"swscanf",
"vsscanf",
"vswscanf",
"swprintf",
159 "snprintf",
"vswprintf",
"vsnprintf",
"memcpy",
"memmove",
160 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
161 .Cases(
"strncpy",
"strncat",
"memset",
162 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
163 .Case(
"drand48", &WalkAST::checkCall_rand)
164 .Case(
"erand48", &WalkAST::checkCall_rand)
165 .Case(
"jrand48", &WalkAST::checkCall_rand)
166 .Case(
"lrand48", &WalkAST::checkCall_rand)
167 .Case(
"mrand48", &WalkAST::checkCall_rand)
168 .Case(
"nrand48", &WalkAST::checkCall_rand)
169 .Case(
"lcong48", &WalkAST::checkCall_rand)
170 .Case(
"rand", &WalkAST::checkCall_rand)
171 .Case(
"rand_r", &WalkAST::checkCall_rand)
172 .Case(
"random", &WalkAST::checkCall_random)
173 .Case(
"vfork", &WalkAST::checkCall_vfork)
179 (this->*evalFunction)(CE, FD);
188 if (
CallExpr *CE = dyn_cast<CallExpr>(Child))
189 checkUncheckedReturnValue(CE);
194 void WalkAST::VisitForStmt(
ForStmt *FS) {
195 checkLoopConditionForFloat(FS);
212 if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
213 B->getOpcode() == BO_Comma))
225 if (
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
227 return ND == x || ND == y ? DR :
nullptr;
231 return U->isIncrementDecrementOp()
241 void WalkAST::checkLoopConditionForFloat(
const ForStmt *FS) {
242 if (!filter.check_FloatLoopCounter)
279 drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS :
nullptr;
281 if (!drLHS && !drRHS)
284 const VarDecl *vdLHS = drLHS ? dyn_cast<
VarDecl>(drLHS->getDecl()) :
nullptr;
285 const VarDecl *vdRHS = drRHS ? dyn_cast<
VarDecl>(drRHS->getDecl()) :
nullptr;
287 if (!vdLHS && !vdRHS)
303 llvm::raw_svector_ostream os(sbuf);
305 os <<
"Variable '" << drCond->getDecl()->getName()
306 <<
"' with floating point type '" << drCond->getType().getAsString()
307 <<
"' should not be used as a loop counter";
309 ranges.push_back(drCond->getSourceRange());
312 const char *bugType =
"Floating point variable used as loop counter";
314 PathDiagnosticLocation FSLoc =
316 BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
317 bugType,
"Security", os.str(),
328 if (!filter.check_bcmp)
339 for (
int i = 0;
i < 2;
i++) {
354 PathDiagnosticLocation CELoc =
356 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp,
357 "Use of deprecated function in call to 'bcmp()'",
359 "The bcmp() function is obsoleted by memcmp().",
370 if (!filter.check_bcopy)
381 for (
int i = 0;
i < 2;
i++) {
396 PathDiagnosticLocation CELoc =
398 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy,
399 "Use of deprecated function in call to 'bcopy()'",
401 "The bcopy() function is obsoleted by memcpy() " 413 if (!filter.check_bzero)
437 PathDiagnosticLocation CELoc =
439 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero,
440 "Use of deprecated function in call to 'bzero()'",
442 "The bzero() function is obsoleted by memset().",
455 if (!filter.check_gets)
475 PathDiagnosticLocation CELoc =
477 BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
478 "Potential buffer overflow in call to 'gets'",
480 "Call to function 'gets' is extremely insecure as it can " 481 "always result in a buffer overflow",
491 if (!filter.check_getpw)
515 PathDiagnosticLocation CELoc =
517 BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
518 "Potential buffer overflow in call to 'getpw'",
520 "The getpw() function is dangerous as it may overflow the " 521 "provided buffer. It is obsoleted by getpwuid().",
531 if (!filter.check_mktemp) {
534 checkCall_mkstemp(CE, FD);
556 PathDiagnosticLocation CELoc =
558 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
559 "Potential insecure temporary file in call 'mktemp'",
561 "Call to function 'mktemp' is insecure as it always " 562 "creates or uses insecure temporary file. Use 'mkstemp' " 572 if (!filter.check_mkstemp)
576 std::pair<signed, signed> ArgSuffix =
577 llvm::StringSwitch<std::pair<signed, signed> >(Name)
578 .Case(
"mktemp", std::make_pair(0,-1))
579 .Case(
"mkstemp", std::make_pair(0,-1))
580 .Case(
"mkdtemp", std::make_pair(0,-1))
581 .Case(
"mkstemps", std::make_pair(0,1))
582 .
Default(std::make_pair(-1, -1));
584 assert(ArgSuffix.first >= 0 &&
"Unsupported function");
588 if ((
signed) numArgs <= ArgSuffix.first)
604 unsigned n = str.size();
608 if (ArgSuffix.second >= 0) {
609 const Expr *suffixEx = CE->
getArg((
unsigned)ArgSuffix.second);
613 llvm::APSInt Result = EVResult.
Val.
getInt();
615 if (Result.isNegative())
617 suffix = (unsigned) Result.getZExtValue();
618 n = (n > suffix) ? n - suffix : 0;
621 for (
unsigned i = 0;
i < n; ++
i)
622 if (str[
i] ==
'X') ++numX;
628 PathDiagnosticLocation CELoc =
631 llvm::raw_svector_ostream out(buf);
632 out <<
"Call to '" << Name <<
"' should have at least 6 'X's in the" 633 " format string to be secure (" << numX <<
" 'X'";
638 out <<
", " << suffix <<
" character";
641 out <<
" used as a suffix";
644 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
645 "Insecure temporary file creation",
"Security",
657 if (!filter.check_strcpy)
660 if (!checkCall_strCommon(CE, FD))
666 if (
const auto *Array = dyn_cast<ConstantArrayType>(
Target->getType())) {
667 uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;
668 if (
const auto *String = dyn_cast<StringLiteral>(Source)) {
669 if (ArraySize >= String->getLength() + 1)
675 PathDiagnosticLocation CELoc =
677 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
678 "Potential insecure memory buffer bounds restriction in " 681 "Call to function 'strcpy' is insecure as it does not " 682 "provide bounding of the memory buffer. Replace " 683 "unbounded copy functions with analogous functions that " 684 "support length arguments such as 'strlcpy'. CWE-119.",
696 if (!filter.check_strcpy)
699 if (!checkCall_strCommon(CE, FD))
703 PathDiagnosticLocation CELoc =
705 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
706 "Potential insecure memory buffer bounds restriction in " 709 "Call to function 'strcat' is insecure as it does not " 710 "provide bounding of the memory buffer. Replace " 711 "unbounded copy functions with analogous functions that " 712 "support length arguments such as 'strlcat'. CWE-119.",
732 void WalkAST::checkDeprecatedOrUnsafeBufferHandling(
const CallExpr *CE,
734 if (!filter.check_DeprecatedOrUnsafeBufferHandling)
737 if (!BR.getContext().getLangOpts().C11)
742 enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 };
745 if (Name.startswith(
"__builtin_"))
746 Name = Name.substr(10);
749 llvm::StringSwitch<int>(Name)
750 .Cases(
"scanf",
"wscanf",
"vscanf",
"vwscanf", 0)
751 .Cases(
"sprintf",
"vsprintf",
"fscanf",
"fwscanf",
"vfscanf",
752 "vfwscanf",
"sscanf",
"swscanf",
"vsscanf",
"vswscanf", 1)
753 .Cases(
"swprintf",
"snprintf",
"vswprintf",
"vsnprintf",
"memcpy",
754 "memmove",
"memset",
"strncpy",
"strncat", DEPR_ONLY)
755 .Default(UNKNOWN_CALL);
757 assert(ArgIndex != UNKNOWN_CALL &&
"Unsupported function");
758 bool BoundsProvided = ArgIndex == DEPR_ONLY;
760 if (!BoundsProvided) {
767 FormatString->getString().find(
"%s") == StringRef::npos &&
768 FormatString->getString().find(
"%[") == StringRef::npos)
769 BoundsProvided =
true;
774 llvm::raw_svector_ostream Out1(Buf1);
775 llvm::raw_svector_ostream Out2(Buf2);
777 Out1 <<
"Potential insecure memory buffer bounds restriction in call '" 779 Out2 <<
"Call to function '" << Name
780 <<
"' is insecure as it does not provide ";
782 if (!BoundsProvided) {
783 Out2 <<
"bounding of the memory buffer or ";
786 Out2 <<
"security checks introduced " 787 "in the C11 standard. Replace with analogous functions that " 788 "support length arguments or provides boundary checks such as '" 789 << Name <<
"_s' in case of C11";
791 PathDiagnosticLocation CELoc =
793 BR.EmitBasicReport(AC->getDecl(),
794 filter.checkName_DeprecatedOrUnsafeBufferHandling,
795 Out1.str(),
"Security", Out2.str(), CELoc,
810 if (numArgs != 2 && numArgs != 3)
814 for (
int i = 0;
i < 2;
i++) {
835 if (!filter.check_rand || !CheckRand)
856 llvm::raw_svector_ostream os1(buf1);
857 os1 <<
'\'' << *FD <<
"' is a poor random number generator";
860 llvm::raw_svector_ostream os2(buf2);
861 os2 <<
"Function '" << *FD
862 <<
"' is obsolete because it implements a poor random number generator." 863 <<
" Use 'arc4random' instead";
865 PathDiagnosticLocation CELoc =
867 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
868 "Security", os2.str(), CELoc,
878 if (!CheckRand || !filter.check_rand)
890 PathDiagnosticLocation CELoc =
892 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
893 "'random' is not a secure random number generator",
895 "The 'random' function produces a sequence of values that " 896 "an adversary may be able to predict. Use 'arc4random' " 906 if (!filter.check_vfork)
910 PathDiagnosticLocation CELoc =
912 BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
913 "Potential insecure implementation-specific behavior in " 916 "Call to function 'vfork' is insecure as it can lead to " 917 "denial of service situations in the parent process. " 918 "Replace calls to vfork with calls to the safer " 919 "'posix_spawn' function",
928 void WalkAST::checkUncheckedReturnValue(
CallExpr *CE) {
929 if (!filter.check_UncheckedReturn)
936 if (II_setid[0] ==
nullptr) {
937 static const char *
const identifiers[num_setids] = {
938 "setuid",
"setgid",
"seteuid",
"setegid",
939 "setreuid",
"setregid" 942 for (
size_t i = 0;
i < num_setids;
i++)
943 II_setid[
i] = &BR.getContext().Idents.get(identifiers[
i]);
949 for (identifierid = 0; identifierid < num_setids; identifierid++)
950 if (
id == II_setid[identifierid])
953 if (identifierid >= num_setids)
972 llvm::raw_svector_ostream os1(buf1);
973 os1 <<
"Return value is not checked in call to '" << *FD <<
'\'';
976 llvm::raw_svector_ostream os2(buf2);
977 os2 <<
"The return value from the call to '" << *FD
978 <<
"' is not checked. If an error occurs in '" << *FD
979 <<
"', the following code may execute with unexpected privileges";
981 PathDiagnosticLocation CELoc =
983 BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
984 "Security", os2.str(), CELoc,
993 class SecuritySyntaxChecker :
public Checker<check::ASTCodeBody> {
997 void checkASTCodeBody(
const Decl *D, AnalysisManager& mgr,
998 BugReporter &BR)
const {
999 WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
1005 void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
1006 mgr.registerChecker<SecuritySyntaxChecker>();
1009 bool ento::shouldRegisterSecuritySyntaxChecker(
const LangOptions &LO) {
1013 #define REGISTER_CHECKER(name) \ 1014 void ento::register##name(CheckerManager &mgr) { \ 1015 SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ 1016 checker->filter.check_##name = true; \ 1017 checker->filter.checkName_##name = mgr.getCurrentCheckName(); \ 1020 bool ento::shouldRegister##name(const LangOptions &LO) { \
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.
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.
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.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
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
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Expr * IgnoreImpCasts() LLVM_READONLY
Skip past any implicit casts which might surround this expression until reaching a fixed point...
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
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Expr * IgnoreParenLValueCasts() LLVM_READONLY
Skip past any parentheses and lvalue casts which might surround this expression until reaching a fixe...
QualType getParamType(unsigned i) const
bool EvaluateAsInt(EvalResult &Result, const ASTContext &Ctx, SideEffectsKind AllowSideEffects=SE_NoSideEffects, bool InConstantContext=false) const
EvaluateAsInt - Return true if this is a constant which we can fold and convert to an integer...
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.