mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Benno Lossin" <lossin@kernel.org>
To: "Andreas Hindborg" <a.hindborg@kernel.org>
Cc: "Miguel Ojeda" <ojeda@kernel.org>,
	"Alex Gaynor" <alex.gaynor@gmail.com>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Masahiro Yamada" <masahiroy@kernel.org>,
	"Nathan Chancellor" <nathan@kernel.org>,
	"Luis Chamberlain" <mcgrof@kernel.org>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"Nicolas Schier" <nicolas.schier@linux.dev>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Adam Bratschi-Kaye" <ark.email@gmail.com>,
	rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-kbuild@vger.kernel.org, "Petr Pavlu" <petr.pavlu@suse.com>,
	"Sami Tolvanen" <samitolvanen@google.com>,
	"Daniel Gomez" <da.gomez@samsung.com>,
	"Simona Vetter" <simona.vetter@ffwll.ch>,
	"Greg KH" <gregkh@linuxfoundation.org>,
	"Fiona Behrens" <me@kloenk.dev>,
	"Daniel Almeida" <daniel.almeida@collabora.com>,
	linux-modules@vger.kernel.org
Subject: Re: [PATCH v13 1/6] rust: str: add radix prefixed integer parsing functions
Date: Thu, 19 Jun 2025 14:17:26 +0200	[thread overview]
Message-ID: <DAQI4RPK2Y7T.3TQ1G3IMZCNK4@kernel.org> (raw)
In-Reply-To: <871prg7zoh.fsf@kernel.org>

On Thu Jun 19, 2025 at 1:12 PM CEST, Andreas Hindborg wrote:
> I'm having a difficult time parsing. Are you suggesting that we guard
> against implementations of `TryInto<u64>` that misbehave?

Let me try a different explanation:

The safety requirement for implementing the `FromStrRadix`:

    /// The member functions of this trait must be implemented according to
    /// their documentation.

Together with the functions of the trait:

    /// Parse `src` to [`Self`] using radix `radix`.
    fn from_str_radix(src: &BStr, radix: u32) -> Result<Self, crate::error::Error>;
    
    /// Return the absolute value of [`Self::MIN`].
    fn abs_min() -> u64;
    
    /// Perform bitwise 2's complement on `self`.
    ///
    /// Note: This function does not make sense for unsigned integers.
    fn complement(self) -> Self;

Doesn't make sense. What does it mean to return the "absolute value of
[`Self::MIN`]"? We don't have "absolute value" defined for an arbitrary
type. Similarly for `complement` and `from_str_radix`, what does "Parse
`src` to [`Self`] using radex `radix`" mean? It's not well-defined.

You use this safety requirement in the parsing branch for negative
numbers (the `unsafe` call at the bottom):

    [b'-', rest @ ..] => {
        let (radix, digits) = strip_radix(rest.as_ref());
        // 2's complement values range from -2^(b-1) to 2^(b-1)-1.
        // So if we want to parse negative numbers as positive and
        // later multiply by -1, we have to parse into a larger
        // integer. We choose `u64` as sufficiently large.
        //
        // NOTE: 128 bit integers are not available on all
        // platforms, hence the choice of 64 bits.
        let val =
            u64::from_str_radix(core::str::from_utf8(digits).map_err(|_| EINVAL)?, radix)
                .map_err(|_| EINVAL)?;
    
        if val > Self::abs_min() {
            return Err(EINVAL);
        }
    
        if val == Self::abs_min() {
            return Ok(Self::MIN);
        }
    
        // SAFETY: We checked that `val` will fit in `Self` above.
        let val: Self = unsafe { val.try_into().unwrap_unchecked() };
    
        Ok(val.complement())
    }

But you don't mention that the check is valid due to the safety
requirements of implementing `FromStrRadix`. But even if you did, that
wouldn't mean anything as I explained above.

So let's instead move all of this negation & u64 conversion logic into
the `FromStrRadix` trait. Then it can be safe & the `ParseInt::from_str`
function doesn't use `unsafe` (there still will be `unsafe` in the
macro, but that is fine, as it's more local and knows the concrete
types).

---
Cheers,
Benno

Here is what I have in mind:

diff --git a/rust/kernel/str/parse_int.rs b/rust/kernel/str/parse_int.rs
index 0754490aec4b..9d6e146c5ea7 100644
--- a/rust/kernel/str/parse_int.rs
+++ b/rust/kernel/str/parse_int.rs
@@ -13,32 +13,16 @@
 // `ParseInt`, that is, prevents downstream users from implementing the
 // trait.
 mod private {
+    use crate::prelude::*;
     use crate::str::BStr;
 
     /// Trait that allows parsing a [`&BStr`] to an integer with a radix.
-    ///
-    /// # Safety
-    ///
-    /// The member functions of this trait must be implemented according to
-    /// their documentation.
-    ///
-    /// [`&BStr`]: kernel::str::BStr
-    // This is required because the `from_str_radix` function on the primitive
-    // integer types is not part of any trait.
-    pub unsafe trait FromStrRadix: Sized {
-        /// The minimum value this integer type can assume.
-        const MIN: Self;
-
+    pub trait FromStrRadix: Sized {
         /// Parse `src` to [`Self`] using radix `radix`.
-        fn from_str_radix(src: &BStr, radix: u32) -> Result<Self, crate::error::Error>;
-
-        /// Return the absolute value of [`Self::MIN`].
-        fn abs_min() -> u64;
+        fn from_str_radix(src: &BStr, radix: u32) -> Result<Self>;
 
-        /// Perform bitwise 2's complement on `self`.
-        ///
-        /// Note: This function does not make sense for unsigned integers.
-        fn complement(self) -> Self;
+        /// Tries to convert `value` into [`Self`] and negates the resulting value.
+        fn from_u64_negated(value: u64) -> Result<Self>;
     }
 }
 
@@ -108,19 +92,7 @@ fn from_str(src: &BStr) -> Result<Self> {
                 let val =
                     u64::from_str_radix(core::str::from_utf8(digits).map_err(|_| EINVAL)?, radix)
                         .map_err(|_| EINVAL)?;
-
-                if val > Self::abs_min() {
-                    return Err(EINVAL);
-                }
-
-                if val == Self::abs_min() {
-                    return Ok(Self::MIN);
-                }
-
-                // SAFETY: We checked that `val` will fit in `Self` above.
-                let val: Self = unsafe { val.try_into().unwrap_unchecked() };
-
-                Ok(val.complement())
+                Self::from_u64_negated(val)
             }
             _ => {
                 let (radix, digits) = strip_radix(src);
@@ -131,41 +103,49 @@ fn from_str(src: &BStr) -> Result<Self> {
 }
 
 macro_rules! impl_parse_int {
-    ($ty:ty) => {
-        // SAFETY: We implement the trait according to the documentation.
-        unsafe impl private::FromStrRadix for $ty {
-            const MIN: Self = <$ty>::MIN;
-
-            fn from_str_radix(src: &BStr, radix: u32) -> Result<Self, crate::error::Error> {
-                <$ty>::from_str_radix(core::str::from_utf8(src).map_err(|_| EINVAL)?, radix)
-                    .map_err(|_| EINVAL)
-            }
-
-            fn abs_min() -> u64 {
-                #[allow(unused_comparisons)]
-                if Self::MIN < 0 {
-                    1u64 << (Self::BITS - 1)
-                } else {
-                    0
+    ($($ty:ty),*) => {
+        $(
+            impl private::FromStrRadix for $ty {
+                fn from_str_radix(src: &BStr, radix: u32) -> Result<Self> {
+                    <$ty>::from_str_radix(core::str::from_utf8(src).map_err(|_| EINVAL)?, radix)
+                        .map_err(|_| EINVAL)
                 }
-            }
 
-            fn complement(self) -> Self {
-                (!self).wrapping_add((1 as $ty))
+                fn from_u64_negated(value: u64) -> Result<Self> {
+                    const ABS_MIN: u64 = {
+                        #[allow(unused_comparisons)]
+                        if <$ty>::MIN < 0 {
+                            1u64 << (Self::BITS - 1)
+                        } else {
+                            0
+                        }
+                    };
+
+                    fn complement(self) -> Self {
+                        (!self).wrapping_add((1 as $ty))
+                    }
+                    if val > ABS_MIN {
+                        return Err(EINVAL);
+                    }
+
+                    if val == ABS_MIN {
+                        return Ok(<$ty>::MIN);
+                    }
+
+                    // SAFETY: The above checks guarantee that `val` fits into `Self`:
+                    // - if `Self` is unsigned, then `ABS_MIN == 0` and thus we have returned above
+                    //   (either `EINVAL` or `MIN`).
+                    // - if `Self` is signed, then we have that `0 <= val < ABS_MIN`. And since
+                    //   `ABS_MIN - 1` fits into `Self` by construction, `val` also does.
+                    let val: Self = unsafe { val.try_into().unwrap_unchecked() };
+
+                    Ok((!val).wrapping_add(1))
+                }
             }
-        }
 
-        impl ParseInt for $ty {}
+            impl ParseInt for $ty {}
+        )*
     };
 }
 
-impl_parse_int!(i8);
-impl_parse_int!(u8);
-impl_parse_int!(i16);
-impl_parse_int!(u16);
-impl_parse_int!(i32);
-impl_parse_int!(u32);
-impl_parse_int!(i64);
-impl_parse_int!(u64);
-impl_parse_int!(isize);
-impl_parse_int!(usize);
+impl_parse_int![i8, u8, i16, u16, i32, u32, i64, u64, isize, usize];

  reply	other threads:[~2025-06-19 12:17 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-06-12 13:40 [PATCH v13 0/6] rust: extend `module!` macro with integer parameter support Andreas Hindborg
2025-06-12 13:40 ` [PATCH v13 1/6] rust: str: add radix prefixed integer parsing functions Andreas Hindborg
2025-06-18 20:38   ` Benno Lossin
2025-06-19 11:12     ` Andreas Hindborg
2025-06-19 12:17       ` Benno Lossin [this message]
2025-06-19 12:41         ` Andreas Hindborg
2025-06-12 13:40 ` [PATCH v13 2/6] rust: introduce module_param module Andreas Hindborg
2025-06-18 20:59   ` Benno Lossin
2025-06-19 12:20     ` Andreas Hindborg
2025-06-19 12:55       ` Benno Lossin
2025-06-20 10:31         ` Andreas Hindborg
2025-06-19 13:15   ` Benno Lossin
2025-06-20 11:29     ` Andreas Hindborg
2025-06-20 11:52       ` Andreas Hindborg
2025-06-20 12:28       ` Benno Lossin
2025-06-23  9:44         ` Andreas Hindborg
2025-06-23 11:48           ` Benno Lossin
2025-06-23 12:37             ` Miguel Ojeda
2025-06-23 13:55               ` Benno Lossin
2025-06-23 14:31             ` Andreas Hindborg
2025-06-23 15:20               ` Benno Lossin
2025-06-24 11:57                 ` Andreas Hindborg
2025-06-27  7:57                   ` Andreas Hindborg
2025-06-27  8:23                     ` Benno Lossin
2025-06-30 11:18                       ` Andreas Hindborg
2025-06-30 12:27                         ` Benno Lossin
2025-06-30 13:15                           ` Andreas Hindborg
2025-06-30 19:02                             ` Benno Lossin
2025-07-01  8:43                               ` Andreas Hindborg
2025-07-01  9:05                                 ` Benno Lossin
2025-07-01 14:14                                   ` Andreas Hindborg
2025-07-01 15:43                                     ` Benno Lossin
2025-07-01 16:27                                       ` Miguel Ojeda
2025-07-01 16:54                                         ` Benno Lossin
2025-07-02  8:30                                           ` Andreas Hindborg
2025-07-02  8:26                                         ` Andreas Hindborg
2025-07-02 10:01                                           ` Benno Lossin
2025-07-02  7:56                                       ` Andreas Hindborg
2025-06-23  9:47         ` Andreas Hindborg
2025-06-12 13:40 ` [PATCH v13 3/6] rust: module: use a reference in macros::module::module Andreas Hindborg
2025-06-18 20:07   ` Benno Lossin
2025-06-12 13:40 ` [PATCH v13 4/6] rust: module: update the module macro with module parameter support Andreas Hindborg
2025-06-18 21:07   ` Benno Lossin
2025-06-19 12:31     ` Andreas Hindborg
2025-06-12 13:40 ` [PATCH v13 5/6] rust: samples: add a module parameter to the rust_minimal sample Andreas Hindborg
2025-06-18 19:48   ` Benno Lossin
2025-06-30 11:30   ` Danilo Krummrich
2025-06-30 12:12     ` Andreas Hindborg
2025-06-30 12:18       ` Danilo Krummrich
2025-06-30 12:23         ` Danilo Krummrich
2025-06-30 12:31           ` Benno Lossin
2025-06-12 13:40 ` [PATCH v13 6/6] modules: add rust modules files to MAINTAINERS Andreas Hindborg

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=DAQI4RPK2Y7T.3TQ1G3IMZCNK4@kernel.org \
    --to=lossin@kernel.org \
    --cc=a.hindborg@kernel.org \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=ark.email@gmail.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=da.gomez@samsung.com \
    --cc=dakr@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=gary@garyguo.net \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kbuild@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-modules@vger.kernel.org \
    --cc=masahiroy@kernel.org \
    --cc=mcgrof@kernel.org \
    --cc=me@kloenk.dev \
    --cc=nathan@kernel.org \
    --cc=nicolas.schier@linux.dev \
    --cc=ojeda@kernel.org \
    --cc=petr.pavlu@suse.com \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=samitolvanen@google.com \
    --cc=simona.vetter@ffwll.ch \
    --cc=tmgross@umich.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®