Skip to content

Commit 8f68629

Browse files
authored
Rollup merge of rust-lang#98839 - 5225225:assert_transmute_copy_size, r=thomcc
Add assertion that `transmute_copy`'s U is not larger than T This is called out as a safety requirement in the docs, but because knowing this can be done at compile time and constant folded (just like the `align_of` branch is removed), we can just panic here. I've looked at the asm (using `cargo-asm`) of a function that both is correct and incorrect, and the panic is completely removed, or is unconditional, without needing build-std. I don't expect this to cause much breakage in the wild. I scanned through https://miri.saethlin.dev/ub for issues that would look like this (error: Undefined Behavior: memory access failed: alloc1768 has size 1, so pointer to 8 bytes starting at offset 0 is out-of-bounds), but couldn't find any. That doesn't rule out it happening in crates tested that fail earlier for some other reason, though, but it indicates that doing this is rare, if it happens at all. A crater run for this would need to be build and test, since this is a runtime thing. Also added a few more transmute_copy tests.
2 parents 8804161 + 5f5ca88 commit 8f68629

File tree

2 files changed

+42
-0
lines changed

2 files changed

+42
-0
lines changed

library/core/src/mem/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,8 @@ pub fn copy<T: Copy>(x: &T) -> T {
10401040
#[stable(feature = "rust1", since = "1.0.0")]
10411041
#[rustc_const_unstable(feature = "const_transmute_copy", issue = "83165")]
10421042
pub const unsafe fn transmute_copy<T, U>(src: &T) -> U {
1043+
assert!(size_of::<T>() >= size_of::<U>(), "cannot transmute_copy if U is larger than T");
1044+
10431045
// If U has a higher alignment requirement, src might not be suitably aligned.
10441046
if align_of::<U>() > align_of::<T>() {
10451047
// SAFETY: `src` is a reference which is guaranteed to be valid for reads.

library/core/tests/mem.rs

+40
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,46 @@ fn test_transmute_copy() {
9797
assert_eq!(1, unsafe { transmute_copy(&1) });
9898
}
9999

100+
#[test]
101+
fn test_transmute_copy_shrink() {
102+
assert_eq!(0_u8, unsafe { transmute_copy(&0_u64) });
103+
}
104+
105+
#[test]
106+
fn test_transmute_copy_unaligned() {
107+
#[repr(C)]
108+
#[derive(Default)]
109+
struct Unaligned {
110+
a: u8,
111+
b: [u8; 8],
112+
}
113+
114+
let u = Unaligned::default();
115+
assert_eq!(0_u64, unsafe { transmute_copy(&u.b) });
116+
}
117+
118+
#[test]
119+
#[cfg(panic = "unwind")]
120+
fn test_transmute_copy_grow_panics() {
121+
use std::panic;
122+
123+
let err = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe {
124+
let _unused: u64 = transmute_copy(&1_u8);
125+
}));
126+
127+
match err {
128+
Ok(_) => unreachable!(),
129+
Err(payload) => {
130+
payload
131+
.downcast::<&'static str>()
132+
.and_then(|s| {
133+
if *s == "cannot transmute_copy if U is larger than T" { Ok(s) } else { Err(s) }
134+
})
135+
.unwrap_or_else(|p| panic::resume_unwind(p));
136+
}
137+
}
138+
}
139+
100140
#[test]
101141
#[allow(dead_code)]
102142
fn test_discriminant_send_sync() {

0 commit comments

Comments
 (0)