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];
next prev parent 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®