From c94172314b85c0dcc5031e66095293ebeaeb3b45 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 16 May 2020 10:46:41 +0200 Subject: [PATCH] fix Nomicon transmute UB --- src/transmutes.md | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/transmutes.md b/src/transmutes.md index 9d47412..b7393e7 100644 --- a/src/transmutes.md +++ b/src/transmutes.md @@ -12,17 +12,29 @@ to have the same size. The ways to cause Undefined Behavior with this are mind boggling. * First and foremost, creating an instance of *any* type with an invalid state - is going to cause arbitrary chaos that can't really be predicted. + is going to cause arbitrary chaos that can't really be predicted. Do not + transmute `3` to `bool`. Even if you never *do* anything with the `bool`. Just + don't. * Transmute has an overloaded return type. If you do not specify the return type it may produce a surprising type to satisfy inference. -* Making a primitive with an invalid value is UB -* Transmuting between non-repr(C) types is UB -* Transmuting an & to &mut is UB - * Transmuting an & to &mut is *always* UB - * No you can't do it - * No you're not special +* Transmuting an & to &mut is UB. + * Transmuting an & to &mut is *always* UB. + * No you can't do it. + * No you're not special. * Transmuting to a reference without an explicitly provided lifetime produces an [unbounded lifetime] +* When transmuting between different compound types, you have to make sure they + are laid out the same way! If layouts differ, the wrong fields are going to + get filled with the wrong data, which will make you unhappy and can also be UB + (see above). + + So how do you know if the layouts are the same? For `repr(C)` types and + `repr(transparent)` types, layout is precisely defined. But for your + run-of-the-mill `repr(Rust)`, it is not. Even different instances of the same + generic type can have wildly different layout. `Vec` and `Vec` + *might* have their fields in the same order, or they might not. The details of + what exactly is and is not guaranteed for data layout are still being worked + out over [at the UCG WG][ucg-layout]. [`mem::transmute_copy`][transmute_copy] somehow manages to be *even more* wildly unsafe than this. It copies `size_of` bytes out of an `&T` and @@ -30,10 +42,12 @@ interprets them as a `U`. The size check that `mem::transmute` has is gone (as it may be valid to copy out a prefix), though it is Undefined Behavior for `U` to be larger than `T`. -Also of course you can get most of the functionality of these functions using -pointer casts. +Also of course you can get all of the functionality of these functions using raw +pointer casts or `union`s, but without any of the lints or other basic sanity +checks. Raw pointer casts and `union`s do not magically avoid the above rules. [unbounded lifetime]: unbounded-lifetimes.html [transmute]: ../std/mem/fn.transmute.html [transmute_copy]: ../std/mem/fn.transmute_copy.html +[ucg-layout]: https://rust-lang.github.io/unsafe-code-guidelines/layout.html