On Mon, Aug 10, 2026 at 05:34:10PM +0900, Eliot Courtney wrote:
> It is currently possible to construct a non-`BitmapVec` backed
> `Bitmap` using `Bitmap::from_raw` that is larger than `i32::MAX`, and
> it is not part of the unsafe requirements. Restricting all bitmaps
> (even non-`BitmapVec` backed ones) to a maximum size of `i32::MAX`
> simplifies a few things and matches `BitmapVec::MAX_LEN`.
> 
> Add that requirement to the unsafe requirements on `Bitmap::from_raw`
> and `Bitmap::from_raw_mut`, and to the invariants on `Bitmap`.
> 
> This also fixes u32 casts truncating in `copy_and_extend`, which could
> otherwise lead to OOB writes.
> 
> Fixes: 11eca92a2cae ("rust: add bitmap API.")
> Link: https://lore.kernel.org/[email protected]
> Signed-off-by: Eliot Courtney <[email protected]>
> ---
>  rust/kernel/bitmap.rs | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs
> index a43bfe0ec3dc..0d481d761f2a 100644
> --- a/rust/kernel/bitmap.rs
> +++ b/rust/kernel/bitmap.rs
> @@ -17,6 +17,7 @@
>  /// # Invariants
>  ///
>  /// Must reference a `[c_ulong]` long enough to fit `data.len()` bits.
> +/// Must not be longer than `i32::MAX` bits.
>  #[cfg_attr(CONFIG_64BIT, repr(align(8)))]
>  #[cfg_attr(not(CONFIG_64BIT), repr(align(4)))]
>  pub struct Bitmap {
> @@ -30,11 +31,13 @@ impl Bitmap {
>      ///
>      /// * `ptr` holds a non-null address of an initialized array of 
> `unsigned long`
>      ///   that is large enough to hold `nbits` bits.
> +    /// * `nbits` must not exceed `i32::MAX`.
>      /// * the array must not be freed for the lifetime of this [`Bitmap`]
>      /// * concurrent access only happens through atomic operations
>      pub unsafe fn from_raw<'a>(ptr: *const usize, nbits: usize) -> &'a 
> Bitmap {
>          let data: *const [()] = core::ptr::slice_from_raw_parts(ptr.cast(), 
> nbits);
>          // INVARIANT: `data` references an initialized array that can hold 
> `nbits` bits.
> +        // INVARIANT: the caller guarantees that `nbits` does not exceed 
> `i32::MAX`.
>          // SAFETY:
>          // The caller guarantees that `data` (derived from `ptr` and `nbits`)
>          // points to a valid, initialized, and appropriately sized memory 
> region
> @@ -55,11 +58,13 @@ pub unsafe fn from_raw<'a>(ptr: *const usize, nbits: 
> usize) -> &'a Bitmap {
>      ///
>      /// * `ptr` holds a non-null address of an initialized array of 
> `unsigned long`
>      ///   that is large enough to hold `nbits` bits.
> +    /// * `nbits` must not exceed `i32::MAX`.
>      /// * the array must not be freed for the lifetime of this [`Bitmap`]
>      /// * no concurrent access may happen.
>      pub unsafe fn from_raw_mut<'a>(ptr: *mut usize, nbits: usize) -> &'a mut 
> Bitmap {
>          let data: *mut [()] = 
> core::ptr::slice_from_raw_parts_mut(ptr.cast(), nbits);
>          // INVARIANT: `data` references an initialized array that can hold 
> `nbits` bits.
> +        // INVARIANT: the caller guarantees that `nbits` does not exceed 
> `i32::MAX`.

Can you enforce it in code, instead of comments? Maybe under
CONFIG_RUST_BITMAP_HARDENED?

>          // SAFETY:
>          // The caller guarantees that `data` (derived from `ptr` and `nbits`)
>          // points to a valid, initialized, and appropriately sized memory 
> region
> @@ -415,7 +420,8 @@ pub fn clear_bit_atomic(&self, index: usize) {
>      #[inline]
>      pub fn copy_and_extend(&mut self, src: &Bitmap) {
>          let len = core::cmp::min(src.len(), self.len());
> -        // SAFETY: access to `self` and `src` is within bounds.
> +        // SAFETY: access to `self` and `src` is within bounds. Both lengths 
> fit in `u32`
> +        // because a `Bitmap` is at most `i32::MAX` bits, so the casts are 
> lossless.
>          unsafe {
>              bindings::bitmap_copy_and_extend(
>                  self.as_mut_ptr(),
> 
> -- 
> 2.55.0

Reply via email to