-
Notifications
You must be signed in to change notification settings - Fork 148
Description
From the VirtAddr::try_new
docs:
This function tries to performs sign extension of bit 47 to make the address canonical. It succeeds if bits 48 to 64 are either a correct sign extension (i.e. copies of bit 47) or all null. Else, an error is returned.
This means that (surprisingly) you can provide a non-canonical address 0x00008123DEADBEEF
and, instead of try_new
failing, you get the VirtAddr
0xFFFF8123DEADBEEF
. As the try_new
implementation is used throughout VirtAddr
's methods, this means you get weird behavior like VirtAddr::new(addr).as_u64() == addr
and VirtAddr::from_ptr(ptr).as_ptr() == ptr
not necessarily being true, which is quite confusing.
It's especially confusing if you're working on a 5-level paging system where the virtual address space now has 57 bits. In that case, 0x0008123DEADBEEF
is a canonical virtual address. I would either expect VirtAddr::try_new
to work in this case (returning the passed in address) or fail (returning Err
, as this library doesn't have to support 5-level paging). What I wouldn't want is for the virtual address to be silently modified.
Issues related to this have come up in #298. One of the consequences of the current implementation is that adding an u64
to a VirtAddr
can "jump" the gap between the lower and upper half of the virtual address space. For example:
let x = VirtAddr::new(0x0000_7FFF_FFFF_FFFF);
let y = x + 5u64;
assert_eq!(y, VirtAddr::new(0xFFFF_8000_0000_0004));
assert_eq!(y - x, 0xFFFF_0000_0000_0005);
assert_ne!((x + 5u64) - x, 5u64);
// But subtraction doesn't "jump" the gap
let _ = y - 5u64; // This panics
which seems very unintuitive.
I would propose the following breaking change: VirtAddr::try_new
and VirtAddr::new
only succeed if the provided address is canonical and fail otherwise. This removes an unexpected foot-gun and simplifies the implementation.