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 ||
30 T.getOS() == llvm::Triple::CloudABI ||
39 bool check_bcmp =
false;
40 bool check_bcopy =
false;
41 bool check_bzero =
false;
42 bool check_gets =
false;
43 bool check_getpw =
false;
44 bool check_mktemp =
false;
45 bool check_mkstemp =
false;
46 bool check_strcpy =
false;
47 bool check_DeprecatedOrUnsafeBufferHandling =
false;
48 bool check_rand =
false;
49 bool check_vfork =
false;
50 bool check_FloatLoopCounter =
false;
51 bool check_UncheckedReturn =
false;
52 bool check_decodeValueOfObjCType =
false;
73 enum { num_setids = 6 };
77 const ChecksFilter &filter;
81 const ChecksFilter &f)
82 : BR(br), AC(ac), II_setid(),
91 void VisitStmt(
Stmt *S) { VisitChildren(S); }
93 void VisitChildren(
Stmt *S);
102 void checkLoopConditionForFloat(
const ForStmt *FS);
112 void checkDeprecatedOrUnsafeBufferHandling(
const CallExpr *CE,
118 void checkUncheckedReturnValue(
CallExpr *CE);
126void WalkAST::VisitChildren(
Stmt *S) {
127 for (
Stmt *Child : S->children())
132void WalkAST::VisitCallExpr(
CallExpr *CE) {
143 StringRef Name = II->
getName();
144 if (Name.startswith(
"__builtin_"))
145 Name = Name.substr(10);
148 FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
149 .Case(
"bcmp", &WalkAST::checkCall_bcmp)
150 .Case(
"bcopy", &WalkAST::checkCall_bcopy)
151 .Case(
"bzero", &WalkAST::checkCall_bzero)
152 .Case(
"gets", &WalkAST::checkCall_gets)
153 .Case(
"getpw", &WalkAST::checkCall_getpw)
154 .Case(
"mktemp", &WalkAST::checkCall_mktemp)
155 .Case(
"mkstemp", &WalkAST::checkCall_mkstemp)
156 .Case(
"mkdtemp", &WalkAST::checkCall_mkstemp)
157 .Case(
"mkstemps", &WalkAST::checkCall_mkstemp)
158 .Cases(
"strcpy",
"__strcpy_chk", &WalkAST::checkCall_strcpy)
159 .Cases(
"strcat",
"__strcat_chk", &WalkAST::checkCall_strcat)
160 .Cases(
"sprintf",
"vsprintf",
"scanf",
"wscanf",
"fscanf",
"fwscanf",
161 "vscanf",
"vwscanf",
"vfscanf",
"vfwscanf",
162 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
163 .Cases(
"sscanf",
"swscanf",
"vsscanf",
"vswscanf",
"swprintf",
164 "snprintf",
"vswprintf",
"vsnprintf",
"memcpy",
"memmove",
165 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
166 .Cases(
"strncpy",
"strncat",
"memset",
167 &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
168 .Case(
"drand48", &WalkAST::checkCall_rand)
169 .Case(
"erand48", &WalkAST::checkCall_rand)
170 .Case(
"jrand48", &WalkAST::checkCall_rand)
171 .Case(
"lrand48", &WalkAST::checkCall_rand)
172 .Case(
"mrand48", &WalkAST::checkCall_rand)
173 .Case(
"nrand48", &WalkAST::checkCall_rand)
174 .Case(
"lcong48", &WalkAST::checkCall_rand)
175 .Case(
"rand", &WalkAST::checkCall_rand)
176 .Case(
"rand_r", &WalkAST::checkCall_rand)
177 .Case(
"random", &WalkAST::checkCall_random)
178 .Case(
"vfork", &WalkAST::checkCall_vfork)
184 (this->*evalFunction)(CE, FD);
191 MsgCheck evalFunction =
193 .Case(
"decodeValueOfObjCType:at:",
194 &WalkAST::checkMsg_decodeValueOfObjCType)
198 (this->*evalFunction)(ME);
205 for (
Stmt *Child : S->children())
207 if (
CallExpr *CE = dyn_cast<CallExpr>(Child))
208 checkUncheckedReturnValue(CE);
213void WalkAST::VisitForStmt(
ForStmt *FS) {
214 checkLoopConditionForFloat(FS);
233 if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
234 B->getOpcode() == BO_Comma))
248 return ND == x || ND == y ? DR :
nullptr;
252 return U->isIncrementDecrementOp()
262void WalkAST::checkLoopConditionForFloat(
const ForStmt *FS) {
263 if (!filter.check_FloatLoopCounter)
267 const Expr *condition = FS->getCond();
273 const Expr *increment = FS->getInc();
302 if (!drLHS && !drRHS)
305 const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->
getDecl()) : nullptr;
306 const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->
getDecl()) : nullptr;
308 if (!vdLHS && !vdRHS)
317 assert(vdInc && (vdInc == vdLHS || vdInc == vdRHS));
321 const DeclRefExpr *drCond = vdLHS == vdInc ? drLHS : drRHS;
325 llvm::raw_svector_ostream os(sbuf);
328 <<
"' with floating point type '" << drCond->
getType()
329 <<
"' should not be used as a loop counter";
334 const char *bugType =
"Floating point variable used as loop counter";
338 BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
339 bugType,
"Security", os.str(),
350 if (!filter.check_bcmp)
361 for (
int i = 0; i < 2; i++) {
378 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp,
379 "Use of deprecated function in call to 'bcmp()'",
381 "The bcmp() function is obsoleted by memcmp().",
392 if (!filter.check_bcopy)
403 for (
int i = 0; i < 2; i++) {
420 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy,
421 "Use of deprecated function in call to 'bcopy()'",
423 "The bcopy() function is obsoleted by memcpy() "
435 if (!filter.check_bzero)
461 BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero,
462 "Use of deprecated function in call to 'bzero()'",
464 "The bzero() function is obsoleted by memset().",
477 if (!filter.check_gets)
499 BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
500 "Potential buffer overflow in call to 'gets'",
502 "Call to function 'gets' is extremely insecure as it can "
503 "always result in a buffer overflow",
513 if (!filter.check_getpw)
539 BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
540 "Potential buffer overflow in call to 'getpw'",
542 "The getpw() function is dangerous as it may overflow the "
543 "provided buffer. It is obsoleted by getpwuid().",
553 if (!filter.check_mktemp) {
556 checkCall_mkstemp(CE, FD);
580 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
581 "Potential insecure temporary file in call 'mktemp'",
583 "Call to function 'mktemp' is insecure as it always "
584 "creates or uses insecure temporary file. Use 'mkstemp' "
594 if (!filter.check_mkstemp)
598 std::pair<signed, signed> ArgSuffix =
599 llvm::StringSwitch<std::pair<signed, signed> >(Name)
600 .Case(
"mktemp", std::make_pair(0,-1))
601 .Case(
"mkstemp", std::make_pair(0,-1))
602 .Case(
"mkdtemp", std::make_pair(0,-1))
603 .Case(
"mkstemps", std::make_pair(0,1))
604 .
Default(std::make_pair(-1, -1));
606 assert(ArgSuffix.first >= 0 &&
"Unsupported function");
610 if ((
signed) numArgs <= ArgSuffix.first)
614 dyn_cast<StringLiteral>(CE->
getArg((
unsigned)ArgSuffix.first)
626 unsigned n = str.size();
630 if (ArgSuffix.second >= 0) {
631 const Expr *suffixEx = CE->
getArg((
unsigned)ArgSuffix.second);
635 llvm::APSInt Result = EVResult.
Val.
getInt();
637 if (Result.isNegative())
639 suffix = (
unsigned) Result.getZExtValue();
640 n = (n > suffix) ? n - suffix : 0;
643 for (
unsigned i = 0; i < n; ++i)
644 if (str[i] ==
'X') ++numX;
653 llvm::raw_svector_ostream out(buf);
654 out <<
"Call to '" << Name <<
"' should have at least 6 'X's in the"
655 " format string to be secure (" << numX <<
" 'X'";
660 out <<
", " << suffix <<
" character";
663 out <<
" used as a suffix";
666 BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
667 "Insecure temporary file creation",
"Security",
679 if (!filter.check_strcpy)
682 if (!checkCall_strCommon(CE, FD))
688 if (
const auto *Array = dyn_cast<ConstantArrayType>(
Target->getType())) {
689 uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;
690 if (
const auto *String = dyn_cast<StringLiteral>(Source)) {
691 if (ArraySize >= String->getLength() + 1)
699 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
700 "Potential insecure memory buffer bounds restriction in "
703 "Call to function 'strcpy' is insecure as it does not "
704 "provide bounding of the memory buffer. Replace "
705 "unbounded copy functions with analogous functions that "
706 "support length arguments such as 'strlcpy'. CWE-119.",
718 if (!filter.check_strcpy)
721 if (!checkCall_strCommon(CE, FD))
727 BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
728 "Potential insecure memory buffer bounds restriction in "
731 "Call to function 'strcat' is insecure as it does not "
732 "provide bounding of the memory buffer. Replace "
733 "unbounded copy functions with analogous functions that "
734 "support length arguments such as 'strlcat'. CWE-119.",
754void WalkAST::checkDeprecatedOrUnsafeBufferHandling(
const CallExpr *CE,
756 if (!filter.check_DeprecatedOrUnsafeBufferHandling)
759 if (!BR.getContext().getLangOpts().C11)
764 enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 };
767 if (Name.startswith(
"__builtin_"))
768 Name = Name.substr(10);
771 llvm::StringSwitch<int>(Name)
772 .Cases(
"scanf",
"wscanf",
"vscanf",
"vwscanf", 0)
773 .Cases(
"sprintf",
"vsprintf",
"fscanf",
"fwscanf",
"vfscanf",
774 "vfwscanf",
"sscanf",
"swscanf",
"vsscanf",
"vswscanf", 1)
775 .Cases(
"swprintf",
"snprintf",
"vswprintf",
"vsnprintf",
"memcpy",
776 "memmove",
"memset",
"strncpy",
"strncat", DEPR_ONLY)
777 .Default(UNKNOWN_CALL);
779 assert(ArgIndex != UNKNOWN_CALL &&
"Unsupported function");
780 bool BoundsProvided = ArgIndex == DEPR_ONLY;
782 if (!BoundsProvided) {
788 if (FormatString && !FormatString->getString().contains(
"%s") &&
789 !FormatString->getString().contains(
"%["))
790 BoundsProvided =
true;
795 llvm::raw_svector_ostream Out1(Buf1);
796 llvm::raw_svector_ostream Out2(Buf2);
798 Out1 <<
"Potential insecure memory buffer bounds restriction in call '"
800 Out2 <<
"Call to function '" << Name
801 <<
"' is insecure as it does not provide ";
803 if (!BoundsProvided) {
804 Out2 <<
"bounding of the memory buffer or ";
807 Out2 <<
"security checks introduced "
808 "in the C11 standard. Replace with analogous functions that "
809 "support length arguments or provides boundary checks such as '"
810 << Name <<
"_s' in case of C11";
814 BR.EmitBasicReport(AC->getDecl(),
815 filter.checkName_DeprecatedOrUnsafeBufferHandling,
816 Out1.str(),
"Security", Out2.str(), CELoc,
831 if (numArgs != 2 && numArgs != 3)
835 for (
int i = 0; i < 2; i++) {
856 if (!filter.check_rand || !CheckRand)
877 llvm::raw_svector_ostream os1(buf1);
878 os1 <<
'\'' << *FD <<
"' is a poor random number generator";
881 llvm::raw_svector_ostream os2(buf2);
882 os2 <<
"Function '" << *FD
883 <<
"' is obsolete because it implements a poor random number generator."
884 <<
" Use 'arc4random' instead";
888 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
889 "Security", os2.str(), CELoc,
899 if (!CheckRand || !filter.check_rand)
913 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
914 "'random' is not a secure random number generator",
916 "The 'random' function produces a sequence of values that "
917 "an adversary may be able to predict. Use 'arc4random' "
927 if (!filter.check_vfork)
933 BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
934 "Potential insecure implementation-specific behavior in "
937 "Call to function 'vfork' is insecure as it can lead to "
938 "denial of service situations in the parent process. "
939 "Replace calls to vfork with calls to the safer "
940 "'posix_spawn' function",
950void WalkAST::checkMsg_decodeValueOfObjCType(
const ObjCMessageExpr *ME) {
951 if (!filter.check_decodeValueOfObjCType)
957 const TargetInfo &TI = AC->getASTContext().getTargetInfo();
961 case llvm::Triple::IOS:
962 if (VT < VersionTuple(11, 0))
965 case llvm::Triple::MacOSX:
966 if (VT < VersionTuple(10, 13))
969 case llvm::Triple::WatchOS:
970 if (VT < VersionTuple(4, 0))
973 case llvm::Triple::TvOS:
974 if (VT < VersionTuple(11, 0))
984 AC->getDecl(), filter.checkName_decodeValueOfObjCType,
985 "Potential buffer overflow in '-decodeValueOfObjCType:at:'",
"Security",
986 "Deprecated method '-decodeValueOfObjCType:at:' is insecure "
987 "as it can lead to potential buffer overflows. Use the safer "
988 "'-decodeValueOfObjCType:at:size:' method.",
997void WalkAST::checkUncheckedReturnValue(
CallExpr *CE) {
998 if (!filter.check_UncheckedReturn)
1005 if (II_setid[0] ==
nullptr) {
1006 static const char *
const identifiers[num_setids] = {
1007 "setuid",
"setgid",
"seteuid",
"setegid",
1008 "setreuid",
"setregid"
1011 for (
size_t i = 0; i < num_setids; i++)
1012 II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
1016 size_t identifierid;
1018 for (identifierid = 0; identifierid < num_setids; identifierid++)
1019 if (
id == II_setid[identifierid])
1022 if (identifierid >= num_setids)
1041 llvm::raw_svector_ostream os1(buf1);
1042 os1 <<
"Return value is not checked in call to '" << *FD <<
'\'';
1045 llvm::raw_svector_ostream os2(buf2);
1046 os2 <<
"The return value from the call to '" << *FD
1047 <<
"' is not checked. If an error occurs in '" << *FD
1048 <<
"', the following code may execute with unexpected privileges";
1052 BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
1053 "Security", os2.str(), CELoc,
1062class SecuritySyntaxChecker :
public Checker<check::ASTCodeBody> {
1064 ChecksFilter filter;
1078bool ento::shouldRegisterSecuritySyntaxChecker(
const CheckerManager &mgr) {
1082#define REGISTER_CHECKER(name) \
1083 void ento::register##name(CheckerManager &mgr) { \
1084 SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \
1085 checker->filter.check_##name = true; \
1086 checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \
1089 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)
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.
EvalResult is a struct with detailed info about an evaluated expression.
APValue Val
Val - This is the value the expression can be folded to.