Add Tests with Address & UB Sanitizers Enabled (#285)

* Getting address sanitizer to work with MSVC and compile with Clang

* Some CMake fixups

* Get it working for Clang

* Enable UBSan

* Still need to debug

* Temporarily revert use of /Ob2 due to bug in Clang

* Update tests/sanitize-address/CMakeLists.txt

* Update scripts/azure-pipelines.yml
This commit is contained in:
Duncan Horn 2023-01-18 18:29:04 -08:00 коммит произвёл GitHub
Родитель 89ecb2bc98
Коммит ab663cfd89
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
21 изменённых файлов: 339 добавлений и 213 удалений

Просмотреть файл

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.11)
cmake_minimum_required(VERSION 3.15)
project(WIL)
include(GNUInstallDirs)

Просмотреть файл

@ -1,5 +1,6 @@
# E.g. replace_cxx_flag("/W[0-4]", "/W4")
# This is unfortunately still needed to disable exceptions/RTTI since modern CMake still has no builtin support...
# E.g. replace_cxx_flag("/EHsc", "/EHs-c-")
macro(replace_cxx_flag pattern text)
foreach (flag
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
@ -10,66 +11,61 @@ macro(replace_cxx_flag pattern text)
endforeach()
endmacro()
macro(append_cxx_flag text)
foreach (flag
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
string(APPEND ${flag} " ${text}")
endforeach()
endmacro()
# Fixup default compiler settings
add_compile_options(
# Be as strict as reasonably possible, since we want to support consumers using strict warning levels
/W4 /WX
)
# Be as strict as reasonably possible, since we want to support consumers using strict warning levels
replace_cxx_flag("/W[0-4]" "/W4")
append_cxx_flag("/WX")
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(
# Ignore some pedantic warnings enabled by '-Wextra'
-Wno-missing-field-initializers
# We want to be as conformant as possible, so tell MSVC to not be permissive (note that this has no effect on clang-cl)
append_cxx_flag("/permissive-")
# Ignore some pedantic warnings enabled by '-Wpedantic'
-Wno-language-extension-token
-Wno-c++17-attribute-extensions
-Wno-gnu-zero-variadic-macro-arguments
-Wno-extra-semi
# wistd::function has padding due to alignment. This is expected
append_cxx_flag("/wd4324")
# For tests, we want to be able to test self assignment, so disable this warning
-Wno-self-assign-overloaded
-Wno-self-move
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
# Ignore a few Clang warnings. We may want to revisit in the future to see if any of these can/should be removed
append_cxx_flag("-Wno-switch")
append_cxx_flag("-Wno-c++17-compat-mangling")
append_cxx_flag("-Wno-missing-field-initializers")
# clang needs this to enable _InterlockedCompareExchange128
-mcx16
# For tests, we want to be able to test self assignment, so disable this warning
append_cxx_flag("-Wno-self-assign-overloaded")
append_cxx_flag("-Wno-self-move")
# We don't want legacy MSVC conformance
-fno-delayed-template-parsing
# C++/WinRT does not declare 'override' in a number of places
append_cxx_flag("-Wno-inconsistent-missing-override")
# NOTE: Windows headers not clean enough for us to realistically attempt to start fixing these errors yet. That
# said, errors that originate from WIL headers may benefit
# -fno-ms-compatibility
# -ferror-limit=999
# -fmacro-backtrace-limit=0
# clang-cl does not understand the /permissive- flag (or at least it opts to ignore it). We can achieve similar
# results through the following flags.
append_cxx_flag("-fno-delayed-template-parsing")
# clang-cl needs this to enable _InterlockedCompareExchange128
append_cxx_flag("-mcx16")
# NOTE: Windows headers not clean enough for us to realistically attempt to start fixing these errors yet. That
# said, errors that originate from WIL headers may benefit
# append_cxx_flag("-fno-ms-compatibility")
# append_cxx_flag("-ferror-limit=999")
# append_cxx_flag("-fmacro-backtrace-limit=0")
# -fno-ms-compatibility turns off preprocessor compatability, which currently only works when __VA_OPT__ support is
# available (i.e. >= C++20)
# append_cxx_flag("-Xclang -std=c++2a")
# -fno-ms-compatibility turns off preprocessor compatability, which currently only works when __VA_OPT__ support
# is available (i.e. >= C++20)
# -Xclang -std=c++2a
)
else()
# Flags that are either ignored or unrecognized by clang-cl
# TODO: https://github.com/Microsoft/wil/issues/6
# append_cxx_flag("/experimental:preprocessor")
add_compile_options(
# We want to be as conformant as possible, so tell MSVC to not be permissive (note that this has no effect on clang-cl)
/permissive-
# CRT headers are not yet /experimental:preprocessor clean, so work around the known issues
# append_cxx_flag("/Wv:18")
# wistd::function has padding due to alignment. This is expected
/wd4324
append_cxx_flag("/bigobj")
# TODO: https://github.com/Microsoft/wil/issues/6
# /experimental:preprocessor
# NOTE: Temporary workaround while https://github.com/microsoft/wil/issues/102 is being investigated
append_cxx_flag("/d2FH4-")
# CRT headers are not yet /experimental:preprocessor clean, so work around the known issues
# /Wv:18
# Some tests have a LOT of template instantiations
/bigobj
# NOTE: Temporary workaround while https://github.com/microsoft/wil/issues/102 is being investigated
/d2FH4-
)
endif()

Просмотреть файл

@ -250,21 +250,24 @@ namespace wistd // ("Windows Implementation" std)
return _Invoker::__call(__f_, wistd::forward<_ArgTypes>(__arg)...);
}
} // __function
template<class _Rp, class ..._ArgTypes>
class __WI_LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)>
: public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>,
public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)>
{
// 'wistd::function' is most similar to 'inplace_function' in that it _only_ permits holding function objects
// that can fit within its internal buffer. Therefore, we expand this size to accommodate space for at least 12
// pointers (__base vtable takes an additional one).
static constexpr size_t __buffer_size = 13 * sizeof(void*);
constexpr const size_t __buffer_size = 13 * sizeof(void*);
} // __function
// NOTE: The extra 'alignas' here is to work around the x86 compiler bug mentioned in
// https://github.com/microsoft/STL/issues/1533 to force alignment on the stack
template<class _Rp, class ..._ArgTypes>
class __WI_LIBCPP_TEMPLATE_VIS __WI_ALIGNAS(typename aligned_storage<__function::__buffer_size>::type)
function<_Rp(_ArgTypes...)>
: public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>,
public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)>
{
using __base = __function::__base<_Rp(_ArgTypes...)>;
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
typename aligned_storage<__buffer_size>::type __buf_;
typename aligned_storage<__function::__buffer_size>::type __buf_;
__base* __f_;
__WI_LIBCPP_NO_CFI static __base *__as_base(void *p) {

Просмотреть файл

@ -149,8 +149,8 @@ namespace wistd // ("Windows Implementation" std)
struct __second_tag {};
template <class _T1, class _T2>
class __compressed_pair : private __compressed_pair_elem<_T1, 0>,
private __compressed_pair_elem<_T2, 1> {
class __declspec(empty_bases) __compressed_pair : private __compressed_pair_elem<_T1, 0>,
private __compressed_pair_elem<_T2, 1> {
using _Base1 = __compressed_pair_elem<_T1, 0>;
using _Base2 = __compressed_pair_elem<_T2, 1>;

Просмотреть файл

@ -18,7 +18,7 @@ jobs:
displayName: 'Install Clang'
- script: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars32.bat"
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsamd64_x86.bat"
if %ERRORLEVEL% NEQ 0 goto :eof
call scripts\init_all.cmd --fast
@ -37,5 +37,9 @@ jobs:
call scripts\build_all.cmd
displayName: 'Build x64'
- script: call scripts\runtests.cmd ~[LocalOnly]
# NOTE: We run the tests in the 32-bit cross-tools window out of convenience as this adds all necessary directories to
# the PATH that are necessary for finding the ASan/UBSan DLLs
- script: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsamd64_x86.bat""
call scripts\runtests.cmd ~[LocalOnly]
displayName: 'Run Tests'

Просмотреть файл

@ -1,4 +1,5 @@
@echo off
setlocal
setlocal EnableDelayedExpansion
set BUILD_ROOT=%~dp0\..\build
@ -15,23 +16,15 @@ if "%Platform%"=="x64" (
exit /B 1
)
call :build clang debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build clang release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build clang relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build clang minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
set COMPILERS=clang msvc
set BUILD_TYPES=debug release relwithdebinfo minsizerel
call :build msvc debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build msvc release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build msvc relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build msvc minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
for %%c in (%COMPILERS%) do (
for %%b in (%BUILD_TYPES%) do (
call :build %%c %%b
if !ERRORLEVEL! NEQ 0 ( goto :eof )
)
)
echo All build completed successfully!
@ -47,5 +40,6 @@ if not exist %BUILD_DIR% (
pushd %BUILD_DIR%
echo Building from %CD%
ninja
set EXIT_CODE=%ERRORLEVEL%
popd
goto :eof
exit /B %EXIT_CODE%

Просмотреть файл

@ -10,8 +10,8 @@ goto :init
:usage
echo USAGE:
echo init.cmd [--help] [-c^|--compiler ^<clang^|msvc^>] [-g^|--generator ^<ninja^|msbuild^>]
echo [-b^|--build-type ^<debug^|release^|relwithdebinfo^|minsizerel^>] [-l^|--linker link^|lld-link]
echo [-v^|--version X.Y.Z] [--cppwinrt ^<version^>] [--fast]
echo [-b^|--build-type ^<debug^|release^|relwithdebinfo^|minsizerel^>] [-v^|--version X.Y.Z]
echo [--cppwinrt ^<version^>] [--fast]
echo.
echo ARGUMENTS
echo -c^|--compiler Controls the compiler used, either 'clang' (the default) or 'msvc'
@ -31,7 +31,6 @@ goto :init
set COMPILER=
set GENERATOR=
set BUILD_TYPE=
set LINKER=
set CMAKE_ARGS=
set BITNESS=
set VERSION=
@ -90,21 +89,6 @@ goto :init
goto :parse
)
set LINKER_SET=0
if /I "%~1"=="-l" set LINKER_SET=1
if /I "%~1"=="--linker" set LINKER_SET=1
if %LINKER_SET%==1 (
if "%LINKER%" NEQ "" echo ERROR: Linker already specified & call :usage & exit /B 1
if /I "%~2"=="link" set LINKER=link
if /I "%~2"=="lld-link" set LINKER=lld-link
if "!LINKER!"=="" echo ERROR: Unrecognized/missing linker %~2 & call :usage & exit /B 1
shift
shift
goto :parse
)
set VERSION_SET=0
if /I "%~1"=="-v" set VERSION_SET=1
if /I "%~1"=="--version" set VERSION_SET=1
@ -145,9 +129,6 @@ goto :init
:: Check for conflicting arguments
if "%GENERATOR%"=="msbuild" (
if "%COMPILER%"=="clang" echo ERROR: Cannot use Clang with MSBuild & exit /B 1
:: While CMake won't give an error, specifying the linker won't actually have any effect with the VS generator
if "%LINKER%"=="lld-link" echo ERROR: Cannot use lld-link with MSBuild & exit /B 1
)
:: Select defaults
@ -158,8 +139,6 @@ goto :init
if "%BUILD_TYPE%"=="" set BUILD_TYPE=debug
if "%LINKER%"=="" set LINKER=link
:: Formulate CMake arguments
if %GENERATOR%==ninja set CMAKE_ARGS=%CMAKE_ARGS% -G Ninja
@ -180,10 +159,6 @@ goto :init
set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_SYSTEM_VERSION=10.0
)
if %LINKER%==lld-link (
set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_LINKER=lld-link
)
if "%VERSION%" NEQ "" set CMAKE_ARGS=%CMAKE_ARGS% -DWIL_BUILD_VERSION=%VERSION%
if "%CPPWINRT_VERSION%" NEQ "" set CMAKE_ARGS=%CMAKE_ARGS% -DCPPWINRT_VERSION=%CPPWINRT_VERSION%
@ -208,7 +183,6 @@ goto :init
:: Run CMake
pushd %BUILD_DIR%
echo Using compiler....... %COMPILER%
echo Using linker......... %LINKER%
echo Using architecture... %Platform%
echo Using build type..... %BUILD_TYPE%
echo Using build root..... %CD%

Просмотреть файл

@ -1,13 +1,14 @@
@echo off
setlocal
setlocal EnableDelayedExpansion
:: NOTE: Architecture is picked up from the command window, so we can't control that here :(
set COMPILERS=clang msvc
set BUILD_TYPES=debug relwithdebinfo
call %~dp0\init.cmd -c clang -g ninja -b debug %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call %~dp0\init.cmd -c clang -g ninja -b relwithdebinfo %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call %~dp0\init.cmd -c msvc -g ninja -b debug %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call %~dp0\init.cmd -c msvc -g ninja -b relwithdebinfo %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )
for %%c in (%COMPILERS%) do (
for %%b in (%BUILD_TYPES%) do (
call %~dp0\init.cmd -c %%c -g ninja -b %%b %*
if !ERRORLEVEL! NEQ 0 ( goto :eof )
)
)

Просмотреть файл

@ -7,41 +7,18 @@ set TEST_ARGS=%*
set BUILD_ROOT=%~dp0\..\build
:: Unlike building, we don't need to limit ourselves to the Platform of the command window
call :execute_tests clang64debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang64release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang64relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang64minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
set COMPILERS=clang msvc
set ARCHITECTURES=32 64
set BUILD_TYPES=debug release relwithdebinfo minsizerel
call :execute_tests clang32debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
for %%c in (%COMPILERS%) do (
for %%a in (%ARCHITECTURES%) do (
for %%b in (%BUILD_TYPES%) do (
call :execute_tests %%c%%a%%b
if !ERRORLEVEL! NEQ 0 ( goto :eof )
)
)
)
goto :eof
@ -52,18 +29,24 @@ if not exist %BUILD_DIR% ( goto :eof )
pushd %BUILD_DIR%
echo Running tests from %CD%
call :execute_test app witest.app.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
call :execute_test cpplatest witest.cpplatest.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
call :execute_test noexcept witest.noexcept.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
call :execute_test normal witest.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
call :execute_test sanitize-address witest.asan.exe
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
call :execute_test sanitize-undefined-behavior witest.ubsan.exe
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
call :execute_test win7 witest.win7.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
popd
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
goto :eof
:execute_tests_done
set EXIT_CODE=%ERRORLEVEL%
popd
exit /B %EXIT_CODE%
:execute_test
if not exist tests\%1\%2 ( goto :eof )

Просмотреть файл

@ -1,5 +1,5 @@
include(${PROJECT_SOURCE_DIR}/cmake/common_build_flags.cmake)
cmake_minimum_required(VERSION 3.11)
# All projects need to reference the WIL headers
include_directories(${PROJECT_SOURCE_DIR}/include)
@ -38,6 +38,14 @@ if (${FAST_BUILD})
add_definitions(-DCATCH_CONFIG_FAST_COMPILE -DWIL_FAST_BUILD)
endif()
# For some unknown reason, 'RelWithDebInfo' compiles with '/Ob1' as opposed to '/Ob2' which prevents inlining of
# functions not marked 'inline'. The reason we prefer 'RelWithDebInfo' over 'Release' is to get debug info, so manually
# revert to the desired (and default) inlining behavior as that exercises more interesting code paths
if (${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
# TODO: This is currently blocked by an apparent Clang bug: https://github.com/llvm/llvm-project/issues/59690
# replace_cxx_flag("/Ob1" "/Ob2")
endif()
set(COMMON_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/CommonTests.cpp
@ -58,3 +66,32 @@ add_subdirectory(cpplatest)
add_subdirectory(noexcept)
add_subdirectory(normal)
add_subdirectory(win7)
set(DEBUG_BUILD FALSE)
set(HAS_DEBUG_INFO FALSE)
if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")
set(DEBUG_BUILD TRUE)
set(HAS_DEBUG_INFO TRUE)
elseif(${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
set(HAS_DEBUG_INFO TRUE)
endif()
set(ASAN_AVAILABLE FALSE)
set(UBSAN_AVAILABLE FALSE)
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
# Address Sanitizer is available for all architectures and build types, but warns/errors if debug info is not enabled
set(ASAN_AVAILABLE ${HAS_DEBUG_INFO})
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# Address Sanitizer is not available with debug libraries
set(ASAN_AVAILABLE NOT ${DEBUG_BUILD})
set(UBSAN_AVAILABLE NOT ${DEBUG_BUILD})
endif()
if (${ASAN_AVAILABLE})
add_subdirectory(sanitize-address)
endif()
if (${UBSAN_AVAILABLE})
add_subdirectory(sanitize-undefined-behavior)
endif()

Просмотреть файл

@ -159,7 +159,7 @@ enum class EClassTest
};
DEFINE_ENUM_FLAG_OPERATORS(EClassTest);
enum ERawTest
enum ERawTest : unsigned int
{
ER_None = 0x0,
ER_One = 0x1,

Просмотреть файл

@ -240,8 +240,8 @@ void DoHStringDifferentValueComparisonTest(const wchar_t (&lhs)[LhsSize], const
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsUniqueStr, rhsUniqueStr, 1);
#ifdef WIL_ENABLE_EXCEPTIONS
std::wstring lhsWstr(lhs, 7);
std::wstring rhsWstr(rhs, 7);
std::wstring lhsWstr(lhs);
std::wstring rhsWstr(rhs);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsWstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhs, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsNonConstArray, 1);
@ -710,7 +710,7 @@ TEST_CASE("WinRTTests::VectorToVectorTest", "[winrt][to_vector]")
ComPtr<IInspectable> oneItem;
THROW_IF_FAILED(ints->GetAt(i, &oneItem));
REQUIRE(cast_to<UINT32>(vec[i]) == cast_to<UINT32>(oneItem));
}
}
#endif
}

Просмотреть файл

@ -1,10 +1,11 @@
project(witest.app)
add_executable(witest.app)
add_definitions(-DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP)
target_compile_definitions(witest.app PRIVATE
-DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP
)
target_sources(witest.app PUBLIC
target_sources(witest.app PRIVATE
${COMMON_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp

Просмотреть файл

@ -1,12 +1,11 @@
add_executable(witest.cpplatest)
# Compilers often don't use the latest C++ standard as the default. Periodically update this value (possibly conditioned
# on compiler) as new standards are ratified/support is available
set(CMAKE_CXX_STANDARD 20)
target_compile_features(witest.cpplatest PRIVATE cxx_std_20)
project(witest.cpplatest)
add_executable(witest.cpplatest)
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# Clang is not compatible with the experimental coroutine header, so temporarily disable some headers until full
# C++20 support is available
set(COROUTINE_SOURCES)
@ -15,7 +14,7 @@ else()
${CMAKE_CURRENT_SOURCE_DIR}/../ComApartmentVariableTests.cpp)
endif()
target_sources(witest.cpplatest PUBLIC
target_sources(witest.cpplatest PRIVATE
${COMMON_SOURCES}
${COROUTINE_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRTTests.cpp

Просмотреть файл

@ -6,3 +6,15 @@
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#if WITEST_ADDRESS_SANITIZER
extern "C" __declspec(dllexport)
const char* __asan_default_options()
{
return
// Tests validate OOM, so this is expected
"allocator_may_return_null=1"
// Some structs in Windows have dynamic size where we over-allocate for extra data past the end
":new_delete_type_mismatch=0";
}
#endif

Просмотреть файл

@ -1,16 +1,19 @@
project(witest.noexcept)
add_executable(witest.noexcept)
# Turn off exceptions for this test
replace_cxx_flag("/EHsc" "")
add_definitions(-DCATCH_CONFIG_DISABLE_EXCEPTIONS)
replace_cxx_flag("/EHsc" "/EHs-c-")
target_compile_definitions(witest.noexcept PRIVATE
-DCATCH_CONFIG_DISABLE_EXCEPTIONS
)
# Catch2 has a no exceptions mode (configured above), however still includes STL headers which contain try...catch
# statements... Thankfully MSVC just gives us a warning that we can disable
append_cxx_flag("/wd4530")
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
target_compile_options(witest.noexcept PRIVATE /wd4530)
endif()
target_sources(witest.noexcept PUBLIC
target_sources(witest.noexcept PRIVATE
${COMMON_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp

Просмотреть файл

@ -1,8 +1,7 @@
project(witest)
add_executable(witest)
target_sources(witest PUBLIC
target_sources(witest PRIVATE
${COMMON_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp

Просмотреть файл

@ -0,0 +1,49 @@
add_executable(witest.asan)
target_compile_options(witest.asan PRIVATE -fsanitize=address)
target_compile_definitions(witest.asan PRIVATE
-DCATCH_CONFIG_NO_WINDOWS_SEH # ASAN relies on first chance AVs
-DWITEST_ADDRESS_SANITIZER # To conditionally enable/disable code
)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_definitions(witest.asan PRIVATE
# Not compatible with using lld-link
-D_DISABLE_VECTOR_ANNOTATION
# See below; not compatible with exceptions
-DCATCH_CONFIG_DISABLE_EXCEPTIONS
)
# Clang ASan on Windows has issues with exceptions: https://github.com/google/sanitizers/issues/749
replace_cxx_flag("/EHsc" "/EHs-c-")
if ($ENV{Platform} STREQUAL "x86")
target_link_libraries(witest.asan PRIVATE
clang_rt.asan_dynamic-i386.lib
clang_rt.asan_dynamic_runtime_thunk-i386.lib
)
else()
target_link_libraries(witest.asan PRIVATE
clang_rt.asan_dynamic-x86_64.lib
clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
)
endif()
else()
# Using exceptions, so we can compile the STL tests
set(EXTRA_SOURCES
${EXTRA_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
)
endif()
target_sources(witest.asan PUBLIC
${COMMON_SOURCES}
${EXTRA_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinVerifyTrustTest.cpp
)

Просмотреть файл

@ -0,0 +1,25 @@
add_executable(witest.ubsan)
target_compile_options(witest.ubsan PRIVATE
-fsanitize=undefined
-fno-sanitize-recover=undefined # So we get test failures
)
target_compile_definitions(witest.ubsan PRIVATE
-DWITEST_UB_SANITIZER # To conditionally enable/disable code
)
# UBSan libraries were built assuming static linking
set_property(TARGET witest.ubsan
PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
target_sources(witest.ubsan PUBLIC
${COMMON_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinVerifyTrustTest.cpp
)

Просмотреть файл

@ -1,10 +1,11 @@
project(witest.win7)
add_executable(witest.win7)
add_definitions("-D_WIN32_WINNT=0x0601")
target_compile_definitions(witest.win7 PRIVATE
-D_WIN32_WINNT=0x0601
)
target_sources(witest.win7 PUBLIC
target_sources(witest.win7 PRIVATE
${COMMON_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp

Просмотреть файл

@ -12,14 +12,6 @@
#pragma once
#endif // _MSC_VER
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpragma-pack"
#pragma clang diagnostic ignored "-Wunused-value"
#pragma clang diagnostic ignored "-Wmicrosoft-sealed"
#pragma clang diagnostic ignored "-Winaccessible-base"
#endif
#pragma region includes
#include <inspectable.h>
@ -599,6 +591,8 @@ struct VerifyInheritanceHelper<I, Nil>
} // namespace Details
// note: Due to potential shutdown ordering issues, the results of GetModuleBase
// should always be checked for null on reference counting and cleanup operations.
inline Details::ModuleBase* GetModuleBase() throw()
{
return Details::ModuleBase::module_;
@ -1523,6 +1517,8 @@ private:
#define UnknownInterlockedCompareExchangePointer InterlockedCompareExchangePointer
#define UnknownInterlockedCompareExchangePointerForIncrement InterlockedCompareExchangePointer
#define UnknownInterlockedCompareExchangePointerForRelease InterlockedCompareExchangePointer
#define UnknownInterlockedCompareExchangeForIncrement InterlockedCompareExchange
#define UnknownInterlockedCompareExchangeForRelease InterlockedCompareExchange
#elif defined(_ARM_)
@ -1532,6 +1528,8 @@ private:
#define UnknownInterlockedCompareExchangePointer InterlockedCompareExchangePointer
#define UnknownInterlockedCompareExchangePointerForIncrement InterlockedCompareExchangePointerNoFence
#define UnknownInterlockedCompareExchangePointerForRelease InterlockedCompareExchangePointerRelease
#define UnknownInterlockedCompareExchangeForIncrement InterlockedCompareExchangeNoFence
#define UnknownInterlockedCompareExchangeForRelease InterlockedCompareExchangeRelease
#elif defined(_ARM64_)
@ -1541,6 +1539,8 @@ private:
#define UnknownInterlockedCompareExchangePointer InterlockedCompareExchangePointer
#define UnknownInterlockedCompareExchangePointerForIncrement InterlockedCompareExchangePointerNoFence
#define UnknownInterlockedCompareExchangePointerForRelease InterlockedCompareExchangePointerRelease
#define UnknownInterlockedCompareExchangeForIncrement InterlockedCompareExchangeNoFence
#define UnknownInterlockedCompareExchangeForRelease InterlockedCompareExchangeRelease
#else
@ -1562,6 +1562,37 @@ class __declspec(novtable) RuntimeClassImpl;
// PREFast cannot see through template instantiation for AsIID()
#pragma warning(disable: 6388)
// Reference counting functions that check overflow. If overflow is detected, ref count value will stop at LONG_MAX, and the object being
// reference-counted will be leaked.
inline unsigned long SafeUnknownIncrementReference(long volatile &refcount) throw()
{
long oldValue = refcount;
while (oldValue != LONG_MAX && (UnknownInterlockedCompareExchangeForIncrement(&refcount, oldValue + 1, oldValue) != oldValue))
{
oldValue = refcount;
}
if (oldValue != LONG_MAX)
{
return oldValue + 1;
}
else
{
return LONG_MAX;
}
}
inline unsigned long SafeUnknownDecrementReference(long volatile &refcount) throw()
{
long oldValue = refcount;
while (oldValue != LONG_MAX && (UnknownInterlockedCompareExchangeForRelease(&refcount, oldValue - 1, oldValue) != oldValue))
{
oldValue = refcount;
}
return oldValue - 1;
}
template <class RuntimeClassFlagsT, bool implementsWeakReferenceSource, bool implementsFtmBase, typename ...TInterfaces>
class __declspec(novtable) RuntimeClassImpl<RuntimeClassFlagsT, implementsWeakReferenceSource, false, implementsFtmBase, TInterfaces...> :
public Details::AdjustImplements<RuntimeClassFlagsT, false, TInterfaces...>::Type,
@ -1624,7 +1655,7 @@ protected:
#ifdef _PERF_COUNTERS
IncrementAddRefCount();
#endif
return UnknownIncrementReference(&refcount_);
return SafeUnknownIncrementReference(refcount_);
}
unsigned long InternalRelease() throw()
@ -1634,7 +1665,7 @@ protected:
#endif
// A release fence is required to ensure all guarded memory accesses are
// complete before any thread can begin destroying the object.
unsigned long newValue = UnknownDecrementReference(&refcount_);
unsigned long newValue = SafeUnknownDecrementReference(refcount_);
if (newValue == 0)
{
// An acquire fence is required before object destruction to ensure
@ -1786,7 +1817,7 @@ protected:
#ifdef _PERF_COUNTERS
IncrementAddRefCount();
#endif
return UnknownIncrementReference(&refcount_);
return SafeUnknownIncrementReference(refcount_);
}
unsigned long InternalRelease() throw()
@ -1796,7 +1827,7 @@ protected:
#endif
// A release fence is required to ensure all guarded memory accesses are
// complete before any thread can begin destroying the object.
unsigned long newValue = UnknownDecrementReference(&refcount_);
unsigned long newValue = SafeUnknownDecrementReference(refcount_);
if (newValue == 0)
{
// An acquire fence is required before object destruction to ensure
@ -1828,14 +1859,14 @@ public:
unsigned long IncrementStrongReference() throw()
{
return UnknownIncrementReference(&strongRefCount_);
return SafeUnknownIncrementReference(strongRefCount_);
}
unsigned long DecrementStrongReference() throw()
{
// A release fence is required to ensure all guarded memory accesses are
// complete before any thread can begin destroying the object.
unsigned long newValue = UnknownDecrementReference(&strongRefCount_);
unsigned long newValue = SafeUnknownDecrementReference(strongRefCount_);
if (newValue == 0)
{
// An acquire fence is required before object destruction to ensure
@ -2089,7 +2120,7 @@ inline INT_PTR EncodeWeakReferencePointer(Microsoft::WRL::Details::WeakReference
inline Microsoft::WRL::Details::WeakReferenceImpl* DecodeWeakReferencePointer(INT_PTR value)
{
return reinterpret_cast<Microsoft::WRL::Details::WeakReferenceImpl*>(value << 1);
return reinterpret_cast<Microsoft::WRL::Details::WeakReferenceImpl*>(static_cast<UINT_PTR>(value) << 1);
}
#pragma warning(pop) // C6388
@ -2134,6 +2165,7 @@ class RuntimeClass<InterfaceListHelper<TInterfaces...>, RuntimeClassFlagsT, impl
public RuntimeClassImpl<RuntimeClassFlagsT, implementsWeakReferenceSource, implementsInspectable, implementsFtmBase, TInterfaces...>
{
protected:
#pragma warning(suppress: 6101) // Function only used internally and the value of 'ppvObject' is only used if *handled is true
HRESULT CustomQueryInterface(REFIID /*riid*/, _Outptr_result_nullonfailure_ void** /*ppvObject*/, _Out_ bool *handled)
{
*handled = false;
@ -2153,6 +2185,7 @@ class RuntimeClass :
RuntimeClass(const RuntimeClass&);
RuntimeClass& operator=(const RuntimeClass&);
protected:
#pragma warning(suppress: 6101) // Function only used internally and the value of 'ppvObject' is only used if *handled is true
HRESULT CustomQueryInterface(REFIID /*riid*/, _Outptr_result_nullonfailure_ void** /*ppvObject*/, _Out_ bool *handled)
{
*handled = false;
@ -2177,6 +2210,7 @@ class RuntimeClass<RuntimeClassFlags<classFlags>, TInterfaces...> :
RuntimeClass(const RuntimeClass&);
RuntimeClass& operator=(const RuntimeClass&);
protected:
#pragma warning(suppress: 6101) // Function only used internally and the value of 'ppvObject' is only used if *handled is true
HRESULT CustomQueryInterface(REFIID /*riid*/, _Outptr_result_nullonfailure_ void** /*ppvObject*/, _Out_ bool *handled)
{
*handled = false;
@ -2196,8 +2230,8 @@ public:
namespace Details
{
//Weak reference implementation
class WeakReferenceImpl sealed:
// Weak reference implementation
class WeakReferenceImpl final :
public ::Microsoft::WRL::RuntimeClass<RuntimeClassFlags<ClassicCom>, IWeakReference>,
public StrongReference
{
@ -2289,6 +2323,11 @@ unsigned long RuntimeClassImpl<RuntimeClassFlagsT, true, true, false, I0, TInter
{
if (!IsValueAPointerToWeakReference(currentValue.rawValue))
{
if (static_cast<long>(currentValue.refCount) == LONG_MAX)
{
return LONG_MAX;
}
UINT_PTR updateValue = currentValue.refCount + 1;
#ifdef __WRL_UNITTEST__
@ -2324,6 +2363,11 @@ unsigned long RuntimeClassImpl<RuntimeClassFlagsT, true, true, false, I0, TInter
{
if (!IsValueAPointerToWeakReference(currentValue.rawValue))
{
if (static_cast<long>(currentValue.refCount) == LONG_MAX)
{
return LONG_MAX - 1;
}
UINT_PTR updateValue = currentValue.refCount - 1;
#ifdef __WRL_UNITTEST__
@ -2432,8 +2476,13 @@ public:
// Allocate memory with operator new(size, nothrow) only
// This will allow developer to override one operator only
// to enable different memory allocation model
buffer_ = (char*) (operator new (sizeof(T), std::nothrow));
return buffer_;
#ifdef __cpp_aligned_new
if constexpr (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
{
return buffer_ = (char*) operator new (sizeof(T), static_cast<std::align_val_t>(alignof(T)), ::std::nothrow);
}
#endif // /std:c++17 or later
return buffer_ = (char*) operator new (sizeof(T), ::std::nothrow);
}
void Detach() throw()
@ -2526,7 +2575,7 @@ namespace Details
{ \
return trustLevel; \
} \
STDMETHOD(GetRuntimeClassName)(_Out_ HSTRING* runtimeName) \
STDMETHOD(GetRuntimeClassName)(_Out_ HSTRING* runtimeName) override \
{ \
*runtimeName = nullptr; \
HRESULT hr = S_OK; \
@ -2537,7 +2586,7 @@ namespace Details
} \
return hr; \
} \
STDMETHOD(GetTrustLevel)(_Out_ ::TrustLevel* trustLvl) \
STDMETHOD(GetTrustLevel)(_Out_ ::TrustLevel* trustLvl) override \
{ \
*trustLvl = trustLevel; \
return S_OK; \
@ -2545,22 +2594,22 @@ namespace Details
STDMETHOD(GetIids)(_Out_ ULONG *iidCount, \
_When_(*iidCount == 0, _At_(*iids, _Post_null_)) \
_When_(*iidCount > 0, _At_(*iids, _Post_notnull_)) \
_Result_nullonfailure_ IID **iids) \
_Result_nullonfailure_ IID **iids) override \
{ \
return RuntimeClassT::GetIids(iidCount, iids); \
} \
STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppvObject) \
STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppvObject) override \
{ \
bool handled = false; \
HRESULT hr = this->CustomQueryInterface(riid, ppvObject, &handled); \
if (FAILED(hr) || handled) return hr; \
return RuntimeClassT::QueryInterface(riid, ppvObject); \
} \
STDMETHOD_(ULONG, Release)() \
STDMETHOD_(ULONG, Release)() override \
{ \
return RuntimeClassT::Release(); \
} \
STDMETHOD_(ULONG, AddRef)() \
STDMETHOD_(ULONG, AddRef)() override \
{ \
return RuntimeClassT::AddRef(); \
} \
@ -2648,8 +2697,4 @@ namespace Details
// Restore packing
#include <poppack.h>
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif // _WRL_IMPLEMENTS_H_