-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[libcxx] Poison memory in variant destroy #101048
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@llvm/pr-subscribers-libcxx Author: Chris Cotter (ccotter) ChangesIn the version of With these changes, MSAN reports uninitialized memory: I wasn't sure how to add a test for this just yet, but wanted to get the changes up first to review whether this change would make sense in libc++. Full diff: https://github.com/llvm/llvm-project/pull/101048.diff 1 Files Affected:
diff --git a/libcxx/include/variant b/libcxx/include/variant
index 631ffceab5f68..41c946fc96727 100644
--- a/libcxx/include/variant
+++ b/libcxx/include/variant
@@ -261,6 +261,10 @@ namespace std {
#include <new>
#include <version>
+#if __has_feature(memory_sanitizer)
+#include <sanitizer/msan_interface.h>
+#endif
+
// standard-mandated includes
// [variant.syn]
@@ -781,6 +785,9 @@ _LIBCPP_VARIANT_DESTRUCTOR(
_Trait::_TriviallyAvailable,
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__dtor() = default,
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __destroy() noexcept {
+#if __has_feature(memory_sanitizer)
+ __sanitizer_dtor_callback(&this->__data, sizeof(this->__data));
+#endif
this->__index = __variant_npos<__index_t>;
} _LIBCPP_EAT_SEMICOLON);
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
In the version of `__destroy` that does not destruct any part of
`__data`, MSAN is unable to see that `__data` is logically
uninitialized. This allows false negatives such as the following to
run without any MSAN diagnostic.
```
std::variant<double, int> v;
v.emplace<double>();
double& d = std::get<double>(v);
v.emplace<int>();
if (d) ...
```
With these changes, MSAN reports uninitialized memory:
```
==32202==WARNING: MemorySanitizer: use-of-uninitialized-value
#0 0x5557b64820aa in main /home/ccotter/variant_msan.cpp:19:9
llvm#1 0x7fbfacd88554 in __libc_start_main /usr/src/debug/glibc-2.17-c758a686/csu/../csu/libc-start.c:266
llvm#2 0x5557b63ed40d in _start (/home/ccotter/a.out+0x3140d)
```
|
@ldionne could you help me find a reviewer for this? I've not contributed to libcxx before. Thanks! |
|
Couldn't you just call the destructor? |
|
I don't think there is a destructor to call in this case as the type is trivial, so I didn't think that it was semantically correct to call the dtor in this case, unless you think it's appropriate even for the trivial case (although I'd be happy to try that if that's your preference). The destructor for the |
There is no destructor to call, but it's perfectly valid to call a destructor on such types: https://godbolt.org/z/qWjzvPYjW. It's called a pseudo-destructor for these cases. I think this would be a better alternative, since it avoids any MSan-specific code and makes it clear to any tooling that the lifetime of the object has ended. |
|
I agree with @philnik777 here, I think this improvement makes a lot of sense, but we'd want to implement it without referring to explicit sanitizer APIs. |
|
In my testing this will need an enhancement on the MSAN side too to understand that |
|
Gentle ping @vitalybuka. IMO the desired outcome for libc++ is pretty clear, it would be nice if MSAN's behavior could be aligned with it. Said differently, our code would also be valid if we already called the destructor inside |
I guess is intentional, but msan can be improved. As-is __sanitizer_dtor_callback is a function call, so it can be too expensive. Maybe this can be done reasonably efficiently if we inline the call. BTW. libc++ uses |
|
I think we're on the same page, then. @ccotter Could you turn this patch into one that makes a call to the destructor instead? The remaining part of the underlying issue should be fixed from the MSAN side instead. |
|
Yes, that makes sense. thanks! |
|
@ccotter do you plan to continue working on this? If not, let's close this PR for now and file an issue about this instead. |
In the version of
__destroythat does not destruct any part of__data, MSAN is unable to see that__datais logically uninitialized. This allows false negatives such as the following to run without any MSAN diagnostic.With these changes, MSAN reports uninitialized memory:
I wasn't sure how to add a test for this just yet, but wanted to get the changes up first to review whether this change would make sense in libc++.