20#include "llvm/ADT/SmallString.h"
21#include "llvm/ADT/StringSwitch.h"
22#include "llvm/Support/raw_ostream.h"
29 return T.getVendor() == llvm::Triple::Apple ||
38 bool check_bcmp =
false;
39 bool check_bcopy =
false;
40 bool check_bzero =
false;
41 bool check_gets =
false;
42 bool check_getpw =
false;
43 bool check_mktemp =
false;
44 bool check_mkstemp =
false;
45 bool check_strcpy =
false;
46 bool check_DeprecatedOrUnsafeBufferHandling =
false;
47 bool check_rand =
false;
48 bool check_vfork =
false;
49 bool check_FloatLoopCounter =
false;
50 bool check_UncheckedReturn =
false;
51 bool check_decodeValueOfObjCType =
false;
72 enum { num_setids = 6 };
76 const ChecksFilter &filter;
80 const ChecksFilter &f)
81 : BR(br), AC(ac), II_setid(),
90 void VisitStmt(
Stmt *S) { VisitChildren(S); }
92 void VisitChildren(
Stmt *S);
101 void checkLoopConditionForFloat(
const ForStmt *FS);
111 void checkDeprecatedOrUnsafeBufferHandling(
const CallExpr *CE,
117 void checkUncheckedReturnValue(
CallExpr *CE);
125void WalkAST::VisitChildren(
Stmt *S) {
126 for (
Stmt *Child : S->children())
131void WalkAST::VisitCallExpr(
CallExpr *CE) {
142 StringRef Name = II->
getName();
143 Name.consume_front(
"__builtin_");
146 FnCheck evalFunction =
147 llvm::StringSwitch<FnCheck>(Name)
148 .Case(
"bcmp", &WalkAST::checkCall_bcmp)
149 .Case(
"bcopy", &WalkAST::checkCall_bcopy)
150 .Case(
"bzero", &WalkAST::checkCall_bzero)
151 .Case(
"gets", &WalkAST::checkCall_gets)
152 .Case(
"getpw", &WalkAST::checkCall_getpw)
153 .Case(
"mktemp", &WalkAST::checkCall_mktemp)
154 .Case(
"mkstemp", &WalkAST::checkCall_mkstemp)
155 .Case(
"mkdtemp", &WalkAST::checkCall_mkstemp)
156 .Case(
"mkstemps", &WalkAST::checkCall_mkstemp)
157 .Cases(
"strcpy",
"__strcpy_chk", &WalkAST::checkCall_strcpy)
158 .Cases(
"strcat",
"__strcat_chk", &WalkAST::checkCall_strcat)
159 .Cases(
"sprintf",
"vsprintf",
"scanf",
"wscanf",
"fscanf",
"fwscanf",
160 "vscanf",
"vwscanf",
"vfscanf",
"vfwscanf",
161 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
162 .Cases(
"sscanf",
"swscanf",
"vsscanf",
"vswscanf",
"swprintf",
163 "snprintf",
"vswprintf",
"vsnprintf",
"memcpy",
"memmove",
164 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
165 .Cases(
"strncpy",
"strncat",
"memset",
"fprintf",
166 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
167 .Case(
"drand48", &WalkAST::checkCall_rand)
168 .Case(
"erand48", &WalkAST::checkCall_rand)
169 .Case(
"jrand48", &WalkAST::checkCall_rand)
170 .Case(
"lrand48", &WalkAST::checkCall_rand)
171 .Case(
"mrand48", &WalkAST::checkCall_rand)
172 .Case(
"nrand48", &WalkAST::checkCall_rand)
173 .Case(
"lcong48", &WalkAST::checkCall_rand)
174 .Case(
"rand", &WalkAST::checkCall_rand)
175 .Case(
"rand_r", &WalkAST::checkCall_rand)
176 .Case(
"random", &WalkAST::checkCall_random)
177 .Case(
"vfork", &WalkAST::checkCall_vfork)
183 (this->*evalFunction)(CE, FD);
190 MsgCheck evalFunction =
192 .Case(
"decodeValueOfObjCType:at:",
193 &WalkAST::checkMsg_decodeValueOfObjCType)
197 (this->*evalFunction)(ME);
204 for (
Stmt *Child : S->children())
206 if (
CallExpr *CE = dyn_cast<CallExpr>(Child))
207 checkUncheckedReturnValue(CE);
212void WalkAST::VisitForStmt(
ForStmt *FS) {
213 checkLoopConditionForFloat(FS);
231 if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
232 B->getOpcode() == BO_Comma))
246 return ND == x || ND == y ? DR :
nullptr;
250 return U->isIncrementDecrementOp()
260void WalkAST::checkLoopConditionForFloat(
const ForStmt *FS) {
261 if (!filter.check_FloatLoopCounter)
265 const Expr *condition = FS->getCond();
271 const Expr *increment = FS->getInc();
300 if (!drLHS && !drRHS)
303 const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->
getDecl()) : nullptr;
304 const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->
getDecl()) : nullptr;
306 if (!vdLHS && !vdRHS)
315 assert(vdInc && (vdInc == vdLHS || vdInc == vdRHS));
319 const DeclRefExpr *drCond = vdLHS == vdInc ? drLHS : drRHS;
323 llvm::raw_svector_ostream os(sbuf);
326 <<
"' with floating point type '" << drCond->
getType()
327 <<
"' should not be used as a loop counter";
332 const char *bugType =
"Floating point variable used as loop counter";
336 BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
337 bugType,
"Security", os.str(),
348 if (!filter.check_bcmp)
359 for (
int i = 0; i < 2; i++) {
376 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp,
377 "Use of deprecated function in call to 'bcmp()'",
379 "The bcmp() function is obsoleted by memcmp().",
390 if (!filter.check_bcopy)
401 for (
int i = 0; i < 2; i++) {
418 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy,
419 "Use of deprecated function in call to 'bcopy()'",
421 "The bcopy() function is obsoleted by memcpy() "
433 if (!filter.check_bzero)
459 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero,
460 "Use of deprecated function in call to 'bzero()'",
462 "The bzero() function is obsoleted by memset().",
475 if (!filter.check_gets)
497 BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
498 "Potential buffer overflow in call to 'gets'",
500 "Call to function 'gets' is extremely insecure as it can "
501 "always result in a buffer overflow",
511 if (!filter.check_getpw)
537 BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
538 "Potential buffer overflow in call to 'getpw'",
540 "The getpw() function is dangerous as it may overflow the "
541 "provided buffer. It is obsoleted by getpwuid().",
551 if (!filter.check_mktemp) {
554 checkCall_mkstemp(CE, FD);
578 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
579 "Potential insecure temporary file in call 'mktemp'",
581 "Call to function 'mktemp' is insecure as it always "
582 "creates or uses insecure temporary file. Use 'mkstemp' "
592 if (!filter.check_mkstemp)
596 std::pair<signed, signed> ArgSuffix =
597 llvm::StringSwitch<std::pair<signed, signed> >(Name)
598 .Case(
"mktemp", std::make_pair(0,-1))
599 .Case(
"mkstemp", std::make_pair(0,-1))
600 .Case(
"mkdtemp", std::make_pair(0,-1))
601 .Case(
"mkstemps", std::make_pair(0,1))
602 .
Default(std::make_pair(-1, -1));
604 assert(ArgSuffix.first >= 0 &&
"Unsupported function");
608 if ((
signed) numArgs <= ArgSuffix.first)
612 dyn_cast<StringLiteral>(CE->
getArg((
unsigned)ArgSuffix.first)
624 unsigned n = str.size();
628 if (ArgSuffix.second >= 0) {
629 const Expr *suffixEx = CE->
getArg((
unsigned)ArgSuffix.second);
633 llvm::APSInt Result = EVResult.
Val.
getInt();
635 if (Result.isNegative())
637 suffix = (
unsigned) Result.getZExtValue();
638 n = (n > suffix) ? n - suffix : 0;
641 for (
unsigned i = 0; i < n; ++i)
642 if (str[i] ==
'X') ++numX;
651 llvm::raw_svector_ostream out(buf);
652 out <<
"Call to '" << Name <<
"' should have at least 6 'X's in the"
653 " format string to be secure (" << numX <<
" 'X'";
658 out <<
", " << suffix <<
" character";
661 out <<
" used as a suffix";
664 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
665 "Insecure temporary file creation",
"Security",
677 if (!filter.check_strcpy)
680 if (!checkCall_strCommon(CE, FD))
686 if (
const auto *Array = dyn_cast<ConstantArrayType>(
Target->getType())) {
687 uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;
688 if (
const auto *String = dyn_cast<StringLiteral>(Source)) {
689 if (ArraySize >= String->getLength() + 1)
697 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
698 "Potential insecure memory buffer bounds restriction in "
701 "Call to function 'strcpy' is insecure as it does not "
702 "provide bounding of the memory buffer. Replace "
703 "unbounded copy functions with analogous functions that "
704 "support length arguments such as 'strlcpy'. CWE-119.",
716 if (!filter.check_strcpy)
719 if (!checkCall_strCommon(CE, FD))
725 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
726 "Potential insecure memory buffer bounds restriction in "
729 "Call to function 'strcat' is insecure as it does not "
730 "provide bounding of the memory buffer. Replace "
731 "unbounded copy functions with analogous functions that "
732 "support length arguments such as 'strlcat'. CWE-119.",
752void WalkAST::checkDeprecatedOrUnsafeBufferHandling(
const CallExpr *CE,
754 if (!filter.check_DeprecatedOrUnsafeBufferHandling)
757 if (!BR.getContext().getLangOpts().C11)
762 enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 };
765 Name.consume_front(
"__builtin_");
768 llvm::StringSwitch<int>(Name)
769 .Cases(
"scanf",
"wscanf",
"vscanf",
"vwscanf", 0)
770 .Cases(
"fscanf",
"fwscanf",
"vfscanf",
"vfwscanf",
"sscanf",
771 "swscanf",
"vsscanf",
"vswscanf", 1)
772 .Cases(
"sprintf",
"vsprintf",
"fprintf", 1)
773 .Cases(
"swprintf",
"snprintf",
"vswprintf",
"vsnprintf",
"memcpy",
774 "memmove",
"memset",
"strncpy",
"strncat", DEPR_ONLY)
775 .Default(UNKNOWN_CALL);
777 assert(ArgIndex != UNKNOWN_CALL &&
"Unsupported function");
778 bool BoundsProvided = ArgIndex == DEPR_ONLY;
780 if (!BoundsProvided) {
786 if (FormatString && !FormatString->getString().contains(
"%s") &&
787 !FormatString->getString().contains(
"%["))
788 BoundsProvided =
true;
793 llvm::raw_svector_ostream Out1(Buf1);
794 llvm::raw_svector_ostream Out2(Buf2);
796 Out1 <<
"Potential insecure memory buffer bounds restriction in call '"
798 Out2 <<
"Call to function '" << Name
799 <<
"' is insecure as it does not provide ";
801 if (!BoundsProvided) {
802 Out2 <<
"bounding of the memory buffer or ";
805 Out2 <<
"security checks introduced "
806 "in the C11 standard. Replace with analogous functions that "
807 "support length arguments or provides boundary checks such as '"
808 << Name <<
"_s' in case of C11";
812 BR.EmitBasicReport(AC->getDecl(),
813 filter.checkName_DeprecatedOrUnsafeBufferHandling,
814 Out1.str(),
"Security", Out2.str(), CELoc,
829 if (numArgs != 2 && numArgs != 3)
833 for (
int i = 0; i < 2; i++) {
859 if (!filter.check_rand || !CheckRand)
880 llvm::raw_svector_ostream os1(buf1);
881 os1 <<
'\'' << *FD <<
"' is a poor random number generator";
884 llvm::raw_svector_ostream os2(buf2);
885 os2 <<
"Function '" << *FD
886 <<
"' is obsolete because it implements a poor random number generator."
887 <<
" Use 'arc4random' instead";
891 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
892 "Security", os2.str(), CELoc,
898 if (!CheckRand || !filter.check_rand)
912 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
913 "'random' is not a secure random number generator",
915 "The 'random' function produces a sequence of values that "
916 "an adversary may be able to predict. Use 'arc4random' "
926 if (!filter.check_vfork)
932 BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
933 "Potential insecure implementation-specific behavior in "
936 "Call to function 'vfork' is insecure as it can lead to "
937 "denial of service situations in the parent process. "
938 "Replace calls to vfork with calls to the safer "
939 "'posix_spawn' function",
949void WalkAST::checkMsg_decodeValueOfObjCType(
const ObjCMessageExpr *ME) {
950 if (!filter.check_decodeValueOfObjCType)
956 const TargetInfo &TI = AC->getASTContext().getTargetInfo();
960 case llvm::Triple::IOS:
961 if (VT < VersionTuple(11, 0))
964 case llvm::Triple::MacOSX:
965 if (VT < VersionTuple(10, 13))
968 case llvm::Triple::WatchOS:
969 if (VT < VersionTuple(4, 0))
972 case llvm::Triple::TvOS:
973 if (VT < VersionTuple(11, 0))
976 case llvm::Triple::XROS:
985 AC->getDecl(), filter.checkName_decodeValueOfObjCType,
986 "Potential buffer overflow in '-decodeValueOfObjCType:at:'",
"Security",
987 "Deprecated method '-decodeValueOfObjCType:at:' is insecure "
988 "as it can lead to potential buffer overflows. Use the safer "
989 "'-decodeValueOfObjCType:at:size:' method.",
1008void WalkAST::checkUncheckedReturnValue(
CallExpr *CE) {
1009 if (!filter.check_UncheckedReturn)
1016 if (II_setid[0] ==
nullptr) {
1017 static const char *
const identifiers[num_setids] = {
1018 "setuid",
"setgid",
"seteuid",
"setegid",
1019 "setreuid",
"setregid"
1022 for (
size_t i = 0; i < num_setids; i++)
1023 II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
1027 size_t identifierid;
1029 for (identifierid = 0; identifierid < num_setids; identifierid++)
1030 if (
id == II_setid[identifierid])
1033 if (identifierid >= num_setids)
1052 llvm::raw_svector_ostream os1(buf1);
1053 os1 <<
"Return value is not checked in call to '" << *FD <<
'\'';
1056 llvm::raw_svector_ostream os2(buf2);
1057 os2 <<
"The return value from the call to '" << *FD
1058 <<
"' is not checked. If an error occurs in '" << *FD
1059 <<
"', the following code may execute with unexpected privileges";
1063 BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
1064 "Security", os2.str(), CELoc,
1073class SecuritySyntaxChecker :
public Checker<check::ASTCodeBody> {
1075 ChecksFilter filter;
1089bool ento::shouldRegisterSecuritySyntaxChecker(
const CheckerManager &mgr) {
1093#define REGISTER_CHECKER(name) \
1094 void ento::register##name(CheckerManager &mgr) { \
1095 SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \
1096 checker->filter.check_##name = true; \
1097 checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \
1100 bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
#define REGISTER_CHECKER(name)
static const DeclRefExpr * getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y)
static bool isArc4RandomAvailable(const ASTContext &Ctx)
llvm::MachO::Target Target
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
const TargetInfo & getTargetInfo() const
AnalysisDeclContext contains the context data for the function, method or block under analysis.
A builtin binary operation expression such as "x + y" or "x <= y".
static bool isRelationalOp(Opcode Opc)
static bool isEqualityOp(Opcode Opc)
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
CompoundStmt - This represents a group of statements like { stmt stmt }.
A reference to a declared variable, function, enum, etc.
Decl - This represents one declaration (or definition), e.g.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
This represents one expression.
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,...
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Expr * IgnoreParenLValueCasts() LLVM_READONLY
Skip past any parentheses and lvalue casts which might surround this expression until reaching a fixe...
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Expr * IgnoreImpCasts() LLVM_READONLY
Skip past any implicit casts which might surround this expression until reaching a fixed point.
ForStmt - This represents a 'for (init;cond;inc)' stmt.
Represents a function declaration or definition.
Represents a prototype with parameter type info, e.g.
unsigned getNumParams() const
QualType getParamType(unsigned i) const
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Return the actual identifier string.
This represents a decl that may have a name.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
An expression that sends a message to the given Objective-C object or class.
Selector getSelector() const
PointerType - C99 6.7.5.1 - Pointer Declarators.
QualType getPointeeType() const
QualType getUnqualifiedType() const
Retrieve the unqualified variant of the given type, removing as little sugar as possible.
std::string getAsString() const
Derive the full selector name (e.g.
StmtVisitor - This class implements a simple visitor for Stmt subclasses.
Stmt - This represents one statement.
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.
StringRef getString() const
unsigned getCharByteWidth() const
Exposes information about the current target.
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
VersionTuple getPlatformMinVersion() const
Retrieve the minimum desired version of the platform, to which the program should be compiled.
bool isIntegralOrUnscopedEnumerationType() const
Determine whether this type is an integral or unscoped enumeration type.
bool isRealFloatingType() const
Floating point categories.
const T * getAs() const
Member-template getAs<specific type>'.
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Represents a variable declaration or definition.
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
BugReporter is a utility class for generating PathDiagnostics for analysis.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
This wrapper is used to ensure that only StringRefs originating from the CheckerRegistry are used as ...
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
Defines the clang::TargetInfo interface.
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
The JSON file list parser is used to communicate input to InstallAPI.
const FunctionProtoType * T
EvalResult is a struct with detailed info about an evaluated expression.
APValue Val
Val - This is the value the expression can be folded to.