Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -367,6 +367,12 @@ 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 buffer manipulating functions">, + DescFile<"CheckSecuritySyntaxOnly.cpp">; + def DeprecatedBufferHandling : Checker<"DeprecatedBufferHandling">, + HelpText<"Warn on uses of deprecated buffer manipulating functions">, + DescFile<"CheckSecuritySyntaxOnly.cpp">; } let ParentPackage = Security in { def FloatLoopCounter : Checker<"FloatLoopCounter">, Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -4393,6 +4393,8 @@ CmdArgs.push_back("-analyzer-checker=security.insecureAPI.mktemp"); CmdArgs.push_back("-analyzer-checker=security.insecureAPI.mkstemp"); CmdArgs.push_back("-analyzer-checker=security.insecureAPI.vfork"); + CmdArgs.push_back("-analyzer-checker=security.insecureAPI.DeprecatedOrUnsafeBufferHandling"); + CmdArgs.push_back("-analyzer-checker=security.insecureAPI.DeprecatedBufferHandling"); } // Default nullability checks. Index: lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -43,6 +43,8 @@ DefaultBool check_mktemp; DefaultBool check_mkstemp; DefaultBool check_strcpy; + DefaultBool check_DeprecatedOrUnsafeBufferHandling; + DefaultBool check_DeprecatedBufferHandling; DefaultBool check_rand; DefaultBool check_vfork; DefaultBool check_FloatLoopCounter; @@ -53,6 +55,8 @@ CheckName checkName_mktemp; CheckName checkName_mkstemp; CheckName checkName_strcpy; + CheckName checkName_DeprecatedOrUnsafeBufferHandling; + CheckName checkName_DeprecatedBufferHandling; CheckName checkName_rand; CheckName checkName_vfork; CheckName checkName_FloatLoopCounter; @@ -96,6 +100,8 @@ 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 checkDeprecatedBufferHandling(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); @@ -138,6 +144,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::checkDeprecatedBufferHandling) + .Case("snprintf", &WalkAST::checkDeprecatedBufferHandling) + .Case("vswprintf", &WalkAST::checkDeprecatedBufferHandling) + .Case("vsnprintf", &WalkAST::checkDeprecatedBufferHandling) + .Case("memcpy", &WalkAST::checkDeprecatedBufferHandling) + .Case("memmove", &WalkAST::checkDeprecatedBufferHandling) + .Case("strncpy", &WalkAST::checkDeprecatedBufferHandling) + .Case("strncat", &WalkAST::checkDeprecatedBufferHandling) + .Case("memset", &WalkAST::checkDeprecatedBufferHandling) .Case("drand48", &WalkAST::checkCall_rand) .Case("erand48", &WalkAST::checkCall_rand) .Case("jrand48", &WalkAST::checkCall_rand) @@ -553,6 +582,120 @@ } //===----------------------------------------------------------------------===// +// 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. +// CWE-119: Improper Restriction of Operations within +// the Bounds of a Memory Buffer +//===----------------------------------------------------------------------===// +void WalkAST::checkDeprecatedBufferHandling(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_DeprecatedBufferHandling) + return; + + if(!BR.getContext().getLangOpts().C11) + return; + + StringRef Name = FD->getIdentifier()->getName(); + 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 << "Using '" << Name << "' is deprecated as it does not " + "provide bounding of the memory buffer or security " + "checks introduced in the C11 standard. Replace " + "with analogous functions introduced in C11 standard that " + "supports length arguments or provides boundary checks such as '" + << Name << "_s'."; + + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_DeprecatedBufferHandling, + out1.str(), + "Security", + out2.str(), + CELoc, CE->getCallee()->getSourceRange()); +} + +//===----------------------------------------------------------------------===// +// Check: 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; + checkDeprecatedBufferHandling(CE, FD); + + // Issue a warning. + 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) + .Default(-1); + + assert(ArgIndex >= 0 && "Unsupported function"); + + StringRef ReplaceName; + if(Name == "sprintf") + ReplaceName = "'snprintf' or 'sprintf"; + else if(Name == "vsprintf") + ReplaceName = "'vsnprintf' or 'vsprintf"; + else + ReplaceName = Name; + + // 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) + return; + + 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 bounding of the memory buffer. 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) { @@ -585,7 +728,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; @@ -774,5 +916,5 @@ REGISTER_CHECKER(vfork) REGISTER_CHECKER(FloatLoopCounter) REGISTER_CHECKER(UncheckedReturn) - - +REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling) +REGISTER_CHECKER(DeprecatedBufferHandling) 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; @@ -202,3 +205,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{{Using 'sprintf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'sprintf_s'}} + scanf("%d", &a); // expected-warning{{Using 'scanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'scanf_s'}} + scanf("%s", buf); // expected-warning{{Call to function 'scanf' is insecure as it does not provide bounding of the memory buffer. Replace with analogous functions that support length arguments or provides boundary checks such as 'scanf_s' in case of C11}} expected-warning{{Using 'scanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'scanf_s'}} + scanf("%4s", buf); // expected-warning{{Using 'scanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'scanf_s'}} + 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. Replace with analogous functions that support length arguments or provides boundary checks such as 'wscanf_s' in case of C11}} expected-warning{{Using 'wscanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'wscanf_s'}} + fscanf(file, "%d", &a); // expected-warning{{Using 'fscanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'fscanf_s'}} + fscanf(file, "%s", buf); // expected-warning{{Call to function 'fscanf' is insecure as it does not provide bounding of the memory buffer. Replace with analogous functions that support length arguments or provides boundary checks such as 'fscanf_s' in case of C11}} expected-warning{{Using 'fscanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'fscanf_s'}} + fscanf(file, "%4s", buf); // expected-warning{{Using 'fscanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'fscanf_s'}} + 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. Replace with analogous functions that support length arguments or provides boundary checks such as 'fwscanf_s' in case of C11}} expected-warning{{Using 'fwscanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'fwscanf_s'}} + sscanf("5", "%d", &a); // expected-warning{{Using 'sscanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'sscanf_s'}} + sscanf("5", "%s", buf); // expected-warning{{Call to function 'sscanf' is insecure as it does not provide bounding of the memory buffer. Replace with analogous functions that support length arguments or provides boundary checks such as 'sscanf_s' in case of C11}} expected-warning{{Using 'sscanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'sscanf_s'}} + sscanf("5", "%4s", buf); // expected-warning{{Using 'sscanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'sscanf_s'}} + 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. Replace with analogous functions that support length arguments or provides boundary checks such as 'swscanf_s' in case of C11}} expected-warning{{Using 'swscanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'swscanf_s'}} + swprintf(L"5", 1, (const wchar_t*) L"%s", wbuf); // expected-warning{{Using 'swprintf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'swprintf_s'}} + snprintf("5", 1, "%s", buf); // expected-warning{{Using 'snprintf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'snprintf_s'}} + memcpy(buf, wbuf, 1); // expected-warning{{Using 'memcpy' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'memcpy_s'}} + memmove(buf, wbuf, 1); // expected-warning{{Using 'memmove' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'memmove_s'}} + strncpy(buf, "a", 1); // expected-warning{{Using 'strncpy' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'strncpy_s'}} + strncat(buf, "a", 1); // expected-warning{{Using 'strncat' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'strncat_s'}} + memset(buf, 'a', 1); // expected-warning{{Using 'memset' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'memset_s'}} +} + +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{{Using 'vsprintf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'vsprintf_s'}} expected-warning{{Call to function 'vsprintf' is insecure as it does not provide bounding of the memory buffer. 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{{Using 'vscanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'vscanf_s'}} expected-warning{{Call to function 'vscanf' is insecure as it does not provide bounding of the memory buffer. 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{{Using 'vfscanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'vfscanf_s'}} expected-warning{{Call to function 'vfscanf' is insecure as it does not provide bounding of the memory buffer. 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{{Using 'vsscanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'vsscanf_s'}} expected-warning{{Call to function 'vsscanf' is insecure as it does not provide bounding of the memory buffer. 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{{Using 'vsnprintf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'vsnprintf_s'}} +} + +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{{Using 'vwscanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'vwscanf_s'}} expected-warning{{Call to function 'vwscanf' is insecure as it does not provide bounding of the memory buffer. 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{{Using 'vfwscanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'vfwscanf_s'}} expected-warning{{Call to function 'vfwscanf' is insecure as it does not provide bounding of the memory buffer. 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{{Using 'vswscanf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'vswscanf_s'}} expected-warning{{Call to function 'vswscanf' is insecure as it does not provide bounding of the memory buffer. 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{{Using 'vswprintf' is deprecated as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard. Replace with analogous functions introduced in C11 standard that supports length arguments or provides boundary checks such as 'vswprintf_s'}} +} +