Skip to content

Commit 44cc5d5

Browse files
authored
Rollup merge of rust-lang#64891 - SimonSapin:vec-of-fat-raw-ptr, r=sfackler
Fix `vec![x; n]` with null raw fat pointer zeroing the pointer metadata rust-lang#49496 introduced specialization based on: ```rust unsafe impl<T: ?Sized> IsZero for *mut T { fn is_zero(&self) -> bool { (*self).is_null() } } ``` … to call `RawVec::with_capacity_zeroed` for creating `Vec<*mut T>`, which is incorrect for fat pointers since `<*mut T>::is_null` only looks at the data component. That is, a fat pointer can be “null” without being made entirely of zero bits. This commit fixes it by removing the `?Sized` bound on this impl (and the corresponding `*const T` one). This regresses `vec![x; n]` with `x` a null raw slice of length zero, but that seems exceptionally uncommon. (Vtable pointers are never null, so raw trait objects would not take the fast path anyway.) An alternative to keep the `?Sized` bound (or even generalize to `impl<U: Copy> IsZero for U`) would be to cast to `&[u8]` of length `size_of::<U>()`, but the optimizer seems not to be able to propagate alignment information and sticks with comparing one byte at a time: https://rust.godbolt.org/z/xQFkwL ---- Without the library change, the new test fails as follows: ```rust ---- vec::vec_macro_repeating_null_raw_fat_pointer stdout ---- [src/liballoc/tests/vec.rs:1301] ptr_metadata(raw_dyn) = 0x00005596ef95f9a8 [src/liballoc/tests/vec.rs:1306] ptr_metadata(vec[0]) = 0x0000000000000000 thread 'vec::vec_macro_repeating_null_raw_fat_pointer' panicked at 'assertion failed: vec[0] == null_raw_dyn', src/liballoc/tests/vec.rs:1307:5 ```
2 parents 6e270ec + ce60da4 commit 44cc5d5

File tree

2 files changed

+50
-2
lines changed

2 files changed

+50
-2
lines changed

Diff for: src/liballoc/tests/vec.rs

+48
Original file line numberDiff line numberDiff line change
@@ -1281,3 +1281,51 @@ fn test_stable_push_pop() {
12811281
v.pop().unwrap();
12821282
assert_eq!(*v0, 13);
12831283
}
1284+
1285+
// https://github.com/rust-lang/rust/pull/49496 introduced specialization based on:
1286+
//
1287+
// ```
1288+
// unsafe impl<T: ?Sized> IsZero for *mut T {
1289+
// fn is_zero(&self) -> bool {
1290+
// (*self).is_null()
1291+
// }
1292+
// }
1293+
// ```
1294+
//
1295+
// … to call `RawVec::with_capacity_zeroed` for creating `Vec<*mut T>`,
1296+
// which is incorrect for fat pointers since `<*mut T>::is_null` only looks at the data component.
1297+
// That is, a fat pointer can be “null” without being made entirely of zero bits.
1298+
#[test]
1299+
fn vec_macro_repeating_null_raw_fat_pointer() {
1300+
let raw_dyn = &mut (|| ()) as &mut dyn Fn() as *mut dyn Fn();
1301+
let vtable = dbg!(ptr_metadata(raw_dyn));
1302+
let null_raw_dyn = ptr_from_raw_parts(std::ptr::null_mut(), vtable);
1303+
assert!(null_raw_dyn.is_null());
1304+
1305+
let vec = vec![null_raw_dyn; 1];
1306+
dbg!(ptr_metadata(vec[0]));
1307+
assert!(vec[0] == null_raw_dyn);
1308+
1309+
// Polyfill for https://github.com/rust-lang/rfcs/pull/2580
1310+
1311+
fn ptr_metadata(ptr: *mut dyn Fn()) -> *mut () {
1312+
unsafe {
1313+
std::mem::transmute::<*mut dyn Fn(), DynRepr>(ptr).vtable
1314+
}
1315+
}
1316+
1317+
fn ptr_from_raw_parts(data: *mut (), vtable: *mut()) -> *mut dyn Fn() {
1318+
unsafe {
1319+
std::mem::transmute::<DynRepr, *mut dyn Fn()>(DynRepr {
1320+
data,
1321+
vtable
1322+
})
1323+
}
1324+
}
1325+
1326+
#[repr(C)]
1327+
struct DynRepr {
1328+
data: *mut (),
1329+
vtable: *mut (),
1330+
}
1331+
}

Diff for: src/liballoc/vec.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1734,14 +1734,14 @@ impl_is_zero!(char, |x| x == '\0');
17341734
impl_is_zero!(f32, |x: f32| x.to_bits() == 0);
17351735
impl_is_zero!(f64, |x: f64| x.to_bits() == 0);
17361736

1737-
unsafe impl<T: ?Sized> IsZero for *const T {
1737+
unsafe impl<T> IsZero for *const T {
17381738
#[inline]
17391739
fn is_zero(&self) -> bool {
17401740
(*self).is_null()
17411741
}
17421742
}
17431743

1744-
unsafe impl<T: ?Sized> IsZero for *mut T {
1744+
unsafe impl<T> IsZero for *mut T {
17451745
#[inline]
17461746
fn is_zero(&self) -> bool {
17471747
(*self).is_null()

0 commit comments

Comments
 (0)