GHSA-qwgh-2vcv-g2f7: Buffer Overflow
Summary
A caught panic may leave the cursor position of EagerBuffer or ReadBuffer in a corrupted state; this in turn allows out-of-bounds reads/writes.
Details & PoC
The following two tests fail miri:
rust #[cfg(miri)] #[test] fn eagerdigestblockspaniccorruptsinlineposition() { // EagerBuffer stores its cursor in the last byte of the internal block. // When digestblocks completes a previously partial block, it overwrites // that byte with input data before invoking the caller-provided compress // callback. If the callback panics, safe code can catch the panic and keep // using the buffer while its cursor byte no longer satisfies the internal // pos < blocksize invariant. Under Miri this getpos call reaches the // unreachableunchecked used for the assumed-valid cursor. let mut buf = EagerBuffer::<U4>::new(&[1, 2]);
let = std::panic::catchunwind(std::panic::AssertUnwindSafe(|| { buf.digestblocks(&[3, 0xff], || panic!("simulated compression failure")); }));
let = buf.getpos(); }
#[cfg(miri)] #[test] fn readbuffergeneratorpaniccorruptsinlineposition() { // ReadBuffer stores its cursor in buffer[0], but writeblock gives // genblock mutable access to the whole internal block before restoring // buffer[0] to a valid cursor. If genblock writes an arbitrary first // byte and panics, safe code can catch the panic and later observe an // invalid cursor. Under Miri this getpos call reaches the // unreachableunchecked used for the assumed-valid cursor. let mut buf = ReadBuffer::<U4>::default();
let = std::panic::catchunwind(std::panic::AssertUnwindSafe(|| { buf.writeblock( 1, |block| { block[0] = 0xff; panic!("simulated block generation failure"); }, || {}, ); }));
let = buf.getpos(); }
They fail on an unreachableunchecked!() under the invariant for the pos to always be within bounds of the block.
Impact
While the byte that overwrites pos may come from untrusted input and is therefore attacker-controlled, this still relies on the surrounding code catching the panic and carrying on, which should be uncommon in practice.
For this to be exploitable, the attacker also needs a way to trigger a panic here; I have not investigated how feasible that is.
Credits
The issue was discovered by GPT-5.5
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
rust/block_bufferto a version that resolves this vulnerability.Fixed in 0.12.1
Event History
Frequently Asked Questions
What conditions are required to trigger the issue?
A caller-provided callback used during buffer processing must panic, and the panic must be caught so that execution continues and the buffer is reused. The affected internal cursor can then violate its required position invariant.
Which usage patterns are exposed?
Code using EagerBuffer with a compress callback, or ReadBuffer with a block generator callback, is exposed when those callbacks can panic and the panic is caught. Continuing to call buffer methods after that caught panic can lead to out-of-bounds reads or writes.
What can be done if an update cannot be applied immediately?
Avoid allowing callback panics to be caught and then continuing to use the same EagerBuffer or ReadBuffer instance. Treat an instance involved in a caught callback panic as unusable.
How can this be identified during testing?
The supplied proof-of-concept tests fail under Miri after a caught panic and subsequent buffer use. Testing paths where compression or block-generation callbacks panic, are caught, and the buffer is reused can identify affected behavior.