diff options
author | Jan Kokemüller <jan.kokemueller@gmail.com> | 2024-01-22 15:05:39 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-22 09:05:39 -0500 |
commit | 4f4690530e8b40cdf3a17c76a352b26c2fb0446c (patch) | |
tree | c157c476049d6032b0a734ab92e73dbc6d6734e1 /libcxx/test/std/utilities/expected/expected.expected/swap/member.swap.pass.cpp | |
parent | bf7b8dae0615884816fff54cac08bc691746b1ee (diff) | |
download | llvm-4f4690530e8b40cdf3a17c76a352b26c2fb0446c.zip llvm-4f4690530e8b40cdf3a17c76a352b26c2fb0446c.tar.gz llvm-4f4690530e8b40cdf3a17c76a352b26c2fb0446c.tar.bz2 |
[libc++] Ensure that std::expected has no tail padding (#69673)
Currently std::expected can have some padding bytes in its tail due to
[[no_unique_address]]. Those padding bytes can be used by other objects.
For example, in the current implementation:
sizeof(std::expected<std::optional<int>, bool>) ==
sizeof(std::expected<std::expected<std::optional<int>, bool>, bool>)
As a result, the data layout of an
std::expected<std::expected<std::optional<int>, bool>, bool>
can look like this:
+-- optional "has value" flag
| +--padding
/---int---\ | |
00 00 00 00 01 00 00 00
| |
| +- "outer" expected "has value" flag
|
+- expected "has value" flag
This is problematic because `emplace()`ing the "inner" expected can not
only overwrite the "inner" expected "has value" flag (issue #68552) but
also the tail padding where other objects might live.
This patch fixes the problem by ensuring that std::expected has no tail
padding, which is achieved by conditional usage of [[no_unique_address]]
based on the tail padding that this would create.
This is an ABI breaking change because the following property changes:
sizeof(std::expected<std::optional<int>, bool>) <
sizeof(std::expected<std::expected<std::optional<int>, bool>, bool>)
Before the change, this relation didn't hold. After the change, the relation
does hold, which means that the size of std::expected in these cases increases
after this patch. The data layout will change in the following cases where
tail padding can be reused by other objects:
class foo : std::expected<std::optional<int>, bool> {
bool b;
};
or using [[no_unique_address]]:
struct foo {
[[no_unique_address]] std::expected<std::optional<int>, bool> e;
bool b;
};
The vendor communication is handled in #70820.
Fixes: #70494
Co-authored-by: philnik777 <nikolasklauser@berlin.de>
Co-authored-by: Louis Dionne <ldionne.2@gmail.com>
Diffstat (limited to 'libcxx/test/std/utilities/expected/expected.expected/swap/member.swap.pass.cpp')
-rw-r--r-- | libcxx/test/std/utilities/expected/expected.expected/swap/member.swap.pass.cpp | 22 |
1 files changed, 22 insertions, 0 deletions
diff --git a/libcxx/test/std/utilities/expected/expected.expected/swap/member.swap.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/swap/member.swap.pass.cpp index 3478230..f19599d 100644 --- a/libcxx/test/std/utilities/expected/expected.expected/swap/member.swap.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.expected/swap/member.swap.pass.cpp @@ -227,6 +227,28 @@ constexpr bool test() { } } + // CheckForInvalidWrites + { + { + CheckForInvalidWrites<true> x(std::unexpect); + CheckForInvalidWrites<true> y; + + x.swap(y); + + assert(x.check()); + assert(y.check()); + } + { + CheckForInvalidWrites<false> x(std::unexpect); + CheckForInvalidWrites<false> y; + + x.swap(y); + + assert(x.check()); + assert(y.check()); + } + } + return true; } |