summaryrefslogtreecommitdiffstats
path: root/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp')
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp124
1 files changed, 115 insertions, 9 deletions
diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
index 163ca9d855..3f1c213a56 100644
--- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -1,9 +1,8 @@
//==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -45,6 +44,7 @@ struct ChecksFilter {
DefaultBool check_mktemp;
DefaultBool check_mkstemp;
DefaultBool check_strcpy;
+ DefaultBool check_DeprecatedOrUnsafeBufferHandling;
DefaultBool check_rand;
DefaultBool check_vfork;
DefaultBool check_FloatLoopCounter;
@@ -58,6 +58,7 @@ struct ChecksFilter {
CheckName checkName_mktemp;
CheckName checkName_mkstemp;
CheckName checkName_strcpy;
+ CheckName checkName_DeprecatedOrUnsafeBufferHandling;
CheckName checkName_rand;
CheckName checkName_vfork;
CheckName checkName_FloatLoopCounter;
@@ -104,6 +105,8 @@ public:
void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD);
void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD);
void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD);
+ void checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
+ const FunctionDecl *FD);
void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);
void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);
void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD);
@@ -149,6 +152,14 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
.Case("mkstemps", &WalkAST::checkCall_mkstemp)
.Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)
.Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat)
+ .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf",
+ "vscanf", "vwscanf", "vfscanf", "vfwscanf",
+ &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
+ .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf",
+ "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove",
+ &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
+ .Cases("strncpy", "strncat", "memset",
+ &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
.Case("drand48", &WalkAST::checkCall_rand)
.Case("erand48", &WalkAST::checkCall_rand)
.Case("jrand48", &WalkAST::checkCall_rand)
@@ -553,7 +564,6 @@ void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
CELoc, CE->getCallee()->getSourceRange());
}
-
//===----------------------------------------------------------------------===//
// Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.
//===----------------------------------------------------------------------===//
@@ -642,6 +652,7 @@ void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
// CWE-119: Improper Restriction of Operations within
// the Bounds of a Memory Buffer
//===----------------------------------------------------------------------===//
+
void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
if (!filter.check_strcpy)
return;
@@ -680,6 +691,7 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
// CWE-119: Improper Restriction of Operations within
// the Bounds of a Memory Buffer
//===----------------------------------------------------------------------===//
+
void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
if (!filter.check_strcpy)
return;
@@ -702,8 +714,92 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
}
//===----------------------------------------------------------------------===//
+// Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',
+// 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
+// 'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf',
+// 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset'
+// is deprecated since C11.
+//
+// Use of 'sprintf', 'vsprintf', 'scanf', 'wscanf','fscanf',
+// 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
+// 'swscanf', 'vsscanf', 'vswscanf' without buffer limitations
+// is insecure.
+//
+// CWE-119: Improper Restriction of Operations within
+// the Bounds of a Memory Buffer
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
+ const FunctionDecl *FD) {
+ if (!filter.check_DeprecatedOrUnsafeBufferHandling)
+ return;
+
+ if (!BR.getContext().getLangOpts().C11)
+ return;
+
+ // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size
+ // restrictions).
+ enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 };
+
+ StringRef Name = FD->getIdentifier()->getName();
+ if (Name.startswith("__builtin_"))
+ Name = Name.substr(10);
+
+ int ArgIndex =
+ llvm::StringSwitch<int>(Name)
+ .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0)
+ .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf",
+ "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1)
+ .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy",
+ "memmove", "memset", "strncpy", "strncat", DEPR_ONLY)
+ .Default(UNKNOWN_CALL);
+
+ assert(ArgIndex != UNKNOWN_CALL && "Unsupported function");
+ bool BoundsProvided = ArgIndex == DEPR_ONLY;
+
+ if (!BoundsProvided) {
+ // Currently we only handle (not wide) string literals. It is possible to do
+ // better, either by looking at references to const variables, or by doing
+ // real flow analysis.
+ auto FormatString =
+ dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts());
+ if (FormatString &&
+ FormatString->getString().find("%s") == StringRef::npos &&
+ FormatString->getString().find("%[") == StringRef::npos)
+ BoundsProvided = true;
+ }
+
+ SmallString<128> Buf1;
+ SmallString<512> Buf2;
+ llvm::raw_svector_ostream Out1(Buf1);
+ llvm::raw_svector_ostream Out2(Buf2);
+
+ Out1 << "Potential insecure memory buffer bounds restriction in call '"
+ << Name << "'";
+ Out2 << "Call to function '" << Name
+ << "' is insecure as it does not provide ";
+
+ if (!BoundsProvided) {
+ Out2 << "bounding of the memory buffer or ";
+ }
+
+ Out2 << "security checks introduced "
+ "in the C11 standard. Replace with analogous functions that "
+ "support length arguments or provides boundary checks such as '"
+ << Name << "_s' in case of C11";
+
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(),
+ filter.checkName_DeprecatedOrUnsafeBufferHandling,
+ Out1.str(), "Security", Out2.str(), CELoc,
+ CE->getCallee()->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
// Common check for str* functions with no bounds parameters.
//===----------------------------------------------------------------------===//
+
bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) {
const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
if (!FPT)
@@ -906,12 +1002,23 @@ public:
};
}
+void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
+ mgr.registerChecker<SecuritySyntaxChecker>();
+}
+
+bool ento::shouldRegisterSecuritySyntaxChecker(const LangOptions &LO) {
+ return true;
+}
+
#define REGISTER_CHECKER(name) \
void ento::register##name(CheckerManager &mgr) { \
- SecuritySyntaxChecker *checker = \
- mgr.registerChecker<SecuritySyntaxChecker>(); \
+ SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \
checker->filter.check_##name = true; \
checker->filter.checkName_##name = mgr.getCurrentCheckName(); \
+ } \
+ \
+ bool ento::shouldRegister##name(const LangOptions &LO) { \
+ return true; \
}
REGISTER_CHECKER(bcmp)
@@ -926,5 +1033,4 @@ REGISTER_CHECKER(rand)
REGISTER_CHECKER(vfork)
REGISTER_CHECKER(FloatLoopCounter)
REGISTER_CHECKER(UncheckedReturn)
-
-
+REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)