Transmute special-case doesn't take into consideration alignment or enum repr. #88290
Labels
A-layout
Area: Memory layout of types
C-bug
Category: This is a bug.
T-compiler
Relevant to the compiler team, which will review and decide on the PR/issue.
I was looking back on #61956 (comment) and since the current implementation hasn't changed since, I decided to try to and see if I can create an example.
And yes, here is a playground example showing three invalid
transmute
s, all stemming from different points in the code (two alignment-based and one regarding treating "non-null-optimized"enum
s as newtypes).What we special-case is a
transmute
fromOuter1(Pointer1(Inner1(T)))
toOuter2(Pointer2(Inner2(T)))
, where:T
is a generic param (or an associated type of one)T: ?Sized
, becauseT: Sized
would result inPointer(Inner(T))
having a known layout, even when the exact choice ofT
isn't knownInner{1,2}(T)
wrapT
in any number ofstruct
s, with any choice of prefix fieldsPointer(Inner(T))
containingT::Metadata
mattersOuter{1,2}(X)
are newtypesX
field and optionally some ZST fieldsOption
-likeenum
s here, if thePointer
type they wrap is non-null, but that shouldn't matter - it's effectively "desugaring" theenum
optimization to a newtypeenum
opted out of the optimization, which turns out we're not checkingNow, I confused myself with alignment originally, but what's important to note is that even if a newtype of
X
contains higher alignment ZSTs aroundX
, the only difference that will make is on the alignment (and size, through additional padding) of the whole newtype, not on the position ofX
in the newtype.Because of that,
Outer1
andOuter2
could have different alignments and that wouldn't change the fact that reading the pointer from one of them and writing it into the other would work, it's still at offset0
and has the same size.But that's not what we do, since
transmute
is not field-based, and if we copy the larger size, we're reading or writing more bytes than would be legal.Looking at the LLVM IR of my example,
test_align
does look like it's copying64
bytes, which should definitely be UB (but it might take some effort to cause a LLVM optimization to trigger).OTOH,
test_option_like
is even worse, since the pointer goes in the#[repr(C)] enum
tag, and the value inside theSome
is garbage, so running it in release mode crashes trying to print the resulting value.What's a bit sad is I feel like I remember seeing an
assert!
for equaltransmute
sizes in codegen, which would catch such a situation and turn it into an ICE, but I'm guessing it got removed?cc @nagisa
The text was updated successfully, but these errors were encountered: