[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <DAQI4RPK2Y7T.3TQ1G3IMZCNK4@kernel.org>
Date: Thu, 19 Jun 2025 14:17:26 +0200
From: "Benno Lossin" <lossin@...nel.org>
To: "Andreas Hindborg" <a.hindborg@...nel.org>
Cc: "Miguel Ojeda" <ojeda@...nel.org>, "Alex Gaynor"
<alex.gaynor@...il.com>, "Boqun Feng" <boqun.feng@...il.com>, "Gary Guo"
<gary@...yguo.net>, Björn Roy Baron
<bjorn3_gh@...tonmail.com>, "Alice Ryhl" <aliceryhl@...gle.com>, "Masahiro
Yamada" <masahiroy@...nel.org>, "Nathan Chancellor" <nathan@...nel.org>,
"Luis Chamberlain" <mcgrof@...nel.org>, "Danilo Krummrich"
<dakr@...nel.org>, "Nicolas Schier" <nicolas.schier@...ux.dev>, "Trevor
Gross" <tmgross@...ch.edu>, "Adam Bratschi-Kaye" <ark.email@...il.com>,
<rust-for-linux@...r.kernel.org>, <linux-kernel@...r.kernel.org>,
<linux-kbuild@...r.kernel.org>, "Petr Pavlu" <petr.pavlu@...e.com>, "Sami
Tolvanen" <samitolvanen@...gle.com>, "Daniel Gomez" <da.gomez@...sung.com>,
"Simona Vetter" <simona.vetter@...ll.ch>, "Greg KH"
<gregkh@...uxfoundation.org>, "Fiona Behrens" <me@...enk.dev>, "Daniel
Almeida" <daniel.almeida@...labora.com>, <linux-modules@...r.kernel.org>
Subject: Re: [PATCH v13 1/6] rust: str: add radix prefixed integer parsing
functions
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];
Powered by blists - more mailing lists