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;
52 DefaultBool check_decodeValueOfObjCType;
54 CheckerNameRef checkName_bcmp;
55 CheckerNameRef checkName_bcopy;
56 CheckerNameRef checkName_bzero;
57 CheckerNameRef checkName_gets;
58 CheckerNameRef checkName_getpw;
59 CheckerNameRef checkName_mktemp;
60 CheckerNameRef checkName_mkstemp;
61 CheckerNameRef checkName_strcpy;
62 CheckerNameRef checkName_DeprecatedOrUnsafeBufferHandling;
63 CheckerNameRef checkName_rand;
64 CheckerNameRef checkName_vfork;
65 CheckerNameRef checkName_FloatLoopCounter;
66 CheckerNameRef checkName_UncheckedReturn;
67 CheckerNameRef checkName_decodeValueOfObjCType;
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);
103 void checkCall_bcmp(
const CallExpr *CE,
const FunctionDecl *FD);
104 void checkCall_bcopy(
const CallExpr *CE,
const FunctionDecl *FD);
105 void checkCall_bzero(
const CallExpr *CE,
const FunctionDecl *FD);
106 void checkCall_gets(
const CallExpr *CE,
const FunctionDecl *FD);
107 void checkCall_getpw(
const CallExpr *CE,
const FunctionDecl *FD);
108 void checkCall_mktemp(
const CallExpr *CE,
const FunctionDecl *FD);
109 void checkCall_mkstemp(
const CallExpr *CE,
const FunctionDecl *FD);
110 void checkCall_strcpy(
const CallExpr *CE,
const FunctionDecl *FD);
111 void checkCall_strcat(
const CallExpr *CE,
const FunctionDecl *FD);
112 void checkDeprecatedOrUnsafeBufferHandling(
const CallExpr *CE,
114 void checkCall_rand(
const CallExpr *CE,
const FunctionDecl *FD);
115 void checkCall_random(
const CallExpr *CE,
const FunctionDecl *FD);
116 void checkCall_vfork(
const CallExpr *CE,
const FunctionDecl *FD);
118 void checkUncheckedReturnValue(CallExpr *CE);
126 void WalkAST::VisitChildren(
Stmt *S) {
132 void 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);
207 if (
CallExpr *CE = dyn_cast<CallExpr>(Child))
208 checkUncheckedReturnValue(CE);
213 void WalkAST::VisitForStmt(
ForStmt *FS) {
214 checkLoopConditionForFloat(FS);
233 if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
234 B->getOpcode() == BO_Comma))
246 if (
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
248 return ND == x || ND == y ? DR :
nullptr;
252 return U->isIncrementDecrementOp()
262 void WalkAST::checkLoopConditionForFloat(
const ForStmt *FS) {
263 if (!filter.check_FloatLoopCounter)
300 drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS :
nullptr;
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);
329 <<
"' should not be used as a loop counter";
334 const char *bugType =
"Floating point variable used as loop counter";
336 PathDiagnosticLocation FSLoc =
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++) {
376 PathDiagnosticLocation CELoc =
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++) {
418 PathDiagnosticLocation CELoc =
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)
459 PathDiagnosticLocation CELoc =
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)
497 PathDiagnosticLocation CELoc =
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)
537 PathDiagnosticLocation CELoc =
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);
578 PathDiagnosticLocation CELoc =
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)
626 unsigned n = str.size();
630 if (ArgSuffix.second >= 0) {
631 const Expr *suffixEx = CE->
getArg((
unsigned)ArgSuffix.second);
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;
650 PathDiagnosticLocation CELoc =
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)
697 PathDiagnosticLocation CELoc =
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))
725 PathDiagnosticLocation CELoc =
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.",
754 void 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) {
789 FormatString->getString().find(
"%s") == StringRef::npos &&
790 FormatString->getString().find(
"%[") == StringRef::npos)
791 BoundsProvided =
true;
796 llvm::raw_svector_ostream Out1(Buf1);
797 llvm::raw_svector_ostream Out2(Buf2);
799 Out1 <<
"Potential insecure memory buffer bounds restriction in call '" 801 Out2 <<
"Call to function '" << Name
802 <<
"' is insecure as it does not provide ";
804 if (!BoundsProvided) {
805 Out2 <<
"bounding of the memory buffer or ";
808 Out2 <<
"security checks introduced " 809 "in the C11 standard. Replace with analogous functions that " 810 "support length arguments or provides boundary checks such as '" 811 << Name <<
"_s' in case of C11";
813 PathDiagnosticLocation CELoc =
815 BR.EmitBasicReport(AC->getDecl(),
816 filter.checkName_DeprecatedOrUnsafeBufferHandling,
817 Out1.str(),
"Security", Out2.str(), CELoc,
832 if (numArgs != 2 && numArgs != 3)
836 for (
int i = 0; i < 2; i++) {
857 if (!filter.check_rand || !CheckRand)
878 llvm::raw_svector_ostream os1(buf1);
879 os1 <<
'\'' << *FD <<
"' is a poor random number generator";
882 llvm::raw_svector_ostream os2(buf2);
883 os2 <<
"Function '" << *FD
884 <<
"' is obsolete because it implements a poor random number generator." 885 <<
" Use 'arc4random' instead";
887 PathDiagnosticLocation CELoc =
889 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
890 "Security", os2.str(), CELoc,
900 if (!CheckRand || !filter.check_rand)
912 PathDiagnosticLocation CELoc =
914 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
915 "'random' is not a secure random number generator",
917 "The 'random' function produces a sequence of values that " 918 "an adversary may be able to predict. Use 'arc4random' " 928 if (!filter.check_vfork)
932 PathDiagnosticLocation CELoc =
934 BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
935 "Potential insecure implementation-specific behavior in " 938 "Call to function 'vfork' is insecure as it can lead to " 939 "denial of service situations in the parent process. " 940 "Replace calls to vfork with calls to the safer " 941 "'posix_spawn' function",
951 void WalkAST::checkMsg_decodeValueOfObjCType(
const ObjCMessageExpr *ME) {
952 if (!filter.check_decodeValueOfObjCType)
958 const TargetInfo &TI = AC->getASTContext().getTargetInfo();
962 case llvm::Triple::IOS:
963 if (VT < VersionTuple(11, 0))
966 case llvm::Triple::MacOSX:
967 if (VT < VersionTuple(10, 13))
970 case llvm::Triple::WatchOS:
971 if (VT < VersionTuple(4, 0))
974 case llvm::Triple::TvOS:
975 if (VT < VersionTuple(11, 0))
982 PathDiagnosticLocation MELoc =
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.",
998 void WalkAST::checkUncheckedReturnValue(
CallExpr *CE) {
999 if (!filter.check_UncheckedReturn)
1006 if (II_setid[0] ==
nullptr) {
1007 static const char *
const identifiers[num_setids] = {
1008 "setuid",
"setgid",
"seteuid",
"setegid",
1009 "setreuid",
"setregid" 1012 for (
size_t i = 0; i < num_setids; i++)
1013 II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
1017 size_t identifierid;
1019 for (identifierid = 0; identifierid < num_setids; identifierid++)
1020 if (
id == II_setid[identifierid])
1023 if (identifierid >= num_setids)
1042 llvm::raw_svector_ostream os1(buf1);
1043 os1 <<
"Return value is not checked in call to '" << *FD <<
'\'';
1046 llvm::raw_svector_ostream os2(buf2);
1047 os2 <<
"The return value from the call to '" << *FD
1048 <<
"' is not checked. If an error occurs in '" << *FD
1049 <<
"', the following code may execute with unexpected privileges";
1051 PathDiagnosticLocation CELoc =
1053 BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
1054 "Security", os2.str(), CELoc,
1063 class SecuritySyntaxChecker :
public Checker<check::ASTCodeBody> {
1065 ChecksFilter filter;
1067 void checkASTCodeBody(
const Decl *D, AnalysisManager& mgr,
1068 BugReporter &BR)
const {
1069 WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
1075 void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
1076 mgr.registerChecker<SecuritySyntaxChecker>();
1079 bool ento::shouldRegisterSecuritySyntaxChecker(
const LangOptions &LO) {
1083 #define REGISTER_CHECKER(name) \ 1084 void ento::register##name(CheckerManager &mgr) { \ 1085 SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ 1086 checker->filter.check_##name = true; \ 1087 checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ 1090 bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
Represents a function declaration or definition.
VersionTuple getPlatformMinVersion() const
Retrieve the minimum desired version of the platform, to which the program should be compiled...
PointerType - C99 6.7.5.1 - Pointer Declarators.
QualType getPointeeType() const
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Selector getSelector() const
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.
Exposes information about the current target.
This represents one expression.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
An expression that sends a message to the given Objective-C object or class.
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.
std::string getAsString() const
Derive the full selector name (e.g.
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.
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
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]).
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
A reference to a declared variable, function, enum, etc.
static bool isArc4RandomAvailable(const ASTContext &Ctx)
This represents a decl that may have a name.