lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251105002346.53119-1-dakr@kernel.org>
Date: Wed,  5 Nov 2025 01:22:48 +0100
From: Danilo Krummrich <dakr@...nel.org>
To: gregkh@...uxfoundation.org,
	rafael@...nel.org,
	ojeda@...nel.org,
	alex.gaynor@...il.com,
	boqun.feng@...il.com,
	gary@...yguo.net,
	bjorn3_gh@...tonmail.com,
	lossin@...nel.org,
	a.hindborg@...nel.org,
	aliceryhl@...gle.com,
	tmgross@...ch.edu,
	viro@...iv.linux.org.uk,
	brauner@...nel.org,
	jack@...e.cz,
	arnd@...db.de
Cc: rust-for-linux@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	linux-fsdevel@...r.kernel.org,
	Danilo Krummrich <dakr@...nel.org>,
	Alexandre Courbot <acourbot@...dia.com>
Subject: [PATCH 1/3] rust: fs: add a new type for file::Offset

Replace the existing file::Offset type alias with a new type.

Compared to a type alias, a new type allows for more fine grained
control over the operations that (semantically) make sense for a
specific type.

Cc: Alexander Viro <viro@...iv.linux.org.uk>
Cc: Christian Brauner <brauner@...nel.org>
Cc: Jan Kara <jack@...e.cz>
Reviewed-by: Alice Ryhl <aliceryhl@...gle.com>
Reviewed-by: Alexandre Courbot <acourbot@...dia.com>
Suggested-by: Miguel Ojeda <ojeda@...nel.org>
Link: https://github.com/Rust-for-Linux/linux/issues/1198
Signed-off-by: Danilo Krummrich <dakr@...nel.org>
---
 rust/kernel/debugfs/file_ops.rs |   4 +-
 rust/kernel/fs/file.rs          | 159 +++++++++++++++++++++++++++++++-
 rust/kernel/uaccess.rs          |   4 +-
 3 files changed, 159 insertions(+), 8 deletions(-)

diff --git a/rust/kernel/debugfs/file_ops.rs b/rust/kernel/debugfs/file_ops.rs
index 6c8928902a0b..8e45c15d3c90 100644
--- a/rust/kernel/debugfs/file_ops.rs
+++ b/rust/kernel/debugfs/file_ops.rs
@@ -261,7 +261,7 @@ extern "C" fn blob_read<T: BinaryWriter>(
     // SAFETY:
     // - `ppos` is a valid `file::Offset` pointer.
     // - We have exclusive access to `ppos`.
-    let pos: &mut file::Offset = unsafe { &mut *ppos };
+    let pos = unsafe { file::Offset::from_raw(ppos) };
 
     let mut writer = UserSlice::new(UserPtr::from_ptr(buf.cast()), count).writer();
 
@@ -316,7 +316,7 @@ extern "C" fn blob_write<T: BinaryReader>(
     // SAFETY:
     // - `ppos` is a valid `file::Offset` pointer.
     // - We have exclusive access to `ppos`.
-    let pos: &mut file::Offset = unsafe { &mut *ppos };
+    let pos = unsafe { file::Offset::from_raw(ppos) };
 
     let mut reader = UserSlice::new(UserPtr::from_ptr(buf.cast_mut().cast()), count).reader();
 
diff --git a/rust/kernel/fs/file.rs b/rust/kernel/fs/file.rs
index 23ee689bd240..655019f336d9 100644
--- a/rust/kernel/fs/file.rs
+++ b/rust/kernel/fs/file.rs
@@ -15,12 +15,163 @@
     sync::aref::{ARef, AlwaysRefCounted},
     types::{NotThreadSafe, Opaque},
 };
-use core::ptr;
+use core::{num::TryFromIntError, ptr};
 
-/// Primitive type representing the offset within a [`File`].
+/// Representation of an offset within a [`File`].
 ///
-/// Type alias for `bindings::loff_t`.
-pub type Offset = bindings::loff_t;
+/// Transparent wrapper around `bindings::loff_t`.
+#[repr(transparent)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
+pub struct Offset(pub bindings::loff_t);
+
+impl Offset {
+    /// The largest value that can be represented by this type.
+    pub const MAX: Self = Self(bindings::loff_t::MAX);
+
+    /// The smallest value that can be represented by this type.
+    pub const MIN: Self = Self(bindings::loff_t::MIN);
+
+    /// Create a mutable [`Offset`] reference from the raw `*mut bindings::loff_t`.
+    ///
+    /// # Safety
+    ///
+    /// - `offset` must be a valid pointer to a `bindings::loff_t`.
+    /// - The caller must guarantee exclusive access to `offset`.
+    #[inline]
+    pub const unsafe fn from_raw<'a>(offset: *mut bindings::loff_t) -> &'a mut Self {
+        // SAFETY: By the safety requirements of this function
+        // - `offset` is a valid pointer to a `bindings::loff_t`,
+        // - we have exclusive access to `offset`.
+        unsafe { &mut *offset.cast() }
+    }
+
+    /// Returns `true` if the [`Offset`] is negative.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use kernel::fs::file::Offset;
+    ///
+    /// let offset = Offset::from(1);
+    /// assert!(!offset.is_negative());
+    ///
+    /// let offset = Offset::from(-1);
+    /// assert!(offset.is_negative());
+    /// ```
+    #[inline]
+    pub const fn is_negative(self) -> bool {
+        self.0.is_negative()
+    }
+
+    /// Saturating addition with another [`Offset`].
+    #[inline]
+    pub fn saturating_add(self, rhs: Offset) -> Offset {
+        Self(self.0.saturating_add(rhs.0))
+    }
+
+    /// Saturating addition with a [`usize`].
+    ///
+    /// If the [`usize`] fits in `bindings::loff_t` it is converted and added; otherwise the result
+    /// saturates to [`Offset::MAX`].
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use kernel::fs::file::Offset;
+    ///
+    /// let offset = Offset::from(40);
+    ///
+    /// let offset = offset.saturating_add_usize(2);
+    /// assert_eq!(offset, Offset::from(42));
+    ///
+    /// let offset = Offset::MAX.saturating_sub_usize(1);
+    /// let offset = offset.saturating_add_usize(usize::MAX);
+    /// assert_eq!(offset, Offset::MAX);
+    /// ```
+    pub fn saturating_add_usize(self, rhs: usize) -> Offset {
+        match bindings::loff_t::try_from(rhs) {
+            Ok(rhs_loff) => Self(self.0.saturating_add(rhs_loff)),
+            Err(_) => Self::MAX,
+        }
+    }
+
+    /// Saturating subtraction with another [`Offset`].
+    #[inline]
+    pub fn saturating_sub(self, rhs: Offset) -> Offset {
+        Offset(self.0.saturating_sub(rhs.0))
+    }
+
+    /// Saturating subtraction with a [`usize`].
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use kernel::fs::file::Offset;
+    ///
+    /// let offset = Offset::from(100);
+    /// let offset = offset.saturating_sub_usize(58);
+    /// assert_eq!(offset, Offset::from(42));
+    ///
+    /// let offset = Offset::MIN.saturating_add_usize(1);
+    /// let offset = offset.saturating_sub_usize(usize::MAX);
+    /// assert_eq!(offset, Offset::MIN);
+    /// ```
+    #[inline]
+    pub fn saturating_sub_usize(self, rhs: usize) -> Offset {
+        match bindings::loff_t::try_from(rhs) {
+            Ok(rhs_loff) => Offset(self.0.saturating_sub(rhs_loff)),
+            Err(_) => Self::MIN,
+        }
+    }
+}
+
+impl core::ops::Add<isize> for Offset {
+    type Output = Offset;
+
+    #[inline]
+    fn add(self, rhs: isize) -> Offset {
+        Offset(self.0 + rhs as bindings::loff_t)
+    }
+}
+
+impl core::ops::AddAssign<isize> for Offset {
+    #[inline]
+    fn add_assign(&mut self, rhs: isize) {
+        self.0 += rhs as bindings::loff_t;
+    }
+}
+
+impl From<bindings::loff_t> for Offset {
+    #[inline]
+    fn from(v: bindings::loff_t) -> Self {
+        Self(v)
+    }
+}
+
+impl From<Offset> for bindings::loff_t {
+    #[inline]
+    fn from(offset: Offset) -> Self {
+        offset.0
+    }
+}
+
+impl TryFrom<usize> for Offset {
+    type Error = TryFromIntError;
+
+    #[inline]
+    fn try_from(u: usize) -> Result<Self, Self::Error> {
+        Ok(Self(bindings::loff_t::try_from(u)?))
+    }
+}
+
+impl TryFrom<Offset> for usize {
+    type Error = TryFromIntError;
+
+    #[inline]
+    fn try_from(offset: Offset) -> Result<Self, Self::Error> {
+        usize::try_from(offset.0)
+    }
+}
 
 /// Flags associated with a [`File`].
 pub mod flags {
diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs
index f989539a31b4..7bfc212e78d1 100644
--- a/rust/kernel/uaccess.rs
+++ b/rust/kernel/uaccess.rs
@@ -325,7 +325,7 @@ pub fn read_slice_file(&mut self, out: &mut [u8], offset: &mut file::Offset) ->
         let read = self.read_slice_partial(out, offset_index)?;
 
         // OVERFLOW: `offset + read <= data.len() <= isize::MAX <= Offset::MAX`
-        *offset += read as i64;
+        *offset += read as isize;
 
         Ok(read)
     }
@@ -518,7 +518,7 @@ pub fn write_slice_file(&mut self, data: &[u8], offset: &mut file::Offset) -> Re
         let written = self.write_slice_partial(data, offset_index)?;
 
         // OVERFLOW: `offset + written <= data.len() <= isize::MAX <= Offset::MAX`
-        *offset += written as i64;
+        *offset += written as isize;
 
         Ok(written)
     }

base-commit: f656279afde16afee3ac163b90584ddceacb4e61
-- 
2.51.2


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ