Spotted by @8rsrf7
Affected versions: 0.9.3 and probably earlier versions
|
nz: unsafe { |
|
NonZeroU32::new_unchecked( |
|
((u32::from(hash.0) << 16) + index as u32).wrapping_add(1), |
|
) |
|
}, |
^ This code probably assumes that the case
index=u16::MAX and
hash=u16::MAX is statistically impossible but it's not actually impossible.
Here's a proof of concept that triggers the issue
// src/main.rs
use std::hash::{BuildHasher, Hasher};
use std::sync::atomic::{self, AtomicU16};
use heapless::IndexMap;
fn main() {
let mut map = IndexMap::<_, _, MyHasher, { 1 << 17 }>::default();
for x in 0..=u16::MAX {
map.insert(x, x).unwrap();
}
println!("{}", map.len());
}
#[derive(Default)]
struct MyHasher;
impl Hasher for MyHasher {
fn finish(&self) -> u64 {
static COUNT: AtomicU16 = AtomicU16::new(0);
COUNT.fetch_add(1, atomic::Ordering::Relaxed).into()
}
fn write(&mut self, _bytes: &[u8]) {}
}
impl BuildHasher for MyHasher {
type Hasher = MyHasher;
fn build_hasher(&self) -> Self::Hasher {
MyHasher
}
}
$ cargo run
thread 'main' (33377) panicked at /tmp/heapless-0.9.3/src/index_map.rs:109:9:
(65535, 65535)
# Cargo.toml
[dependencies]
heapless.path = "../heapless-0.9.3"
with this patch applied to heapless-0.9.3 to get a panic instead of UB.
diff --git a/src/index_map.rs b/src/index_map.rs
index cd6e30b..80b74cb 100644
--- a/src/index_map.rs
+++ b/src/index_map.rs
@@ -105,12 +105,10 @@ pub struct Pos {
impl Pos {
fn new(index: usize, hash: HashValue) -> Self {
+ let val = ((u32::from(hash.0) << 16) + index as u32).wrapping_add(1);
+ assert!(val != 0, "{:?}", (index, hash.0));
Self {
- nz: unsafe {
- NonZeroU32::new_unchecked(
- ((u32::from(hash.0) << 16) + index as u32).wrapping_add(1),
- )
- },
+ nz: unsafe { NonZeroU32::new_unchecked(val) },
}
}
One option to prevent this issue for certain would be to limit the capacity to <u16::MAX in the constructor with a (compile-time) assertion. Given that this library is commonly used on resource constrained devices, it's unlikely that people are using such large capacities.
Spotted by @8rsrf7
Affected versions: 0.9.3 and probably earlier versions
heapless/src/index_map.rs
Lines 109 to 113 in 4e9d494
^ This code probably assumes that the case
index=u16::MAXandhash=u16::MAXis statistically impossible but it's not actually impossible.Here's a proof of concept that triggers the issue
with this patch applied to
heapless-0.9.3to get a panic instead of UB.One option to prevent this issue for certain would be to limit the
capacityto<u16::MAXin the constructor with a (compile-time) assertion. Given that this library is commonly used on resource constrained devices, it's unlikely that people are using such large capacities.