diff --git a/CHANGELOG.md b/CHANGELOG.md index 9862d9c..2ca9bac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## Unreleased +- Added metadata validation in `BitVectorData::from_bytes` to reject lengths + exceeding the stored capacity and covered the case with a regression test. - Guarded `CompactVector::from_bytes` against metadata bit-length overflow. - Removed the `anyhow` dependency in favor of crate-defined errors and updated `Serializable` to expose an associated error type for reconstruction. diff --git a/src/bit_vector/mod.rs b/src/bit_vector/mod.rs index 2b1d97d..3f29410 100644 --- a/src/bit_vector/mod.rs +++ b/src/bit_vector/mod.rs @@ -388,6 +388,17 @@ impl BitVectorData { /// Reconstructs the data from zero-copy [`Bytes`] and its metadata. pub fn from_bytes(meta: BitVectorDataMeta, bytes: Bytes) -> Result { let words = meta.handle.view(&bytes)?; + let capacity_bits = words + .as_ref() + .len() + .checked_mul(WORD_LEN) + .ok_or_else(|| Error::invalid_metadata("bit vector capacity overflow"))?; + if meta.len > capacity_bits { + return Err(Error::invalid_metadata(format!( + "bit vector length {} exceeds capacity {}", + meta.len, capacity_bits + ))); + } Ok(Self { words, len: meta.len, @@ -760,6 +771,23 @@ mod tests { assert_eq!(expected, other); } + #[test] + fn from_bytes_rejects_metadata_len_overflow() { + let mut area = ByteArea::new().unwrap(); + let mut sections = area.sections(); + let builder = BitVectorBuilder::with_capacity(WORD_LEN, &mut sections).unwrap(); + let data = builder.into_data(); + let mut meta = data.metadata(); + let capacity_bits = data.num_words() * WORD_LEN; + meta.len = capacity_bits + 1; + drop(data); + drop(sections); + let bytes = area.freeze().unwrap(); + + let err = BitVectorData::from_bytes(meta, bytes).unwrap_err(); + assert!(matches!(err, Error::InvalidMetadata(_))); + } + #[test] fn get_bits_wrapper() { let data = BitVectorData::from_bits([true, false, true, true, false]);