{"id":"GHSA-qwgh-2vcv-g2f7","kind":"vulnerability","aliases":["GHSA-qwgh-2vcv-g2f7"],"related":[],"title":"block_buffer: panic corrupts inline buffer position","description":"### Summary\n\nA caught panic may leave the cursor position of `EagerBuffer` or `ReadBuffer` in a corrupted state; this in turn allows out-of-bounds reads/writes.\n\n### Details & PoC\n\nThe following two tests fail miri:\n\n```rust\n#[cfg(miri)]\n#[test]\nfn eager_digest_blocks_panic_corrupts_inline_position() {\n    // `EagerBuffer` stores its cursor in the last byte of the internal block.\n    // When `digest_blocks` completes a previously partial block, it overwrites\n    // that byte with input data before invoking the caller-provided `compress`\n    // callback. If the callback panics, safe code can catch the panic and keep\n    // using the buffer while its cursor byte no longer satisfies the internal\n    // `pos < block_size` invariant. Under Miri this `get_pos` call reaches the\n    // `unreachable_unchecked` used for the assumed-valid cursor.\n    let mut buf = EagerBuffer::<U4>::new(&[1, 2]);\n\n    let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n        buf.digest_blocks(&[3, 0xff], |_| panic!(\"simulated compression failure\"));\n    }));\n\n    let _ = buf.get_pos();\n}\n\n#[cfg(miri)]\n#[test]\nfn read_buffer_generator_panic_corrupts_inline_position() {\n    // `ReadBuffer` stores its cursor in `buffer[0]`, but `write_block` gives\n    // `gen_block` mutable access to the whole internal block before restoring\n    // `buffer[0]` to a valid cursor. If `gen_block` writes an arbitrary first\n    // byte and panics, safe code can catch the panic and later observe an\n    // invalid cursor. Under Miri this `get_pos` call reaches the\n    // `unreachable_unchecked` used for the assumed-valid cursor.\n    let mut buf = ReadBuffer::<U4>::default();\n\n    let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n        buf.write_block(\n            1,\n            |block| {\n                block[0] = 0xff;\n                panic!(\"simulated block generation failure\");\n            },\n            |_| {},\n        );\n    }));\n\n    let _ = buf.get_pos();\n}\n```\n\nThey fail on an `unreachable_unchecked!()` under the invariant for the `pos` to always be within bounds of the block.\n\n### Impact\n\nWhile 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.\n\nFor this to be exploitable, the attacker also needs a way to trigger a panic here; I have not investigated how feasible that is.\n\n### Credits\n\nThe issue was discovered by GPT-5.5","summary":"The Rust crate block_buffer can leave its internal cursor position in an invalid state if a panic occurs during block processing and is caught by the calling code. The corrupted cursor can then lead to out-of-bounds reads and writes.","severity":"medium","cvss":null,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"block_buffer","fixed":["0.12.1"],"affected":">= 0.11.0, < 0.12.1","ecosystem":"cargo"}],"ecosystems":["cargo"],"tags":["block_buffer","cargo","cwe-119","package","rust"],"not_affected_if":["no-catch-unwind","version-0.12.1-or-later"],"how_to_check":"- Check whether the project depends on `block_buffer` at versions `>= 0.11.0` and `< 0.12.1` with `cargo tree -i block_buffer`\n- Look at `Cargo.lock` for `block_buffer` entries at a version below `0.12.1`","fix":"- Upgrade `block_buffer` to `0.12.1`","status":"open","published_at":"2026-08-19T19:15:34+00:00","first_seen_at":"2026-10-30T15:32:39+00:00","triaged_at":"2026-10-30T15:33:58+00:00","updated_at":"2026-10-30T15:33:58+00:00","category":"package","confidence":0.7,"triage":{"at":"2026-10-30T15:33:58Z","model":"claude-haiku-5-5","output":{"fix":"- Upgrade `block_buffer` to `0.12.1`","reach":"unknown","title":"block_buffer: caught panic corrupts buffer cursor, enabling out-of-bounds memory access","impact":"medium","summary":"The Rust crate block_buffer can leave its internal cursor position in an invalid state if a panic occurs during block processing and is caught by the calling code. The corrupted cursor can then lead to out-of-bounds reads and writes.","audience":"developers","category":"package","nicknames":[],"who_cares":["rust","cargo","block_buffer"],"confidence":0.7,"fix_status":"available","needs_auth":"unknown","worst_case":"out-of-bounds memory reads and writes in the process","analyst_note":"","how_to_check":"- Check whether the project depends on `block_buffer` at versions `>= 0.11.0` and `< 0.12.1` with `cargo tree -i block_buffer`\n- Look at `Cargo.lock` for `block_buffer` entries at a version below `0.12.1`","plain_action":"","needs_analyst":false,"not_affected_if":["version-0.12.1-or-later","no-catch-unwind"],"who_is_affected":"Only Rust projects that pull in block_buffer at versions from 0.11.0 up to but not including 0.12.1, usually indirectly through hashing or cipher crates. Exposure additionally requires that a panic can be triggered during digest processing and that the surrounding code catches it and keeps using the buffer, which the advisory says should be uncommon. Projects not using block_buffer below 0.12.1 can skip it.","user_interaction":"no","needs_special_setup":"yes","severity_if_unknown":"unknown"},"cost_usd":0.000803,"input_tokens":5084,"output_tokens":590,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-119':24B '-5.5':416C '0':239C,256C,318C '0xff':214C,319C '1':201C,315C '2':202C '2vcv':11A,16A '3':213C 'access':247C 'allow':90C 'also':392C 'alway':341C 'arbitrari':266C 'assertunwindsaf':210C,312C 'assum':193C,297C 'assumed-valid':192C,296C 'attack':364C,391C 'attacker-control':363C 'block':1A,18A,20B,30B,47B,111C,127C,130C,135C,176C,212C,242C,245C,252C,263C,314C,316C,317C,322C,347C 'bound':66B,94C,344C 'buf':198C,302C 'buf.digest':211C 'buf.get':220C,326C 'buf.write':313C 'buffer':2A,6A,19A,21B,31B,165C,227C,238C,255C 'byte':123C,139C,169C,268C,351C 'call':54B,184C,288C 'callback':150C,153C 'caller':147C 'caller-provid':146C 'cargo':22B 'carri':377C 'catch':158C,206C,274C,308C,373C 'caught':51B,72C 'cfg':105C,222C 'code':55B,156C,272C,372C 'come':356C 'complet':131C 'compress':149C,217C 'control':365C 'corrupt':4A,57B,85C,113C,230C 'crate':29B 'credit':409C 'cursor':36B,58B,77C,119C,168C,195C,236C,260C,282C,299C 'cwe':23B 'data':142C 'default':304C 'detail':96C 'digest':110C,129C 'discov':413C 'eager':109C 'eagerbuff':80C,116C,199C 'exploit':389C 'fail':102C,329C 'failur':218C,324C 'feasibl':406C 'first':267C 'fn':108C,225C 'follow':99C 'g2f7':12A,17A 'gen':244C,262C 'generat':228C,323C 'get':182C,286C 'ghsa':9A,14A 'ghsa-qwgh-2vcv-g2f7':8A,13A 'give':243C 'gpt':415C 'impact':348C 'inlin':5A,114C,231C 'input':141C,359C 'intern':35B,126C,174C,251C 'invalid':40B,281C 'invari':178C,336C 'investig':404C 'invok':144C 'issu':411C 'keep':162C 'last':122C 'later':278C 'lead':61B 'leav':33B,75C 'let':196C,203C,219C,300C,305C,325C 'longer':171C 'may':74C,355C 'miri':103C,106C,180C,223C,284C 'mut':197C,301C 'mutabl':246C 'need':393C 'new':200C 'observ':279C 'occur':45B 'out-of-bound':63B,91C 'overwrit':137C,353C 'packag':25B 'panic':3A,44B,73C,112C,154C,160C,205C,209C,215C,229C,270C,276C,307C,311C,320C,375C,399C 'partial':134C 'poc':97C 'pos':175C,183C,221C,287C,327C,339C,354C 'posit':7A,37B,78C,115C,232C 'practic':384C 'previous':133C 'process':48B 'provid':148C 'qwgh':10A,15A 'reach':185C,289C 'read':67B,226C 'readbuff':82C,233C,303C 'reads/writes':95C 'reli':368C 'restor':254C 'rust':26B,28B,104C 'safe':155C,271C 'satisfi':172C 'simul':216C,321C 'size':177C 'state':41B,86C 'std':204C,208C,306C,310C 'still':367C 'store':117C,234C 'summari':70C 'surround':371C 'test':101C,107C,224C 'therefor':362C 'trigger':397C 'turn':89C 'two':100C 'uncheck':188C,292C,333C 'uncommon':382C 'unreach':187C,291C,332C 'untrust':358C 'unwind':207C,309C 'use':163C,189C,293C 'valid':194C,259C,298C 'way':395C 'whole':250C 'within':343C 'write':69B,241C,264C","nicknames":[],"mitigations":[],"who_affected":"Only Rust projects that pull in block_buffer at versions from 0.11.0 up to but not including 0.12.1, usually indirectly through hashing or cipher crates. Exposure additionally requires that a panic can be triggered during digest processing and that the surrounding code catches it and keeps using the buffer, which the advisory says should be uncommon. Projects not using block_buffer below 0.12.1 can skip it.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"first_public_at":"2026-08-19T19:15:34+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-qwgh-2vcv-g2f7","url":"https://github.com/advisories/GHSA-qwgh-2vcv-g2f7","severity":"medium","published_at":"2026-08-19T19:15:34+00:00","modified_at":"2026-10-30T15:32:34+00:00","first_seen_at":"2026-10-30T15:32:39+00:00","fetched_at":"2026-10-30T15:37:53+00:00","note":null}],"documents":[{"id":24315,"url":"https://api.github.com/repos/RustCrypto/utils/security-advisories/GHSA-qwgh-2vcv-g2f7","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":24316,"url":"https://github.com/RustCrypto/utils/security/advisories/GHSA-qwgh-2vcv-g2f7","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":24317,"url":"https://github.com/RustCrypto/utils/releases/tag/block-buffer-v0.12.1","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"}],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}