fix: eliminate unsound ptr::read double-drop UB in write_to_file (Program + Library)#2840
Open
amathxbt wants to merge 3 commits into0xMiden:nextfrom
Open
fix: eliminate unsound ptr::read double-drop UB in write_to_file (Program + Library)#2840amathxbt wants to merge 3 commits into0xMiden:nextfrom
amathxbt wants to merge 3 commits into0xMiden:nextfrom
Conversation
…to_file
Replaces `unsafe { core::ptr::read(&*err) }` with `*err` to move the
error out of the Box instead of bitwise-copying it. The original code
caused a double-drop (undefined behaviour): ptr::read copies the
Box<io::Error> contents without consuming the box, then the box is
dropped at end of scope, freeing already-moved memory.
The fix uses safe Rust's move semantics via deref-move (`*err`), which
consumes the Box and moves the inner io::Error out, with no double-drop.
Fixes 0xMiden#2814
…ary::write_to_file
Same soundness fix as core/src/program/mod.rs: replace
`unsafe { core::ptr::read(&*err) }` with `*err` to safely move
the io::Error out of its Box rather than bitwise-copying it.
Fixes 0xMiden#2814
huitseeker
reviewed
Mar 12, 2026
| match p.downcast::<std::io::Error>() { | ||
| // SAFETY: It is guaranteed to be safe to read Box<std::io::Error> | ||
| Ok(err) => unsafe { core::ptr::read(&*err) }, | ||
| Ok(err) => *err, |
Contributor
There was a problem hiding this comment.
Could we add a small regression test that forces this downcast::<std::io::Error>() arm in both Program::write_to_file and Library::write_to_file, so this UB pattern is less likely to come back later?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #2814
Eliminates latent undefined behaviour (double-drop) in both
Program::write_to_fileandLibrary::write_to_fileby replacing an unsoundunsafe { core::ptr::read(&*err) }with the safe equivalent*err.The Bug — Unsound
ptr::readCauses Double-DropBoth functions wrap file I/O in
std::panic::catch_unwindto recover panics from the non-fallibleByteWriterAPI. In the panic handler:Why This Is Undefined Behaviour
p.downcast::<std::io::Error>()returnsOk(Box<io::Error>). The code:Box<io::Error>to get a&io::Errorptr::readperforms a bitwise copy of theio::Errorout of the box — without consuming the boxBox<io::Error>is then dropped at end of scope, freeing the memory that was already bitwise-copied outThis is a double-drop: the
io::Error's destructor runs twice on the same memory — once via the value returned byptr::read, and once when theBoxis dropped. That is explicitly undefined behaviour in Rust.The misleading
// SAFETYcomments claim it is "guaranteed safe" — but no such guarantee exists. The original author likely intended a safe move but reached forptr::readunnecessarily.Why It Hasn't Triggered Yet
The panic payload in the current
WriteAdapterimplementation is a formattedStringmessage, not a boxedio::Error. Sodowncast::<io::Error>()currently returnsErr, and theOk(err)arm is never reached. However:WriteAdapteris refactored to panic with an actualio::Error// SAFETYcomment actively misleads future maintainers into thinking the unsafe block is correctThe Fix — Safe Deref-Move
*erron aBox<T>in a value position performs a deref-move: it consumes theBox, moves the innerio::Errorout, and the box's allocation is freed exactly once. This is entirely safe, requires nounsafe, and is idiomatic Rust.Files Changed
core/src/program/mod.rsunsafe { core::ptr::read(&*err) }→*errcrates/assembly-syntax/src/library/mod.rsunsafe { core::ptr::read(&*err) }→*errPre-PR Checklist
nextfix(core): ...,fix(assembly-syntax): ...)