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 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;
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);
112 void checkDeprecatedOrUnsafeBufferHandling(
const CallExpr *CE,
118 void checkUncheckedReturnValue(
CallExpr *CE);
126 void WalkAST::VisitChildren(
Stmt *S) {
127 for (
Stmt *Child : S->children())
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);
205 for (
Stmt *Child : S->children())
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))
248 return ND ==
x || ND == y ? DR :
nullptr;
252 return U->isIncrementDecrementOp()
262 void 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";
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)
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);
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) {
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";
812 PathDiagnosticLocation CELoc =
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";
886 PathDiagnosticLocation CELoc =
888 BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
889 "Security", os2.str(), CELoc,
899 if (!CheckRand || !filter.check_rand)
911 PathDiagnosticLocation CELoc =
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)
931 PathDiagnosticLocation CELoc =
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",
950 void 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))
981 PathDiagnosticLocation MELoc =
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.",
997 void 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";
1050 PathDiagnosticLocation CELoc =
1052 BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
1053 "Security", os2.str(), CELoc,
1062 class SecuritySyntaxChecker :
public Checker<check::ASTCodeBody> {
1064 ChecksFilter filter;
1066 void checkASTCodeBody(
const Decl *D, AnalysisManager& mgr,
1067 BugReporter &BR)
const {
1068 WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
1074 void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
1075 mgr.registerChecker<SecuritySyntaxChecker>();
1078 bool 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; }