Hi. I am experimenting with pointer tagging and have have a question about zero-sized-types.
I have noticed that when I create Box<T>
(or &T
) where T
is ZST, then inner raw pointer is always equal to that type's alignment.
#[repr(align(2))]
struct A2;
#[repr(align(16))]
struct A16;
#[repr(align(1024))]
struct A1024;
macro_rules! print_ptr {
($ty:expr) => {
let x = Box::new($ty);
println!("{x:p}");
let y = &$ty;
println!("{y:p}");
}
}
fn main() {
print_ptr!(());
print_ptr!(A2);
print_ptr!(A16);
print_ptr!(A1024);
}
prints
0x1
0x1
0x2
0x2
0x10
0x10
0x400
0x400
I know that Rust guarantees that pointers to ZST are aligned to their alignment. But does it guarantee that their value is the same as I have observed? And if it doesn't guarantee that, can I freely swap address of any pointer to ZST?
For example does following code contain any UB(s)?
fn main() {
let x = Box::new(());
// intentionally forget original pointer
let _ = Box::into_raw(x);
// let's guess what the address was.
// alignment is correct, so what could go wrong?
let raw: *mut () = 0x100 as _; // is this safe?
let unit_ref: &() = unsafe {
&*raw // is this safe?
};
let x = unsafe {
Box::from_raw(raw) // is this safe?
};
assert_eq!(*x, ());
drop(x);
}
And how does Strict Provenance play with any of this?
Use Case
If this all sounds like XY-problem, then this is a simplified version of my use-case.
I want to take Box<dyn Foo>
, turn it into raw pointer, then split *mut dyn Foo
into pointer to data and pointer to VTable, then I would like to store this pointer to VTable somewhere, and tag the data pointer using its lower bits. However problem arises when this data pointer points to ZST (with low alignment). Using VTable I can determine the size and alignment of original type, but I don't know what assumptions I can make about data pointer. Can I freely "forget it" and when I want to reconstruct the original dyn Foo
can I just produce any pointer with sufficient alignment and cast it into &
or Box
to dyn Foo
?
In this scenario I think I can preserve the provenance of the original pointer, so Strict Provenance should not be a problem here.