You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on May 3, 2026. It is now read-only.
The TryFrom<&[u8]> implementation for Bitmap<SIZE> (lines 137-148 in src/bitmap.rs) contains a memory safety vulnerability that leads to undefined behavior when the backing store type has validity constraints (e.g., bool for SIZE=1). The function performs unchecked memory copying followed by assume_init(), which violates Rust's safety invariants.
Root Cause
The current implementation copies arbitrary byte data into MaybeUninit and immediately calls assume_init() without validating that the byte pattern is valid for the target type:
When SIZE = 1, the backing store type <BitsImpl<1> as Bits>::Store is bool
The bool type in Rust has strict validity requirements: only bit patterns 0x00 and 0x01 are valid
Calling assume_init() on a MaybeUninit<bool> containing any other value (e.g., 0x02) is immediate undefined behavior, even before the value is used
This violates the safety contract of MaybeUninit:: assume_init(), which requires the memory to contain a valid instance of T
Proof of Concept
use bitmaps::Bitmap;fnmain(){// This compiles and triggers UB at runtimelet _ = Bitmap::<1>::try_from(&[2u8][..]).unwrap();// UB occurs during assume_init() due to invalid bool value}
Verification with Miri:
error: Undefined Behavior: constructing invalid value: encountered 0x02, but expected a boolean
--> /home/usr/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bitmaps-3.2.1/src/bitmap.rs:144:27
|
144 | data: data.assume_init(),
| ^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
Impact
Safe API exposing UB: This is a public safe function that allows users to trigger undefined behavior without using unsafe blocks
Compiler optimizations: UB can lead to unpredictable behavior due to compiler optimizations assuming valid values
Potential for exploitation: Invalid values may lead to unexpected control flow or security vulnerabilities in downstream code
Proposed Fix (Zero Runtime Cost)
Replace the unchecked memory copy with read_unaligned followed by explicit validation:
fntry_from(value:&[u8]) -> Result<Self,Self::Error>{if value.len() == size_of::<<BitsImpl<SIZE>asBits>::Store>(){// Read the bytes as the target type (may contain invalid bit patterns)let candidate = unsafe{
value.as_ptr().cast::<<BitsImpl<SIZE>asBits>::Store>().read_unaligned()};// Validate by round-tripping through as_bytes()// This is optimized away by the compiler at -O2/-O3let temp = Self{data: candidate };if temp.as_bytes() == value {Ok(temp)}else{Err(())}}else{Err(())}}
Alternative approach (more explicit):
fntry_from(value:&[u8]) -> Result<Self,Self::Error>{if value.len() != size_of::<<BitsImpl<SIZE>asBits>::Store>(){returnErr(());}// Use from_ne_bytes if available, or similar deserializationlet data = <BitsImpl<SIZE>asBits>::Store::from_bytes(value).ok_or(())?;Ok(Self{ data })}
Why This Fix Has Zero Runtime Cost
Compiler optimizations: At optimization levels -O2 and above, the round-trip validation compiles down to a no-op for types like u8, u16, u32, etc. that have no invalid bit patterns
Conditional validation: For types with validity constraints (like bool), the compiler generates efficient validation code (typically a single comparison)
The validation would be compiled away for most cases where the backing store is an integer type with no invalid bit patterns
Additional Notes
The same issue potentially affects AsMut<[u8]> (lines 117-129), which allows external code to write invalid bit patterns through the mutable slice reference
Consider adding documentation warnings about the validity requirements when using these byte-level APIs
The as_bytes() and as_mut() methods expose raw memory representation, which should be used with caution
Summary
The
TryFrom<&[u8]>implementation forBitmap<SIZE>(lines 137-148 insrc/bitmap.rs) contains a memory safety vulnerability that leads to undefined behavior when the backing store type has validity constraints (e.g.,boolforSIZE=1). The function performs unchecked memory copying followed byassume_init(), which violates Rust's safety invariants.Root Cause
The current implementation copies arbitrary byte data into
MaybeUninitand immediately callsassume_init()without validating that the byte pattern is valid for the target type:bitmaps/src/bitmap.rs
Lines 137 to 148 in b449cec
Why This Causes UB
SIZE = 1, the backing store type<BitsImpl<1> as Bits>::Storeisboolbooltype in Rust has strict validity requirements: only bit patterns0x00and0x01are validassume_init()on aMaybeUninit<bool>containing any other value (e.g.,0x02) is immediate undefined behavior, even before the value is usedMaybeUninit:: assume_init(), which requires the memory to contain a valid instance ofTProof of Concept
Verification with Miri:
Impact
unsafeblocksProposed Fix (Zero Runtime Cost)
Replace the unchecked memory copy with
read_unalignedfollowed by explicit validation:Alternative approach (more explicit):
Why This Fix Has Zero Runtime Cost
-O2and above, the round-trip validation compiles down to a no-op for types likeu8,u16,u32, etc. that have no invalid bit patternsbool), the compiler generates efficient validation code (typically a single comparison)Additional Notes
AsMut<[u8]>(lines 117-129), which allows external code to write invalid bit patterns through the mutable slice referenceas_bytes()andas_mut()methods expose raw memory representation, which should be used with cautionReferences
bool