[PATCH v4 1/2] rust: hrtimer: Restrict expires() to safe contexts
FUJITA Tomonori <[email protected]>
| Newsgroups | org.kernel.vger.rust-for-linux |
|---|---|
| Message-ID | <[email protected]> |
From: FUJITA Tomonori <[email protected]> HrTimer::expires() previously read node.expires via a volatile load, which can race with C-side updates. Rework the API so it is only callable with exclusive access or from the callback context. Introduce expires_unchecked() with an explicit safety contract, switch HrTimer::expires() to Pin<&mut Self>, add HrTimerCallbackContext::expires(), and route the read through hrtimer_get_expires() via a Rust helper. Fixes: 4b0147494275 ("rust: hrtimer: Add HrTimer::expires()") Closes: https://lore.kernel.org/rust-for-linux/[email protected]/ Signed-off-by: FUJITA Tomonori <[email protected]> --- v4 - Add a patch to make HrTimer repr(transparent) v3: https://lore.kernel.org/all/[email protected]/ - Change the signature of raw_expires() - Rename raw_expires() to expires_unchecked() v2: https://lore.kernel.org/rust-for-linux/[email protected]/ - Add Fixes and Closes tags - Fix and improve comments v1: https://lore.kernel.org/rust-for-linux/[email protected]/ --- rust/helpers/time.c | 6 +++++ rust/kernel/time/hrtimer.rs | 46 ++++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/rust/helpers/time.c b/rust/helpers/time.c index 32f495970493..ef8999621399 100644 --- a/rust/helpers/time.c +++ b/rust/helpers/time.c @@ -2,6 +2,7 @@ #include <linux/delay.h> #include <linux/ktime.h> +#include <linux/hrtimer.h> #include <linux/timekeeping.h> __rust_helper void rust_helper_fsleep(unsigned long usecs) @@ -38,3 +39,8 @@ __rust_helper void rust_helper_udelay(unsigned long usec) { udelay(usec); } + +__rust_helper ktime_t rust_helper_hrtimer_get_expires(const struct hrtimer *timer) +{ + return hrtimer_get_expires(timer); +} diff --git a/rust/kernel/time/hrtimer.rs b/rust/kernel/time/hrtimer.rs index 2d7f1131a813..1db84cd4cbe8 100644 --- a/rust/kernel/time/hrtimer.rs +++ b/rust/kernel/time/hrtimer.rs @@ -560,27 +560,36 @@ pub fn forward_now(self: Pin<&mut Self>, interval: Delta) -> u64 self.forward(HrTimerInstant::<T>::now(), interval) } + /// Return the time expiry for this [`HrTimer`]. + /// + /// # Safety + /// + /// The caller must either have exclusive access to `self`, or be within the context of the + /// timer callback. + #[inline] + unsafe fn expires_unchecked(&self) -> HrTimerInstant<T> + where + T: HasHrTimer<T>, + { + // SAFETY: + // - The C API requirements for this function are fulfilled by our safety contract. + // - Timers cannot have negative `ktime_t` values as their expiration time. + unsafe { Instant::from_ktime(bindings::hrtimer_get_expires(Self::raw_get(self))) } + } + /// Return the time expiry for this [`HrTimer`]. /// /// This value should only be used as a snapshot, as the actual expiry time could change after /// this function is called. - pub fn expires(&self) -> HrTimerInstant<T> + pub fn expires(self: Pin<&mut Self>) -> HrTimerInstant<T> where T: HasHrTimer<T>, { - // SAFETY: `self` is an immutable reference and thus always points to a valid `HrTimer`. - let c_timer_ptr = unsafe { HrTimer::raw_get(self) }; + // SAFETY: `expires_unchecked` does not move `Self`. + let this = unsafe { self.get_unchecked_mut() }; - // SAFETY: - // - Timers cannot have negative ktime_t values as their expiration time. - // - There's no actual locking here, a racy read is fine and expected - unsafe { - Instant::from_ktime( - // This `read_volatile` is intended to correspond to a READ_ONCE call. - // FIXME(read_once): Replace with `read_once` when available on the Rust side. - core::ptr::read_volatile(&raw const ((*c_timer_ptr).node.expires)), - ) - } + // SAFETY: By existence of `Pin<&mut Self>`, we have exclusive access to `Self`. + unsafe { this.expires_unchecked() } } } @@ -1065,6 +1074,17 @@ pub fn forward(&mut self, now: HrTimerInstant<T>, interval: Delta) -> u64 { pub fn forward_now(&mut self, duration: Delta) -> u64 { self.forward(HrTimerInstant::<T>::now(), duration) } + + /// Return the time expiry for the timer. + /// + /// This function is identical to [`HrTimer::expires()`] except that it may only be used from + /// within the context of a [`HrTimer`] callback. + pub fn expires(&self) -> HrTimerInstant<T> { + // SAFETY: + // - We are guaranteed to be within the context of a timer callback by our type invariants. + // - By our type invariants, `self.0` always points to a valid `HrTimer<T>`. + unsafe { self.0.as_ref().expires_unchecked() } + } } /// Use to implement the [`HasHrTimer<T>`] trait. base-commit: dc01dfb37b34beeefcfe1c3055364d41a4070c7e -- 2.43.0