From 940ce48f9c0900f3f7c6fb77612c00fcaf3fc210 Mon Sep 17 00:00:00 2001 From: USCMig Date: Sun, 23 Aug 2026 04:28:05 +0000 Subject: [PATCH] fix(mem_forest): return writer errors from serialize instead of panicking `serialize` returns `io::Result<()>` and uses `?` for the two length prefixes, but then `unwrap()`ed the result of writing each root. A writer that fails partway -- a full disk, a closed pipe, a socket that went away -- panics rather than returning the `Err` the signature promises. Separate from the deserialize fixes in the previous commit and safe to take on its own: this one is about a failing writer, not untrusted input. --- src/mem_forest/mod.rs | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/mem_forest/mod.rs b/src/mem_forest/mod.rs index 577ec5b..59688c3 100644 --- a/src/mem_forest/mod.rs +++ b/src/mem_forest/mod.rs @@ -268,7 +268,7 @@ impl MemForest { writer.write_all(&(self.roots.len() as u64).to_le_bytes())?; for root in &self.roots { - root.write_one(&mut writer).unwrap(); + root.write_one(&mut writer)?; } Ok(()) @@ -1133,4 +1133,41 @@ mod test { assert!(deserialized.get_roots()[0].get_data().is_empty()); assert_eq!(deserialized.leaves, 16); } + + #[test] + fn test_serialize_reports_writer_errors() { + // `serialize` returns `io::Result`, so a writer that fails partway -- + // a full disk, a closed pipe -- has to come back as `Err`. The root + // loop used to `unwrap()` it and panic instead. + struct ShortWriter(usize); + impl Write for ShortWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + if self.0 == 0 { + return Err(io::Error::new(io::ErrorKind::WriteZero, "no room left")); + } + let n = buf.len().min(self.0); + self.0 -= n; + Ok(n) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + + let hashes: Vec = + (0..4_u8).map(|i| BitcoinNodeHash::from([i; 32])).collect(); + let mut p = MemForest::::new(); + p.modify(&hashes, &[]).expect("modify should work"); + + // Room for the two length prefixes but not for the roots that follow. + let err = p + .serialize(ShortWriter(16)) + .expect_err("a writer that runs out of room is an error, not a panic"); + assert_eq!(err.kind(), io::ErrorKind::WriteZero); + + // The same forest still serializes when the writer accepts everything. + let mut good = Vec::new(); + p.serialize(&mut good).expect("serialize should work"); + assert!(!good.is_empty()); + } }