Skip to content

Commit ca68232

Browse files
jianhe25Jian He
andauthored
fix(buffer): don't create &mut [u64] over uninitialized memory in collect_words_in (#10294)
`BitBufferMut::collect_words_in` allocated with `with_capacity_in`, called `set_len` to claim the memory was initialised, and passed `as_mut_slice()` to the fill closure. That creates a `&mut [u64]` over uninitialised memory before anything writes to it. The safety comment argued this was fine because `fill` writes every word and `u64` has no invalid bit patterns, but a reference to uninitialised memory is UB on its own. This uses `BufferMut::zeroed_in(num_words, allocator)` instead, keeping the allocator. `fill` still overwrites every word, so the only extra cost is one memset. ### Testing - Behaviour is unchanged and already covered by the existing `BitBufferMut` tests; I didn't add a new one. - `cargo test -p vortex-buffer --all-features`, clippy, nightly fmt, and Miri on `bit::buf_mut` with CI's `MIRIFLAGS`. ### AI assistance This change was prepared with help from an AI coding assistant (Google-internal agentic tooling). I reviewed and verified the change and the tests myself. Signed-off-by: Jian He <hejia@google.com> Co-authored-by: Jian He <hejia@google.com>
1 parent 5a6de2b commit ca68232

1 file changed

Lines changed: 5 additions & 5 deletions

File tree

‎vortex-buffer/src/bit/buf_mut.rs‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -270,11 +270,11 @@ impl BitBufferMut {
270270
fill: impl FnOnce(&mut [u64]),
271271
) -> Self {
272272
let num_words = len.div_ceil(64);
273-
let mut buffer = BufferMut::<u64>::with_capacity_in(num_words, allocator);
274-
// SAFETY: `fill` (a `collect_bool_words` variant) writes every word in `0..num_words`
275-
// below before any read; `u64` has no invalid bit patterns and the assignments inside
276-
// `collect_bool_words` are pure writes.
277-
unsafe { buffer.set_len(num_words) };
273+
// Allocate the words already initialised. Calling `set_len` on a `with_capacity`
274+
// allocation and then passing `as_mut_slice()` to `fill` would materialise a `&mut [u64]`
275+
// over uninitialised memory, which is Undefined Behaviour regardless of `u64` having no
276+
// invalid bit patterns. `fill` overwrites every word, so the zeroing costs one memset.
277+
let mut buffer = BufferMut::<u64>::zeroed_in(num_words, allocator);
278278
fill(buffer.as_mut_slice());
279279

280280
let mut bytes = buffer.into_byte_buffer();

0 commit comments

Comments
 (0)