From a32b239699377f09bba08b2e8ae0d167c1488b1f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 14 Feb 2025 12:06:13 +0100 Subject: rust: timer: wrap QEMUTimer with Opaque<> and express pinning requirements Timers must be pinned in memory, because modify() stores a pointer to them in the TimerList. To express this requirement, change init_full() to take a pinned reference. Because the only way to obtain a Timer is through Timer::new(), which is unsafe, modify() can assume that the timer it got was later initialized; and because the initialization takes a Pin<&mut Timer> modify() can assume that the timer is pinned. In the future the pinning requirement will be expressed through the pin_init crate instead. Note that Timer is a bit different from other users of Opaque, in that it is created in Rust code rather than C code. This is why it has to use the unsafe constructors provided by Opaque; and in fact Timer::new() is also unsafe, because it leaves it to the caller to invoke init_full() before modify(). Without a call to init_full(), modify() will cause a NULL pointer dereference. An alternative could be to combine new() + init_full() by returning a pinned box; however, using a reference makes it easier to express the requirement that the opaque outlives the timer. Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/hpet.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'rust/hw/timer/hpet/src') diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index 02c81ae..3d3d6ef 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -4,6 +4,7 @@ use std::{ ffi::CStr, + pin::Pin, ptr::{addr_of_mut, null_mut, NonNull}, slice::from_ref, }; @@ -184,7 +185,9 @@ impl HPETTimer { fn init(&mut self, index: usize, state: &HPETState) { *self = HPETTimer { index, - qemu_timer: Timer::new(), + // SAFETY: the HPETTimer will only be used after the timer + // is initialized below. + qemu_timer: unsafe { Timer::new() }, state: NonNull::new(state as *const _ as *mut _).unwrap(), config: 0, cmp: 0, @@ -195,7 +198,10 @@ impl HPETTimer { last: 0, }; - self.qemu_timer.init_full( + // SAFETY: HPETTimer is only used as part of HPETState, which is + // always pinned. + let qemu_timer = unsafe { Pin::new_unchecked(&mut self.qemu_timer) }; + qemu_timer.init_full( None, CLOCK_VIRTUAL, Timer::NS, -- cgit v1.1