Index: clang/docs/analyzer/checkers.rst =================================================================== --- clang/docs/analyzer/checkers.rst +++ clang/docs/analyzer/checkers.rst @@ -1915,6 +1915,18 @@ It warns on misusing the following functions: ``strcpy()``, ``gets()``, ``fscanf()``, ``sprintf()``. +.. _alpha-security-cert-str-32c: + +alpha.security.cert.str.32c +""""""""""""""""""""""""""" + +SEI CERT checker of `STR32-C rule`_. + +It warns on reading non-null-terminated strings. This warning is restricted to +the allocations which the Static Analyzer models with :ref:`unix.Malloc`_. + +Also warns on misusing the ``strncpy()`` function. + .. _alpha-security-ArrayBound: alpha.security.ArrayBound (C) Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -812,6 +812,11 @@ Dependencies<[StrCheckerBase]>, Documentation; +def Str32cChecker : Checker<"32c">, + HelpText<"SEI CERT checker of rules defined in STR32-C">, + Dependencies<[StrCheckerBase]>, + Documentation; + } // end "alpha.cert.str" let ParentPackage = SecurityAlpha in { Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h @@ -13,11 +13,13 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICSIZE_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICSIZE_H +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSizeInfo.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "llvm/ADT/Optional.h" namespace clang { namespace ento { @@ -26,6 +28,12 @@ DefinedOrUnknownSVal getDynamicSize(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB); +/// \returns The stored dynamic size information or the array's static size +/// information for the region \p MR. +Optional getDynamicSizeInfo(ProgramStateRef State, + const MemRegion *MR, + SValBuilder &SVB); + /// \returns The stored dynamic size expression for the region \p MR. const Expr *getDynamicSizeExpr(ProgramStateRef State, const MemRegion *MR); Index: clang/lib/StaticAnalyzer/Checkers/cert/StrChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/cert/StrChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/cert/StrChecker.cpp @@ -15,6 +15,9 @@ // - '31c' // https://wiki.sei.cmu.edu/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator // +// - '32c' +// https://wiki.sei.cmu.edu/confluence/display/c/STR32-C.+Do+not+pass+a+non-null-terminated+character+sequence+to+a+library+function+that+expects+a+string +// //===----------------------------------------------------------------------===// #include "AllocationState.h" @@ -39,14 +42,18 @@ struct CallContext { CallContext(Optional DestinationPos, - Optional SourcePos = None) - : DestinationPos(DestinationPos), SourcePos(SourcePos) {} + Optional SourcePos = None, + Optional LengthPos = None) + : DestinationPos(DestinationPos), SourcePos(SourcePos), + LengthPos(LengthPos) {} Optional DestinationPos; Optional SourcePos; + Optional LengthPos; }; -class StrCheckerBase : public Checker { +class StrCheckerBase + : public Checker> { using StrCheck = std::function; @@ -64,6 +71,8 @@ // store the string is being null-terminated in the 'NullTerminationMap'. void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; + void checkPostStmt(const DeclStmt *S, CheckerContext &C) const; + void checkGets(const CallEvent &Call, const CallContext &CallC, CheckerContext &C) const; void checkSprintf(const CallEvent &Call, const CallContext &CallC, @@ -72,13 +81,16 @@ CheckerContext &C) const; void checkStrcpy(const CallEvent &Call, const CallContext &CallC, CheckerContext &C) const; + void checkStrncpy(const CallEvent &Call, const CallContext &CallC, + CheckerContext &C) const; void createOverflowReport(const CallEvent &Call, const CallContext &CallC, CheckerContext &C) const; std::unique_ptr BT; bool WarnOnCall; - bool EnableStr31cChecker = false; + + bool EnableStr31cChecker = false, EnableStr32cChecker = false; private: const CallDescriptionMap> CDM = { @@ -90,13 +102,20 @@ // int fscanf(FILE *stream, const char *format, ... [char *dest]); {{"fscanf", 3, 2}, {&StrCheckerBase::checkFscanf, {2}}}, // char *strcpy(char *dest, const char *src); - {{"strcpy", 2}, {&StrCheckerBase::checkStrcpy, {0, 1}}}}; + {{"strcpy", 2}, {&StrCheckerBase::checkStrcpy, {0, 1}}}, + + // The following checks STR32-C rules. + // char *strncpy(char *dest, const char *src, size_t count) + {{"strncpy", 3}, {&StrCheckerBase::checkStrncpy, {0, 1, 2}}}}; }; } // namespace // It stores whether the string is null-terminated. REGISTER_MAP_WITH_PROGRAMSTATE(NullTerminationMap, const MemRegion *, bool) +// It stores the allocations which we emit notes on. +REGISTER_LIST_WITH_PROGRAMSTATE(AllocationList, const MemRegion *) + //===----------------------------------------------------------------------===// // Helper functions. //===----------------------------------------------------------------------===// @@ -151,15 +170,16 @@ ProgramStateRef State = C.getState(); std::string CallName = Call.getCalleeIdentifier()->getName(); - Optional ArrayName = getName(Call.getArgExpr(DestPos), C); + std::string ArrayName = "the array"; + if (Optional TmpArrayName = getName(Call.getArgExpr(DestPos), C)) + ArrayName = *TmpArrayName; SmallString<128> Msg; llvm::raw_svector_ostream Out(Msg); - Out << '\'' << CallName << "' could write outside of " - << (ArrayName ? *ArrayName : "the array"); + Out << '\'' << CallName << "' could write outside of " << ArrayName; std::string ReportMsg = Out.str(); - if (WarnOnCall) { + if (WarnOnCall && !EnableStr32cChecker) { const Expr *Arg = Call.getArgExpr(DestPos); auto Report = std::make_unique( @@ -186,6 +206,68 @@ C.addTransition(State, Tag); } +void createWrongSizeReport(const MemRegion *MR, Optional ArrayName, + bool IsAlloc, ProgramStateRef State, + CheckerContext &C) { + const NoteTag *Tag = C.getNoteTag( + [=]() -> std::string { + SmallString<128> Msg; + llvm::raw_svector_ostream Out(Msg); + Out << (ArrayName ? ('\'' + *ArrayName + '\'') : "The array") << " is " + << (IsAlloc ? "allocated" : "initialized") + << " without considering the length of the string it " + "will store, therefore it could overflow"; + + return Out.str(); + }, + /*IsPrunable=*/false); + + // Mark the string could be not null-terminated. + State = State->set(MR, false); + State = State->add(MR); + C.addTransition(State, C.getPredecessor(), Tag); +} + +static bool isWrongAllocation(SVal DestV, SVal SrcV, CheckerContext &C) { + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); + + // Check the size of the allocation to prevent false alarms. + const MemRegion *SrcMR = getRegion(SrcV); + const MemRegion *DestMR = getRegion(DestV); + if (!SrcMR || !DestMR) + return false; + + DefinedOrUnknownSVal SrcSize = getDynamicSize(State, SrcMR, SVB); + DefinedOrUnknownSVal DestSize = getDynamicSize(State, DestMR, SVB); + + // 'strlen(src) + integer' is most likely fine. + // FIXME: We cannot catch every '+ integer' part at the moment so we do not + // check that property for now. + SValHasDescendant HasDescendantSrc(SrcMR); + if (HasDescendantSrc.Visit(DestSize)) + return false; + + if (const MemRegion *DestSizeR = DestSize.getAsRegion()) + if (DestSizeR == SrcMR) + return false; + + // 'StringRegion' returns the size with the null-terminator. + if (const llvm::APSInt *SrcSizeInt = SVB.getKnownValue(State, SrcSize)) + if (const llvm::APSInt *DestSizeInt = SVB.getKnownValue(State, DestSize)) + if (SrcSizeInt->getZExtValue() <= DestSizeInt->getZExtValue()) + return false; + + return true; +} + +static bool isWrongAllocation(const CallEvent &Call, const CallContext &CallC, + CheckerContext &C) { + SVal DestV = Call.getArgSVal(*CallC.DestinationPos); + SVal SrcV = Call.getArgSVal(*CallC.SourcePos); + return isWrongAllocation(DestV, SrcV, C); +} + //===----------------------------------------------------------------------===// // Evaluating problematic function calls. //===----------------------------------------------------------------------===// @@ -227,34 +309,22 @@ if (!EnableStr31cChecker) return; - ProgramStateRef State = C.getState(); - SValBuilder &SVB = C.getSValBuilder(); - SVal SrcV = Call.getArgSVal(*CallC.SourcePos); - SVal DestV = Call.getArgSVal(*CallC.DestinationPos); - - // Check the size of the allocation to prevent false alarms. - const MemRegion *SrcMR = getRegion(SrcV); - const MemRegion *DestMR = getRegion(DestV); - if (!SrcMR || !DestMR) - return; - - DefinedOrUnknownSVal SrcSize = getDynamicSize(State, SrcMR, SVB); - DefinedOrUnknownSVal DestSize = getDynamicSize(State, DestMR, SVB); - - // 'strlen(src) + integer' is most likely fine. - // FIXME: We cannot catch every '+ integer' part at the moment so we do not - // check that property for now. - SValHasDescendant HasDescendantSrc(SrcMR); - if (HasDescendantSrc.Visit(DestSize)) - return; - - // 'StringRegion' returns the size with the null-terminator. - if (const llvm::APSInt *SrcSizeInt = SVB.getKnownValue(State, SrcSize)) - if (const llvm::APSInt *DestSizeInt = SVB.getKnownValue(State, DestSize)) - if (SrcSizeInt->getZExtValue() <= DestSizeInt->getZExtValue()) - return; + if (isWrongAllocation(Call, CallC, C)) { + createOverflowReport(Call, CallC, C); + } else { + // Mark the string null-terminated. + ProgramStateRef State = C.getState(); + State = State->set( + getRegion(Call.getArgSVal(*CallC.DestinationPos)), true); + C.addTransition(State); + } +} - createOverflowReport(Call, CallC, C); +void StrCheckerBase::checkStrncpy(const CallEvent &Call, + const CallContext &CallC, + CheckerContext &C) const { + if (EnableStr32cChecker) + createOverflowReport(Call, CallC, C); } //===----------------------------------------------------------------------===// @@ -319,15 +389,78 @@ } } -void StrCheckerBase::checkBind(SVal L, SVal V, const Stmt *S, - CheckerContext &C) const { +static bool checkAllocation(SVal L, SVal V, const Stmt *S, CheckerContext &C, + BugType &BT) { + const MemRegion *LocMR = getRegion(L); + const MemRegion *NonLocMR = getRegion(V); + if (!LocMR || !NonLocMR) + return false; + + // A C-string is null-terminated. + if (isa(NonLocMR)) + return false; + + // Only check for character allocations. + QualType Ty; + const auto *LocTVR = LocMR->getAs(); + if (!LocTVR) + return false; + + Ty = LocTVR->getValueType(); + if (Ty.isNull()) + return false; + + if (Ty->isPointerType()) + Ty = Ty->getPointeeType(); + + if (!Ty->isAnyCharacterType()) + return false; + + // See whether we have made a report so it prevent reports on rebindings. ProgramStateRef State = C.getState(); + if (State->get().contains(NonLocMR)) + return false; + + // Check for allocations we model. + Optional SizeInfo = + getDynamicSizeInfo(State, NonLocMR, C.getSValBuilder()); + if (!SizeInfo) + return false; + + if (const auto *NonLocTVR = NonLocMR->getAs()) { + QualType Ty = NonLocTVR->getLocationType(); + if (Ty->isPointerType()) + Ty = Ty->getPointeeType(); + + if (Ty->getAsArrayTypeUnsafe()) + return false; + } - // Check for a hand-written null-termination, e.g: 'c_string[length] = '\0';'. - const MemRegion *MR = getRegion(L); - if (!MR) - return; + // 'strlen(something) + something' is most likely fine. + // FIXME: Use the 'SValVisitor' to catch every such constructs of the symbol. + if (const SymExpr *SizeSym = SizeInfo->getSize().getAsSymExpr()) + for (const SymExpr *Sym : SizeSym->symbols()) + if (const auto *SIE = dyn_cast(Sym)) + if (SIE->getOpcode() == BO_Add) + if (const auto *SM = dyn_cast(SIE->getLHS())) + return true; + + Optional ArrayName; + if (const auto *VR = LocMR->getAs()) + if (const VarDecl *VD = VR->getDecl()) + ArrayName = VD->getNameAsString(); + + createWrongSizeReport(NonLocMR, ArrayName, /*IsAlloc=*/true, State, C); + return true; +} + +void StrCheckerBase::checkBind(SVal L, SVal V, const Stmt *S, + CheckerContext &C) const { + if (EnableStr32cChecker) + if (checkAllocation(L, V, S, C, *BT)) + return; + ProgramStateRef State = C.getState(); bool IsNullTermination = false; if (const llvm::APInt *Int = C.getSValBuilder().getKnownValue(State, V)) IsNullTermination = Int->isNullValue(); @@ -336,10 +469,24 @@ return; // Mark the string null-terminated. - State = State->set(MR, true); + State = State->set(getRegion(L), true); C.addTransition(State); } +void StrCheckerBase::checkPostStmt(const DeclStmt *S, CheckerContext &C) const { + if (const auto *VD = cast(S->getSingleDecl())) { + if (VD->getType()->getAsArrayTypeUnsafe()) { + VD->dumpColor(); + ProgramStateRef State = C.getState(); + + const VarRegion *VR = State->getRegion(VD, C.getLocationContext()); + + std::string ArrayName = VD->getNameAsString(); + createWrongSizeReport(VR, ArrayName, /*IsAlloc=*/false, State, C); + } + } +} + void ento::registerStrCheckerBase(CheckerManager &Mgr) { auto *Checker = Mgr.registerChecker(); @@ -362,3 +509,4 @@ bool ento::shouldRegister##Name(const LangOptions &LO) { return true; } REGISTER_CHECKER(Str31cChecker) +REGISTER_CHECKER(Str32cChecker) Index: clang/lib/StaticAnalyzer/Core/DynamicSize.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/DynamicSize.cpp +++ clang/lib/StaticAnalyzer/Core/DynamicSize.cpp @@ -40,6 +40,19 @@ return MR->getMemRegionManager().getStaticSize(MR, SVB); } +Optional getDynamicSizeInfo(ProgramStateRef State, + const MemRegion *MR, + SValBuilder &SVB) { + if (const DynamicSizeInfo *SizeInfo = State->get(MR)) + return *SizeInfo; + + if (const auto *TVR = MR->getAs()) + if (TVR->getValueType()->getAsArrayTypeUnsafe()) + return DynamicSizeInfo(MR->getMemRegionManager().getStaticSize(MR, SVB)); + + return None; +} + const Expr *getDynamicSizeExpr(ProgramStateRef State, const MemRegion *MR) { if (const DynamicSizeInfo *SizeInfo = State->get(MR)) return SizeInfo->getSizeExpr(); Index: clang/test/Analysis/Inputs/system-header-simulator.h =================================================================== --- clang/test/Analysis/Inputs/system-header-simulator.h +++ clang/test/Analysis/Inputs/system-header-simulator.h @@ -38,12 +38,29 @@ extern int errno; typedef int errno_t; -size_t strlen(const char *); +void *memcpy(void *dest, const void *src, size_t count); + +size_t strlen(const char *str); char *strcpy(char *dest, const char *src); errno_t strcpy_s(char *dest, size_t destSize, const char *src); char *strncpy(char *dest, const char *src, size_t count); -void *memcpy(void *dest, const void *src, size_t count); +// Without -fms-compatibility, wchar_t isn't a builtin type. MSVC defines +// _WCHAR_T_DEFINED if wchar_t is available. Microsoft recommends that you use +// the builtin type: "Using the typedef version can cause portability +// problems", but we're ok here because we're not actually running anything. +// Also of note is this cryptic warning: "The wchar_t type is not supported +// when you compile C code". +// +// See the docs for more: +// https://msdn.microsoft.com/en-us/library/dh8che7s.aspx +#if !defined(_WCHAR_T_DEFINED) && !__cplusplus +// "Microsoft implements wchar_t as a two-byte unsigned value" +typedef unsigned short wchar_t; +#define _WCHAR_T_DEFINED +#endif + +size_t wcslen(const wchar_t *str); typedef unsigned long __darwin_pthread_key_t; typedef __darwin_pthread_key_t pthread_key_t; Index: clang/test/Analysis/cert/str31-c-notes-warn-on-call-off.cpp =================================================================== --- clang/test/Analysis/cert/str31-c-notes-warn-on-call-off.cpp +++ clang/test/Analysis/cert/str31-c-notes-warn-on-call-off.cpp @@ -56,7 +56,8 @@ void test_using_before_terminating(const char *src) { char dest[128]; - // expected-note@-1 {{'dest' initialized here}} + // expected-note@-1 {{'dest' is initialized without considering the length of the string it will store, therefore it could overflow}} + // expected-note@-2 {{'dest' initialized here}} strcpy(dest, src); // expected-note@-1 {{'strcpy' could write outside of 'dest'}} Index: clang/test/Analysis/cert/str31-c-notes-warn-on-call-on.cpp =================================================================== --- clang/test/Analysis/cert/str31-c-notes-warn-on-call-on.cpp +++ clang/test/Analysis/cert/str31-c-notes-warn-on-call-on.cpp @@ -46,7 +46,8 @@ void test_using_before_terminating(const char *src) { char dest[128]; - // expected-note@-1 {{'dest' initialized here}} + // expected-note@-1 {{'dest' is initialized without considering the length of the string it will store, therefore it could overflow}} + // expected-note@-2 {{'dest' initialized here}} strcpy(dest, src); // expected-note@-1 {{'strcpy' could write outside of 'dest'}} Index: clang/test/Analysis/cert/str32-c-notes.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/cert/str32-c-notes.cpp @@ -0,0 +1,64 @@ +// RUN: %clang_analyze_cc1 \ +// RUN: -analyzer-checker=core,unix,alpha.security.cert.str.32c \ +// RUN: -analyzer-output=text -verify %s + +// See the examples on the page of STR32-C: +// https://wiki.sei.cmu.edu/confluence/display/c/STR32-C.+Do+not+pass+a+non-null-terminated+character+sequence+to+a+library+function+that+expects+a+string + +#include "../Inputs/system-header-simulator.h" + +void *realloc(void *memblock, size_t size); + +namespace test_strncpy_bad { +enum { STR_SIZE = 32 }; + +size_t func(const char *source) { + char c_str[STR_SIZE]; + // expected-note@-1 {{'c_str' is initialized without considering the length of the string it will store, therefore it could overflow}} + // expected-note@-2 {{'c_str' initialized here}} + + size_t ret = 0; + + if (source) { + // expected-note@-1 {{Assuming 'source' is non-null}} + // expected-note@-2 {{Taking true branch}} + + c_str[sizeof(c_str) - 1] = '\0'; + strncpy(c_str, source, sizeof(c_str)); + // expected-note@-1 {{'strncpy' could write outside of 'c_str'}} + + ret = strlen(c_str); + // expected-note@-1 {{'c_str' is not null-terminated}} + // expected-warning@-2 {{'c_str' is not null-terminated}} + } + return ret; +} +} // namespace test_strncpy_bad + +namespace test_realloc_bad { +size_t cur_msg_size = 1024; +size_t cur_msg_len = 0; + +void lessen_memory_usage(wchar_t *cur_msg) { + if (cur_msg == NULL) { + // expected-note@-1 {{Assuming 'cur_msg' is not equal to NULL}} + // expected-note@-2 {{Taking false branch}} + return; + } + + size_t temp_size = cur_msg_size / 2 + 1; + wchar_t temp[256]; + // expected-note@-1 {{'temp' is initialized without considering the length of the string it will store, therefore it could overflow}} + // expected-note@-2 {{'temp' initialized here}} + + /* temp and cur_msg may no longer be null-terminated */ + + cur_msg = temp; + // expected-note@-1 {{Value assigned to 'cur_msg'}} + + cur_msg_size = temp_size; + cur_msg_len = wcslen(cur_msg); + // expected-note@-1 {{'cur_msg' is not null-terminated}} + // expected-warning@-2 {{'cur_msg' is not null-terminated}} +} +} // namespace test_realloc_bad Index: clang/test/Analysis/cert/str32-c.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/cert/str32-c.cpp @@ -0,0 +1,88 @@ +// RUN: %clang_analyze_cc1 \ +// RUN: -analyzer-checker=core,unix,alpha.security.cert.str.32c \ +// RUN: -verify %s + +// See the examples on the page of STR32-C: +// https://wiki.sei.cmu.edu/confluence/display/c/STR32-C.+Do+not+pass+a+non-null-terminated+character+sequence+to+a+library+function+that+expects+a+string + +#include "../Inputs/system-header-simulator.h" + +void *realloc(void *memblock, size_t size); + +namespace test_strncpy_bad { +enum { STR_SIZE = 32 }; + +size_t func(const char *source) { + char c_str[STR_SIZE]; + size_t ret = 0; + + if (source) { + c_str[sizeof(c_str) - 1] = '\0'; + strncpy(c_str, source, sizeof(c_str)); + ret = strlen(c_str); + // expected-warning@-1 {{'c_str' is not null-terminated}} + } + return ret; +} +} // namespace test_strncpy_bad + +namespace test_strncpy_good { +enum { STR_SIZE = 32 }; + +size_t func(const char *src) { + char c_str[STR_SIZE]; + size_t ret = 0; + + if (src) { + strncpy(c_str, src, sizeof(c_str) - 1); + c_str[sizeof(c_str) - 1] = '\0'; + ret = strlen(c_str); + } + return ret; +} +} // namespace test_strncpy_good + +namespace test_realloc_bad { +wchar_t *cur_msg = NULL; +size_t cur_msg_size = 1024; +size_t cur_msg_len = 0; + +void lessen_memory_usage(void) { + if (cur_msg == NULL) + return; + + size_t temp_size = cur_msg_size / 2 + 1; + wchar_t *temp = (wchar_t *)realloc(cur_msg, temp_size * sizeof(wchar_t)); + /* temp and cur_msg may no longer be null-terminated */ + if (temp == NULL) + return; + + cur_msg = temp; + cur_msg_size = temp_size; + cur_msg_len = wcslen(cur_msg); + // expected-warning@-1 {{'cur_msg' is not null-terminated}} +} +} // namespace test_realloc_bad + +namespace test_realloc_good { +wchar_t *cur_msg = NULL; +size_t cur_msg_size = 1024; +size_t cur_msg_len = 0; + +void lessen_memory_usage(void) { + if (cur_msg == NULL) + return; + + size_t temp_size = cur_msg_size / 2 + 1; + wchar_t *temp = (wchar_t *)realloc(cur_msg, temp_size * sizeof(wchar_t)); + /* temp and cur_msg may no longer be null-terminated */ + if (temp == NULL) + return; + + cur_msg = temp; + /* Properly null-terminate cur_msg */ + cur_msg[temp_size - 1] = L'\0'; + cur_msg_size = temp_size; + cur_msg_len = wcslen(cur_msg); +} +} // namespace test_realloc_good Index: clang/test/Analysis/malloc.c =================================================================== --- clang/test/Analysis/malloc.c +++ clang/test/Analysis/malloc.c @@ -9,21 +9,6 @@ void clang_analyzer_eval(int); -// Without -fms-compatibility, wchar_t isn't a builtin type. MSVC defines -// _WCHAR_T_DEFINED if wchar_t is available. Microsoft recommends that you use -// the builtin type: "Using the typedef version can cause portability -// problems", but we're ok here because we're not actually running anything. -// Also of note is this cryptic warning: "The wchar_t type is not supported -// when you compile C code". -// -// See the docs for more: -// https://msdn.microsoft.com/en-us/library/dh8che7s.aspx -#if !defined(_WCHAR_T_DEFINED) -// "Microsoft implements wchar_t as a two-byte unsigned value" -typedef unsigned short wchar_t; -#define _WCHAR_T_DEFINED -#endif // !defined(_WCHAR_T_DEFINED) - typedef __typeof(sizeof(int)) size_t; void *malloc(size_t); void *alloca(size_t);