[PATCH] rust: num: reject bool conversion to signed one-bit Bounded

Younes Akhouayri <[email protected]>
Newsgroups org.kernel.feeds.b4-sent,org.kernel.vger.rust-for-linux,org.kernel.vger.stable
Message-ID <20260815-fix-rust-bounded-from-bool-submit-v1-1-882227bf40ba@younes.io>
From<bool> turns true into 1. A signed Bounded with N = 1 can hold
only -1 and 0. From<bool> therefore creates a value that the type does
not allow. Deref assumes the value is valid and calls
unreachable_unchecked() when it is not. Safe Rust can therefore reach
undefined behavior.

Rust cannot write N >= 2 directly in this From implementation. Add a
private BoolFits trait. It accepts every valid unsigned width and signed
widths from 2 through 128. The current Integer types do not use more
than 128 bits, so this keeps every valid conversion.

Fixes: 01e345e82ec3 ("rust: num: add Bounded integer wrapping type")
Closes: https://lore.kernel.org/rust-for-linux/[email protected]/
Cc: [email protected]
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Younes Akhouayri <[email protected]>
---
Do not allow From<bool> for signed Bounded values with N = 1.
Keep it available for signed Bounded values with N >= 2.
---
 rust/kernel/num/bounded.rs | 50 +++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 45 insertions(+), 5 deletions(-)

diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs
index d192610a687d..1856fe6c9865 100644
--- a/rust/kernel/num/bounded.rs
+++ b/rust/kernel/num/bounded.rs
@@ -13,7 +13,7 @@
 };
 
 use kernel::{
-    num::Integer,
+    num::{Integer, Signed, Unsigned},
     prelude::*, //
 };
 
@@ -174,13 +174,26 @@ fn fits_within<T: Integer>(value: T, num_bits: u32) -> bool {
 /// // `u8` (regardless of the passed value).
 /// // let _ = Bounded::<u32, 6>::from(10u8);
 ///
-/// // Booleans can be converted into single-bit `Bounded`s.
+/// // Booleans can be converted into unsigned single-bit `Bounded`s.
 ///
 /// let v = Bounded::<u64, 1>::from(false);
 /// assert_eq!(v.get(), 0);
 ///
 /// let v = Bounded::<u64, 1>::from(true);
 /// assert_eq!(v.get(), 1);
+///
+/// // Signed integers need at least two bits to represent both `0` and `1`.
+/// let v = Bounded::<i8, 2>::from(true);
+/// assert_eq!(v.get(), 1);
+/// ```
+///
+/// A signed single-bit [`Bounded`] cannot represent `1`, so converting a boolean into one does not
+/// build.
+///
+/// ```compile_fail,E0277
+/// use kernel::num::Bounded;
+///
+/// let _: Bounded<i8, 1> = true.into();
 /// ```
 ///
 /// Infallible conversions from a [`Bounded`] to a primitive integer are also supported, and
@@ -1109,7 +1122,33 @@ fn from(value: Bounded<T, N>) -> $type {
     i8 i16 i32 i64 isize
 );
 
-// Single-bit `Bounded`s can be converted from/to a boolean.
+// Conversions between `Bounded`s and booleans.
+
+/// Marker for signedness types for which a valid `N`-bit integer can represent a boolean.
+trait BoolFits<const N: u32> {}
+
+impl<const N: u32> BoolFits<N> for Unsigned {}
+
+macro_rules! impl_signed_bool_fits {
+    ($($num_bits:literal)*) => {
+        $(
+        impl BoolFits<$num_bits> for Signed {}
+        )*
+    };
+}
+
+// `N >= 2` cannot be expressed as a trait bound without `generic_const_exprs`, so enumerate every
+// width supported by the current `Integer` implementations.
+impl_signed_bool_fits!(
+    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+    17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
+    33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
+    49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
+    65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
+    81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
+    97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
+    113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
+);
 
 impl<T> From<Bounded<T, 1>> for bool
 where
@@ -1123,10 +1162,11 @@ fn from(value: Bounded<T, 1>) -> Self {
 impl<T, const N: u32> From<bool> for Bounded<T, N>
 where
     T: Integer + From<bool>,
+    T::Signedness: BoolFits<N>,
 {
     fn from(value: bool) -> Self {
-        // SAFETY: A boolean can be represented using a single bit, and thus fits within any
-        // integer type for any `N` > 0.
+        // SAFETY: `__new` enforces that `N` is a valid width, and the `BoolFits` bound guarantees
+        // that the integer representation of `value` fits within any such `N`.
         unsafe { Self::__new(T::from(value)) }
     }
 }

---
base-commit: 47f27155f17498fccb1f222f79089642337498a9
change-id: 20260815-fix-rust-bounded-from-bool-submit-3af836d8f718

Best regards,
--  
Younes Akhouayri <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.