Skip to content
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

Prevent clobbering of data in Result's tail padding internally and externally #407

Merged
merged 9 commits into from
Nov 25, 2023

Conversation

danakj
Copy link
Collaborator

@danakj danakj commented Nov 20, 2023

llvm/llvm-project#70494 points out that [[no_unique_address]] on a union member can cause its placement-new construction to clobber data in its tail padding, as ctors can and do write their entire sizeof() size, not just their data-size.

So we do some wild things to prevent this from occurring:

The T and E union are grouped with the state into a struct, which we call Inner. This structure is always constructed together as a group when changing the state to Ok or Err. This ensures that we construct T or E and then set the state (which follows in the struct) thus avoiding clobbering of the state.

Second, the Inner struct needs to be protected from clobbering other things in its tail padding. So it is wrapped in another struct that conditionally applies [[no_unique_address]] to it, which we call MaybeNoUniqueAddress.

We mark Inner as [[no_unique_address]] only when there is no tail padding in T or E. Then the tail padding of Inner which is introduced by the state flag can be used by things external to the Result.

All of this is put into the outermost structure StorageVoid or StorageNonVoid for a void T or not, respectively. Everything is marked [[no_unique_address]] except Inner which is conditionally marked as mentioned above.

In order to do this, we've rewritten the entire implementation of Result's storage. In the process, copy/move operations and destructors are all trivial if the same for both T and E are trivial, which fixes #403.

The Clang 18 and GCC builds are failing due to the Clang-18 apt package not including libclang: llvm/llvm-project#73402

@danakj danakj added the bug Something isn't working label Nov 20, 2023
@danakj danakj force-pushed the expected-padding branch 6 times, most recently from e74ff33 to 817666b Compare November 21, 2023 03:19
danakj and others added 5 commits November 25, 2023 16:28
…ternally

llvm/llvm-project#70494 points out that `[[no_unique_address]]` on a
union member can cause its placement-new construction to clobber data in
its tail padding, as ctors can and do write their entire `sizeof()`
size, not just their data-size.

So we do some wild things to prevent this from occurring:

The `T` and `E` union are grouped with the state into a struct, which we
call `Inner`. This structure is always constructed together as a group
when changing the state to `Ok` or `Err`. This ensures that we construct
`T` or `E` and then set the state (which follows in the struct) thus
avoiding clobbering of the state.

Second, the `Inner` struct needs to be protected from clobbering other
things in its tail padding. So it is wrapped in another struct that
conditionally applies `[[no_unique_address]]` to it, which we call
`MaybeNoUniqueAddress`.

We mark `Inner` as `[[no_unique_address]]` only when there is no tail
padding in `T` or `E`. Then the tail padding of `Inner` which is
introduced by the state flag can be used by things external to the
Result if `T` are without tail padding.

All of this is put into the outermost structure `StorageVoid` or
`StorageNonVoid` for a void `T` or not, respectively. Everything is
marked `[[no_unique_address]]` except `Inner` which is conditionally
marked as mentioned above.

In order to do this, we've rewritten the entire implementation of
`Result`'s storage. In the process, copy/move operations and destructors
are all trivial if the same for both `T` and `E` are trivial, which
fixes chromium#403.
The constexpr keyword causes MSVC to miscompile and require the
method to be able to be instantiated even though it is removed
by a requires clause.
Update README to say so. Older MSVC crashes in Result now
also, so drop the older version.
MSVC has fixed the bug where [[msvc::no_unique_address]] on an array
would break its alignment. Previous MSVC versions can't even compile
Subspace so we don't need to worry about supporting them here.
@danakj danakj merged commit c59c89c into chromium:main Nov 25, 2023
7 of 8 checks passed
@danakj danakj deleted the expected-padding branch November 25, 2023 22:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ensure Result has trivial copy/move/destroy when T and E are both trivial
1 participant