diff --git a/stl/inc/__msvc_sanitizer_annotate_container.hpp b/stl/inc/__msvc_sanitizer_annotate_container.hpp index 057d68e7c0f..6fd50ccb3c4 100644 --- a/stl/inc/__msvc_sanitizer_annotate_container.hpp +++ b/stl/inc/__msvc_sanitizer_annotate_container.hpp @@ -20,11 +20,13 @@ _STL_DISABLE_CLANG_WARNINGS // (this will be auto-defined on unsupported platforms) // + _DISABLE_STRING_ANNOTATION: same, but for only `basic_string` // + _DISABLE_VECTOR_ANNOTATION: same, but for only `vector` +// + _DISABLE_OPTIONAL_ANNOTATION: same, but for only `optional` // - _ENABLE_STL_ANNOTATION_ON_UNSUPPORTED_PLATFORMS: Don't auto-disable ASan annotations // - _ANNOTATE_STL: Even when ASan annotations are disabled, insert the code for annotating into the STL anyways; // this is useful when building static libraries which may be linked against both ASan and non-ASan binaries. // + _ANNOTATE_STRING: same, but only for `basic_string` // + _ANNOTATE_VECTOR: same, but only for `vector` +// + _ANNOTATE_OPTIONAL: same, but only for `optional` #if !defined(_DISABLE_STL_ANNOTATION) && !defined(_ENABLE_STL_ANNOTATION_ON_UNSUPPORTED_PLATFORMS) @@ -46,6 +48,9 @@ _STL_DISABLE_CLANG_WARNINGS #ifndef _DISABLE_VECTOR_ANNOTATION #define _DISABLE_VECTOR_ANNOTATION #endif // ^^^ !defined(_DISABLE_VECTOR_ANNOTATION) ^^^ +#ifndef _DISABLE_OPTIONAL_ANNOTATION +#define _DISABLE_OPTIONAL_ANNOTATION +#endif // ^^^ !defined(_DISABLE_OPTIONAL_ANNOTATION) ^^^ #endif // ^^^ defined(_DISABLE_STL_ANNOTATION) ^^^ @@ -59,6 +64,10 @@ _STL_DISABLE_CLANG_WARNINGS #define _ANNOTATE_VECTOR #endif // ^^^ !defined(_ANNOTATE_VECTOR) ^^^ +#ifndef _ANNOTATE_OPTIONAL +#define _ANNOTATE_OPTIONAL +#endif // ^^^ !defined(_ANNOTATE_OPTIONAL) ^^^ + #endif // ^^^ defined(_ANNOTATE_STL) ^^^ #ifdef __SANITIZE_ADDRESS__ @@ -67,6 +76,8 @@ _STL_DISABLE_CLANG_WARNINGS #define _INSERT_STRING_ANNOTATION #define _ACTIVATE_VECTOR_ANNOTATION #define _INSERT_VECTOR_ANNOTATION +#define _ACTIVATE_OPTIONAL_ANNOTATION +#define _INSERT_OPTIONAL_ANNOTATION #elif defined(__clang__) // ^^^ defined(__SANITIZE_ADDRESS__) / defined(__clang__) vvv @@ -75,6 +86,8 @@ _STL_DISABLE_CLANG_WARNINGS #define _INSERT_STRING_ANNOTATION #define _ACTIVATE_VECTOR_ANNOTATION #define _INSERT_VECTOR_ANNOTATION +#define _ACTIVATE_OPTIONAL_ANNOTATION +#define _INSERT_OPTIONAL_ANNOTATION #pragma comment(linker, "/INFERASANLIBS") #endif // __has_feature(address_sanitizer) @@ -89,6 +102,10 @@ _STL_DISABLE_CLANG_WARNINGS #undef _ACTIVATE_VECTOR_ANNOTATION #undef _INSERT_VECTOR_ANNOTATION #endif // ^^^ defined(_DISABLE_VECTOR_ANNOTATION) ^^^ +#ifdef _DISABLE_OPTIONAL_ANNOTATION +#undef _ACTIVATE_OPTIONAL_ANNOTATION +#undef _INSERT_OPTIONAL_ANNOTATION +#endif // ^^^ defined(_DISABLE_OPTIONAL_ANNOTATION) ^^^ #ifdef _ANNOTATE_STRING #define _INSERT_STRING_ANNOTATION @@ -96,6 +113,9 @@ _STL_DISABLE_CLANG_WARNINGS #ifdef _ANNOTATE_VECTOR #define _INSERT_VECTOR_ANNOTATION #endif // ^^^ defined(_ANNOTATE_VECTOR) ^^^ +#ifdef _ANNOTATE_OPTIONAL +#define _INSERT_OPTIONAL_ANNOTATION +#endif // ^^^ defined(_ANNOTATE_OPTIONAL) ^^^ #ifndef _INSERT_STRING_ANNOTATION @@ -104,6 +124,9 @@ _STL_DISABLE_CLANG_WARNINGS #ifndef _INSERT_VECTOR_ANNOTATION #pragma detect_mismatch("annotate_vector", "0") #endif // ^^^ !defined(_INSERT_VECTOR_ANNOTATION) ^^^ +#ifndef _INSERT_OPTIONAL_ANNOTATION +#pragma detect_mismatch("annotate_optional", "0") +#endif // ^^^ !defined(_INSERT_OPTIONAL_ANNOTATION) ^^^ #ifdef _ACTIVATE_STRING_ANNOTATION #pragma comment(lib, "stl_asan") @@ -113,9 +136,14 @@ _STL_DISABLE_CLANG_WARNINGS #pragma comment(lib, "stl_asan") #pragma detect_mismatch("annotate_vector", "1") #endif // ^^^ defined(_ACTIVATE_VECTOR_ANNOTATION) ^^^ +#ifdef _ACTIVATE_OPTIONAL_ANNOTATION +#pragma comment(lib, "stl_asan") +#pragma detect_mismatch("annotate_optional", "1") +#endif // ^^^ defined(_ACTIVATE_OPTIONAL_ANNOTATION) ^^^ #undef _ACTIVATE_STRING_ANNOTATION #undef _ACTIVATE_VECTOR_ANNOTATION +#undef _ACTIVATE_OPTIONAL_ANNOTATION extern "C" { #ifdef _INSERT_VECTOR_ANNOTATION @@ -125,16 +153,27 @@ extern const bool _Asan_vector_should_annotate; #ifdef _INSERT_STRING_ANNOTATION extern const bool _Asan_string_should_annotate; #endif + +#ifdef _INSERT_OPTIONAL_ANNOTATION +extern const bool _Asan_optional_should_annotate; +#endif } // extern "C" -#if defined(_INSERT_VECTOR_ANNOTATION) || defined(_INSERT_STRING_ANNOTATION) +#if defined(_INSERT_VECTOR_ANNOTATION) || defined(_INSERT_STRING_ANNOTATION) || defined(_INSERT_OPTIONAL_ANNOTATION) extern "C" { +void __cdecl __asan_poison_memory_region(void const volatile* _Addr, size_t _Size); +void __cdecl __asan_unpoison_memory_region(void const volatile* _Addr, size_t _Size); + // This must match ASan's primary declaration, which isn't marked `noexcept`. void __cdecl __sanitizer_annotate_contiguous_container( const void* _First, const void* _End, const void* _Old_last, const void* _New_last); } // extern "C" #ifdef _M_ARM64EC +#pragma comment(linker, "/alternatename:#__asan_poison_memory_region=#__asan_poison_memory_region_default") +#pragma comment(linker, "/alternatename:__asan_poison_memory_region=__asan_poison_memory_region_default") +#pragma comment(linker, "/alternatename:#__asan_unpoison_memory_region=#__asan_unpoison_memory_region_default") +#pragma comment(linker, "/alternatename:__asan_unpoison_memory_region=__asan_unpoison_memory_region_default") #pragma comment(linker, \ "/alternatename:#__sanitizer_annotate_contiguous_container=#__sanitizer_annotate_contiguous_container_default") #pragma comment(linker, \ @@ -143,7 +182,13 @@ void __cdecl __sanitizer_annotate_contiguous_container( #pragma comment(linker, "/alternatename:_Asan_vector_should_annotate=_Asan_vector_should_annotate_default") #pragma comment(linker, "/alternatename:#_Asan_string_should_annotate=#_Asan_string_should_annotate_default") #pragma comment(linker, "/alternatename:_Asan_string_should_annotate=_Asan_string_should_annotate_default") +#pragma comment(linker, "/alternatename:#_Asan_optional_should_annotate=#_Asan_optional_should_annotate_default") +#pragma comment(linker, "/alternatename:_Asan_optional_should_annotate=_Asan_optional_should_annotate_default") #elif defined(_M_HYBRID) +#pragma comment(linker, "/alternatename:#__asan_poison_memory_region=#__asan_poison_memory_region_default") +#pragma comment(linker, "/alternatename:___asan_poison_memory_region=___asan_poison_memory_region_default") +#pragma comment(linker, "/alternatename:#__asan_unpoison_memory_region=#__asan_unpoison_memory_region_default") +#pragma comment(linker, "/alternatename:___asan_unpoison_memory_region=___asan_unpoison_memory_region_default") #pragma comment(linker, \ "/alternatename:#__sanitizer_annotate_contiguous_container=#__sanitizer_annotate_contiguous_container_default") #pragma comment(linker, \ @@ -152,16 +197,24 @@ void __cdecl __sanitizer_annotate_contiguous_container( #pragma comment(linker, "/alternatename:__Asan_vector_should_annotate=__Asan_vector_should_annotate_default") #pragma comment(linker, "/alternatename:#_Asan_string_should_annotate=#_Asan_string_should_annotate_default") #pragma comment(linker, "/alternatename:__Asan_string_should_annotate=__Asan_string_should_annotate_default") +#pragma comment(linker, "/alternatename:#_Asan_optional_should_annotate=#_Asan_optional_should_annotate_default") +#pragma comment(linker, "/alternatename:__Asan_optional_should_annotate=__Asan_optional_should_annotate_default") #elif defined(_M_IX86) +#pragma comment(linker, "/alternatename:___asan_poison_memory_region=___asan_poison_memory_region_default") +#pragma comment(linker, "/alternatename:___asan_unpoison_memory_region=___asan_unpoison_memory_region_default") #pragma comment(linker, \ "/alternatename:___sanitizer_annotate_contiguous_container=___sanitizer_annotate_contiguous_container_default") #pragma comment(linker, "/alternatename:__Asan_vector_should_annotate=__Asan_vector_should_annotate_default") #pragma comment(linker, "/alternatename:__Asan_string_should_annotate=__Asan_string_should_annotate_default") +#pragma comment(linker, "/alternatename:__Asan_optional_should_annotate=__Asan_optional_should_annotate_default") #elif defined(_M_X64) || defined(_M_ARM64) +#pragma comment(linker, "/alternatename:__asan_poison_memory_region=__asan_poison_memory_region_default") +#pragma comment(linker, "/alternatename:__asan_unpoison_memory_region=__asan_unpoison_memory_region_default") #pragma comment(linker, \ "/alternatename:__sanitizer_annotate_contiguous_container=__sanitizer_annotate_contiguous_container_default") #pragma comment(linker, "/alternatename:_Asan_vector_should_annotate=_Asan_vector_should_annotate_default") #pragma comment(linker, "/alternatename:_Asan_string_should_annotate=_Asan_string_should_annotate_default") +#pragma comment(linker, "/alternatename:_Asan_optional_should_annotate=_Asan_optional_should_annotate_default") #else // ^^^ known architecture / unknown architecture vvv #error Unknown architecture #endif // ^^^ unknown architecture ^^^ diff --git a/stl/inc/optional b/stl/inc/optional index 8454922206e..8a90c37b518 100644 --- a/stl/inc/optional +++ b/stl/inc/optional @@ -13,6 +13,7 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with C #if _HAS_CXX20 #include #endif // _HAS_CXX20 +#include <__msvc_sanitizer_annotate_container.hpp> #include #include #include @@ -74,7 +75,13 @@ struct _Optional_destruct_base { // either contains a value of _Ty or is empty ( }; bool _Has_value; - constexpr _Optional_destruct_base() noexcept : _Dummy{}, _Has_value{false} {} // initialize an empty optional + constexpr _Optional_destruct_base() noexcept : _Dummy{}, _Has_value{false} { // initialize an empty optional +#ifdef _INSERT_OPTIONAL_ANNOTATION + if (!_STD _Is_constant_evaluated() && _Asan_optional_should_annotate) { + __asan_poison_memory_region(_STD addressof(_Value), sizeof(_Ty)); + } +#endif + } template constexpr explicit _Optional_destruct_base(in_place_t, _Types&&... _Args) @@ -93,6 +100,11 @@ struct _Optional_destruct_base { // either contains a value of _Ty or is empty ( _CONSTEXPR20 void reset() noexcept { _Has_value = false; +#ifdef _INSERT_OPTIONAL_ANNOTATION + if (!_STD _Is_constant_evaluated() && _Asan_optional_should_annotate) { + __asan_poison_memory_region(_STD addressof(_Value), sizeof(_Ty)); + } +#endif } }; @@ -108,6 +120,12 @@ struct _Optional_destruct_base<_Ty, false> { // either contains a value of _Ty o if (_Has_value) { _Value.~_Ty(); +#ifdef _INSERT_OPTIONAL_ANNOTATION + if (!_STD _Is_constant_evaluated() && _Asan_optional_should_annotate) { + __asan_poison_memory_region(_STD addressof(_Value), sizeof(_Ty)); + } +#endif + #if _MSVC_STL_DESTRUCTOR_TOMBSTONES // For the non-trivially destructible case, we can set the optional to be empty. // We don't attempt to scribble over the bytes of the object's storage because that could be expensive @@ -117,7 +135,13 @@ struct _Optional_destruct_base<_Ty, false> { // either contains a value of _Ty o } } - constexpr _Optional_destruct_base() noexcept : _Dummy{}, _Has_value{false} {} // initialize an empty optional + constexpr _Optional_destruct_base() noexcept : _Dummy{}, _Has_value{false} { // initialize an empty optional +#ifdef _INSERT_OPTIONAL_ANNOTATION + if (!_STD _Is_constant_evaluated() && _Asan_optional_should_annotate) { + __asan_poison_memory_region(_STD addressof(_Value), sizeof(_Ty)); + } +#endif + } template constexpr explicit _Optional_destruct_base(in_place_t, _Types&&... _Args) @@ -139,6 +163,13 @@ struct _Optional_destruct_base<_Ty, false> { // either contains a value of _Ty o _CONSTEXPR20 void reset() noexcept { if (_Has_value) { _Value.~_Ty(); + +#ifdef _INSERT_OPTIONAL_ANNOTATION + if (!_STD _Is_constant_evaluated() && _Asan_optional_should_annotate) { + __asan_poison_memory_region(_STD addressof(_Value), sizeof(_Ty)); + } +#endif + _Has_value = false; } } @@ -153,6 +184,13 @@ struct _Optional_construct_base : _Optional_destruct_base<_Ty> { _CONSTEXPR20 _Ty& _Construct(_Types&&... _Args) noexcept(is_nothrow_constructible_v<_Ty, _Types...>) { // transition from the empty to the value-containing state _STL_INTERNAL_CHECK(!this->_Has_value); + +#ifdef _INSERT_OPTIONAL_ANNOTATION + if (!_STD _Is_constant_evaluated() && _Asan_optional_should_annotate) { + __asan_unpoison_memory_region(_STD addressof(this->_Value), sizeof(_Ty)); + } +#endif + _STD _Construct_in_place(this->_Value, _STD forward<_Types>(_Args)...); this->_Has_value = true; return this->_Value; diff --git a/stl/src/asan.cpp b/stl/src/asan.cpp index da75de1afcd..e49b05f27f6 100644 --- a/stl/src/asan.cpp +++ b/stl/src/asan.cpp @@ -3,7 +3,8 @@ namespace std { extern "C" { - extern const bool _Asan_string_should_annotate = true; - extern const bool _Asan_vector_should_annotate = true; + extern const bool _Asan_string_should_annotate = true; + extern const bool _Asan_vector_should_annotate = true; + extern const bool _Asan_optional_should_annotate = true; } // extern "C" } // namespace std diff --git a/stl/src/asan_noop.cpp b/stl/src/asan_noop.cpp index fda89488c80..1a32e86ec4b 100644 --- a/stl/src/asan_noop.cpp +++ b/stl/src/asan_noop.cpp @@ -2,8 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception extern "C" { -extern const bool _Asan_string_should_annotate_default = false; -extern const bool _Asan_vector_should_annotate_default = false; +extern const bool _Asan_string_should_annotate_default = false; +extern const bool _Asan_vector_should_annotate_default = false; +extern const bool _Asan_optional_should_annotate_default = false; + +void __cdecl __asan_poison_memory_region_default(void const volatile*, size_t) {} +void __cdecl __asan_unpoison_memory_region_default(void const volatile*, size_t) {} void __cdecl __sanitizer_annotate_contiguous_container_default( const void*, const void*, const void*, const void*) noexcept {} diff --git a/tests/std/test.lst b/tests/std/test.lst index 5bb68adcb1d..b1ba196aeec 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -278,6 +278,7 @@ tests\GH_005553_regex_character_translation tests\GH_005768_pow_accuracy tests\GH_005800_stable_sort_large_alignment tests\GH_005968_headers_provide_begin_end +tests\GH_005974_asan_annotate_optional tests\LWG2381_num_get_floating_point tests\LWG2510_tag_classes tests\LWG2597_complex_branch_cut diff --git a/tests/std/tests/GH_005974_asan_annotate_optional/env.lst b/tests/std/tests/GH_005974_asan_annotate_optional/env.lst new file mode 100644 index 00000000000..f1477bf3294 --- /dev/null +++ b/tests/std/tests/GH_005974_asan_annotate_optional/env.lst @@ -0,0 +1,52 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# This test matrix is the usual test matrix, with all currently unsupported options removed, crossed with the ASan flags. + +# TRANSITION, google/sanitizers#328: clang-cl does not support /MDd or /MTd with ASan +RUNALL_INCLUDE ..\prefix.lst +RUNALL_CROSSLIST +PM_CL="/Zi /wd4611 /w14640 /Zc:threadSafeInit-" PM_LINK="/debug" +RUNALL_CROSSLIST +PM_CL="-fsanitize=address /BE /c /EHsc /MD /std:c++17" +PM_CL="-fsanitize=address /BE /c /EHsc /MDd /std:c++17 /permissive-" +PM_CL="-fsanitize=address /BE /c /EHsc /MT /std:c++20 /permissive-" +PM_CL="-fsanitize=address /BE /c /EHsc /MTd /std:c++latest /permissive-" +PM_CL="-fsanitize=address /EHsc /MD /std:c++17" +PM_CL="-fsanitize=address /EHsc /MD /std:c++20" +PM_CL="-fsanitize=address /EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor" +PM_CL="-fsanitize=address /EHsc /MD /std:c++latest /permissive- /Zc:noexceptTypes-" +PM_CL="-fsanitize=address /EHsc /MDd /std:c++17 /fp:except /Zc:preprocessor" +PM_CL="-fsanitize=address /EHsc /MDd /std:c++17 /permissive-" +PM_CL="-fsanitize=address /EHsc /MDd /std:c++20 /permissive-" +PM_CL="-fsanitize=address /EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t-" +PM_CL="-fsanitize=address /EHsc /MDd /std:c++latest /permissive-" +PM_CL="-fsanitize=address /EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog-" +PM_CL="-fsanitize=address /EHsc /MT /std:c++latest /permissive-" +PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive" +PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog-" +PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive- /fp:strict" +PM_CL="-fsanitize=address /EHsc /MTd /std:c++latest /permissive-" +PM_CL="/D_ANNOTATE_OPTIONAL /BE /c /EHsc /MD /std:c++17" +PM_CL="/D_ANNOTATE_OPTIONAL /BE /c /EHsc /MDd /std:c++17 /permissive-" +PM_CL="/D_ANNOTATE_OPTIONAL /BE /c /EHsc /MT /std:c++20 /permissive-" +PM_CL="/D_ANNOTATE_OPTIONAL /BE /c /EHsc /MTd /std:c++latest /permissive-" +PM_CL="/D_ANNOTATE_OPTIONAL /EHsc /MD /std:c++17" +PM_CL="/D_ANNOTATE_OPTIONAL /EHsc /MD /std:c++20" +PM_CL="/D_ANNOTATE_OPTIONAL /EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor" +PM_CL="/D_ANNOTATE_OPTIONAL /EHsc /MD /std:c++latest /permissive- /Zc:noexceptTypes-" +PM_CL="/D_ANNOTATE_OPTIONAL /EHsc /MDd /std:c++17 /fp:except /Zc:preprocessor" +PM_CL="/D_ANNOTATE_OPTIONAL /EHsc /MDd /std:c++17 /permissive-" +PM_CL="/D_ANNOTATE_OPTIONAL /EHsc /MDd /std:c++20 /permissive-" +PM_CL="/D_ANNOTATE_OPTIONAL /EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t-" +PM_CL="/D_ANNOTATE_OPTIONAL /EHsc /MDd /std:c++latest /permissive-" +PM_CL="/D_ANNOTATE_OPTIONAL /EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog-" +PM_CL="/D_ANNOTATE_OPTIONAL /EHsc /MT /std:c++latest /permissive-" +PM_CL="/D_ANNOTATE_OPTIONAL /EHsc /MTd /std:c++latest /permissive" +PM_CL="/D_ANNOTATE_OPTIONAL /EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog-" +PM_CL="/D_ANNOTATE_OPTIONAL /EHsc /MTd /std:c++latest /permissive- /fp:strict" +PM_CL="/D_ANNOTATE_OPTIONAL /EHsc /MTd /std:c++latest /permissive-" +# TRANSITION, we don't use /ALTERNATENAME for Clang (see GH-5224) so we cannot test /D_ANNOTATE_OPTIONAL without -fsanitize=address +PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++17" +PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++20 /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fsanitize=address -fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++latest /permissive- /fp:strict" diff --git a/tests/std/tests/GH_005974_asan_annotate_optional/notarget.lst b/tests/std/tests/GH_005974_asan_annotate_optional/notarget.lst new file mode 100644 index 00000000000..0027fd085c1 --- /dev/null +++ b/tests/std/tests/GH_005974_asan_annotate_optional/notarget.lst @@ -0,0 +1,5 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +## TRANSITION, VSO-1938218 ASAN is not yet supported on arm64 +arm64 diff --git a/tests/std/tests/GH_005974_asan_annotate_optional/test.cpp b/tests/std/tests/GH_005974_asan_annotate_optional/test.cpp new file mode 100644 index 00000000000..24d4e2405da --- /dev/null +++ b/tests/std/tests/GH_005974_asan_annotate_optional/test.cpp @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// REQUIRES: x64 || x86 + +#include +#include + +#ifdef __SANITIZE_ADDRESS__ +extern "C" int __cdecl __asan_address_is_poisoned(void const volatile* addr); +#define ASAN_VERIFY_POISONED(addr) assert(__asan_address_is_poisoned((addr)) != 0) +#define ASAN_VERIFY_UNPOISONED(addr) assert(__asan_address_is_poisoned((addr)) == 0) +#else +#define ASAN_VERIFY_POISONED(addr) ((void) (addr)) +#define ASAN_VERIFY_UNPOISONED(addr) ((void) (addr)) +#endif + +struct Payload { + long long x; + long long y; + long long z; + long long w; +}; + +void test_poison_on_empty_access() { + [[maybe_unused]] std::optional opt; + ASAN_VERIFY_POISONED(reinterpret_cast(&opt)); +} + +void test_emplace_unpoisoning() { + std::optional opt; + opt.emplace(); + ASAN_VERIFY_UNPOISONED(reinterpret_cast(&opt)); +} + +void test_assignment_unpoisoning() { + std::optional opt = std::nullopt; + opt = Payload{}; + ASAN_VERIFY_UNPOISONED(reinterpret_cast(&opt)); +} + +void test_repoison_after_reset() { + std::optional opt = Payload{}; + ASAN_VERIFY_UNPOISONED(reinterpret_cast(&opt)); + opt.reset(); + ASAN_VERIFY_POISONED(reinterpret_cast(&opt)); +} + +constexpr bool test_constexpr() { +#if _HAS_CXX20 + bool res = true; + std::optional opt = std::nullopt; + opt = Payload{}; + opt.reset(); + opt = Payload{86, 0, 0, 0}; + res = opt->x == 86; + opt.emplace(42, 0, 0, 0); + res = res && (opt->x == 42); + return res; +#else + std::optional opt{Payload{86, 0, 0, 0}}; + return opt->x == 86; +#endif +} + +int main() { + test_poison_on_empty_access(); + test_emplace_unpoisoning(); + test_assignment_unpoisoning(); + test_repoison_after_reset(); + static_assert(test_constexpr(), "constexpr test failed"); + + return 0; +}