Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -389,6 +389,9 @@ def UncheckedReturn : Checker<"UncheckedReturn">, HelpText<"Warn on uses of functions whose return values must be always checked">, DescFile<"CheckSecuritySyntaxOnly.cpp">; + def DeprecatedOrUnsafeBufferHandling : Checker<"DeprecatedOrUnsafeBufferHandling">, + HelpText<"Warn on uses of unsecure or deprecated buffer manipulating functions">, + DescFile<"CheckSecuritySyntaxOnly.cpp">; } let ParentPackage = Security in { def FloatLoopCounter : Checker<"FloatLoopCounter">, Index: lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -42,6 +42,7 @@ DefaultBool check_mktemp; DefaultBool check_mkstemp; DefaultBool check_strcpy; + DefaultBool check_DeprecatedOrUnsafeBufferHandling; DefaultBool check_rand; DefaultBool check_vfork; DefaultBool check_FloatLoopCounter; @@ -52,6 +53,7 @@ CheckName checkName_mktemp; CheckName checkName_mkstemp; CheckName checkName_strcpy; + CheckName checkName_DeprecatedOrUnsafeBufferHandling; CheckName checkName_rand; CheckName checkName_vfork; CheckName checkName_FloatLoopCounter; @@ -95,6 +97,7 @@ 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); @@ -137,6 +140,29 @@ .Case("mkstemps", &WalkAST::checkCall_mkstemp) .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy) .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat) + .Case("sprintf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("vsprintf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("scanf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("wscanf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("fscanf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("fwscanf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("vscanf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("vwscanf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("vfscanf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("vfwscanf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("sscanf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("swscanf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("vsscanf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("vswscanf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("swprintf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("snprintf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("vswprintf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("vsnprintf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("memcpy", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("memmove", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("strncpy", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("strncat", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("memset", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) .Case("drand48", &WalkAST::checkCall_rand) .Case("erand48", &WalkAST::checkCall_rand) .Case("jrand48", &WalkAST::checkCall_rand) @@ -563,6 +589,101 @@ } //===----------------------------------------------------------------------===// +// 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). + StringRef Name = FD->getIdentifier()->getName(); + int ArgIndex = llvm::StringSwitch(Name) + .Case("sprintf", 1) + .Case("vsprintf", 1) + .Case("scanf", 0) + .Case("wscanf", 0) + .Case("fscanf", 1) + .Case("fwscanf", 1) + .Case("vscanf", 0) + .Case("vwscanf", 0) + .Case("vfscanf", 1) + .Case("vfwscanf", 1) + .Case("sscanf", 1) + .Case("swscanf", 1) + .Case("vsscanf", 1) + .Case("vswscanf", 1) + .Case("swprintf", -1) + .Case("snprintf", -1) + .Case("vswprintf", -1) + .Case("vsnprintf", -1) + .Case("memcpy", -1) + .Case("memmove", -1) + .Case("memset", -1) + .Case("strncpy", -1) + .Case("strncat", -1) + .Default(-2); + + assert(ArgIndex >= -1 && "Unsupported function"); + bool BoundsProvided = ArgIndex == -1; + + 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(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) { @@ -595,7 +716,6 @@ // Originally: // CWE-338: Use of cryptographically weak prng //===----------------------------------------------------------------------===// - void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) { if (!filter.check_rand || !CheckRand) return; @@ -784,5 +904,4 @@ REGISTER_CHECKER(vfork) REGISTER_CHECKER(FloatLoopCounter) REGISTER_CHECKER(UncheckedReturn) - - +REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling) Index: test/Analysis/security-syntax-checks.m =================================================================== --- test/Analysis/security-syntax-checks.m +++ test/Analysis/security-syntax-checks.m @@ -13,6 +13,9 @@ # define BUILTIN(f) f #endif /* USE_BUILTINS */ +#include "Inputs/system-header-simulator-for-valist.h" +#include "Inputs/system-header-simulator-for-simple-stream.h" + typedef typeof(sizeof(int)) size_t; @@ -212,3 +215,83 @@ mkdtemp("XXXXXX"); } + +//===----------------------------------------------------------------------=== +// deprecated or unsafe buffer handling +//===----------------------------------------------------------------------=== +typedef int wchar_t; + +int sprintf(char *str, const char *format, ...); +//int vsprintf (char *s, const char *format, va_list arg); +int scanf(const char *format, ...); +int wscanf(const wchar_t *format, ...); +int fscanf(FILE *stream, const char *format, ...); +int fwscanf(FILE *stream, const wchar_t *format, ...); +int vscanf(const char *format, va_list arg); +int vwscanf(const wchar_t *format, va_list arg); +int vfscanf(FILE *stream, const char *format, va_list arg); +int vfwscanf(FILE *stream, const wchar_t *format, va_list arg); +int sscanf(const char *s, const char *format, ...); +int swscanf(const wchar_t *ws, const wchar_t *format, ...); +int vsscanf(const char *s, const char *format, va_list arg); +int vswscanf(const wchar_t *ws, const wchar_t *format, va_list arg); +int swprintf(wchar_t *ws, size_t len, const wchar_t *format, ...); +int snprintf(char *s, size_t n, const char *format, ...); +int vswprintf(wchar_t *ws, size_t len, const wchar_t *format, va_list arg); +int vsnprintf(char *s, size_t n, const char *format, va_list arg); +void *memcpy(void *destination, const void *source, size_t num); +void *memmove(void *destination, const void *source, size_t num); +char *strncpy(char *destination, const char *source, size_t num); +char *strncat(char *destination, const char *source, size_t num); +void *memset(void *ptr, int value, size_t num); + +void test_deprecated_or_unsafe_buffer_handling_1() { + char buf [5]; + wchar_t wbuf [5]; + int a; + FILE *file; + sprintf(buf, "a"); // expected-warning{{Call to function 'sprintf' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'sprintf_s' in case of C11}} + scanf("%d", &a); // expected-warning{{Call to function 'scanf' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'scanf_s' in case of C11}} + scanf("%s", buf); // expected-warning{{Call to function 'scanf' is insecure as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'scanf_s' in case of C11}} + scanf("%4s", buf); // expected-warning{{Call to function 'scanf' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'scanf_s' in case of C11}} + wscanf((const wchar_t*) L"%s", buf); // expected-warning{{Call to function 'wscanf' is insecure as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'wscanf_s' in case of C11}} + fscanf(file, "%d", &a); // expected-warning{{Call to function 'fscanf' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'fscanf_s' in case of C11}} + fscanf(file, "%s", buf); // expected-warning{{Call to function 'fscanf' is insecure as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'fscanf_s' in case of C11}} + fscanf(file, "%4s", buf); // expected-warning{{Call to function 'fscanf' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'fscanf_s' in case of C11}} + fwscanf(file, (const wchar_t*) L"%s", wbuf); // expected-warning{{Call to function 'fwscanf' is insecure as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'fwscanf_s' in case of C11}} + sscanf("5", "%d", &a); // expected-warning{{Call to function 'sscanf' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'sscanf_s' in case of C11}} + sscanf("5", "%s", buf); // expected-warning{{Call to function 'sscanf' is insecure as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'sscanf_s' in case of C11}} + sscanf("5", "%4s", buf); // expected-warning{{Call to function 'sscanf' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'sscanf_s' in case of C11}} + swscanf(L"5", (const wchar_t*) L"%s", wbuf); // expected-warning{{Call to function 'swscanf' is insecure as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'swscanf_s' in case of C11}} + swprintf(L"5", 1, (const wchar_t*) L"%s", wbuf); // expected-warning{{Call to function 'swprintf' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'swprintf_s' in case of C11}} + snprintf("5", 1, "%s", buf); // expected-warning{{Call to function 'snprintf' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'snprintf_s' in case of C11}} + memcpy(buf, wbuf, 1); // expected-warning{{Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'memcpy_s' in case of C11}} + memmove(buf, wbuf, 1); // expected-warning{{Call to function 'memmove' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'memmove_s' in case of C11}} + strncpy(buf, "a", 1); // expected-warning{{Call to function 'strncpy' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'strncpy_s' in case of C11}} + strncat(buf, "a", 1); // expected-warning{{Call to function 'strncat' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'strncat_s' in case of C11}} + memset(buf, 'a', 1); // expected-warning{{Call to function 'memset' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'memset_s' in case of C11}} +} + +void test_deprecated_or_unsafe_buffer_handling_2(const char *format, ...) { + char buf [5]; + FILE *file; + va_list args; + va_start(args, format); + vsprintf(buf, format, args); // expected-warning{{Call to function 'vsprintf' is insecure as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'vsprintf_s' in case of C11}} + vscanf(format, args); // expected-warning{{Call to function 'vscanf' is insecure as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'vscanf_s' in case of C11}} + vfscanf(file, format, args); // expected-warning{{Call to function 'vfscanf' is insecure as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'vfscanf_s' in case of C11}} + vsscanf("a", format, args); // expected-warning{{Call to function 'vsscanf' is insecure as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'vsscanf_s' in case of C11}} + vsnprintf("a", 1, format, args); // expected-warning{{Call to function 'vsnprintf' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'vsnprintf_s' in case of C11}} +} + +void test_deprecated_or_unsafe_buffer_handling_3(const wchar_t *format, ...) { + wchar_t wbuf [5]; + FILE *file; + va_list args; + va_start(args, format); + vwscanf(format, args); // expected-warning{{Call to function 'vwscanf' is insecure as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'vwscanf_s' in case of C11}} + vfwscanf(file, format, args); // expected-warning{{Call to function 'vfwscanf' is insecure as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'vfwscanf_s' in case of C11}} + vswscanf(L"a", format, args); // expected-warning{{Call to function 'vswscanf' is insecure as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'vswscanf_s' in case of C11}} + vswprintf(L"a", 1, format, args); // expected-warning{{Call to function 'vswprintf' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'vswprintf_s' in case of C11}} +} +