Avoid over-allocating when reading corrupted lengths for `HashMap`s
What changed, and why it matters
This commit fixes a denial-of-service risk when rust-lightning reads saved data containing HashMaps. A corrupted or malicious length field could previously trick the program into reserving a huge amount of memory before it had read any actual entries. The patch now caps the initial allocation to a safe maximum based on the largest buffer size the code is willing to handle. The commit message says the affected deserialization paths were mainly ChannelManager and scorer data, with scorer data sometimes coming from a semi-trusted source.
Treat this as a security hardening fix and include it in the next maintenance release. Review whether other collection deserialization macros (Vec, arrays, custom maps) have the same over-allocation issue and apply consistent MAX_BUF_SIZE-based caps. If scorer data can be loaded from network or user-controlled sources, consider additional input validation or sandboxing.
Security signals we found
memory allocation controlled by external length field
deserialization of semi-trusted scorer data
denial-of-service via corrupted length
initial capacity capped by MAX_BUF_SIZE-derived limit
Evidence from the diff
The change is in lightning/src/util/ser.rs inside the impl_for_map macro used for HashMap/HashSet deserialization. Previously, read() parsed a CollectionLength and passed len.0 directly to the map constructor, which could pre-allocate an enormous Vec or hash table if the length field was corrupted. The patch computes an upper bound max_alloc = MAX_BUF_SIZE / (size_of::
Changed components
lightning/src/util/ser.rsHashMap/HashSet deserialization macro (impl_for_map)ChannelManager deserializationscorer deserializationInspect captured patch +3 / −1
diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs
index b93be64..4defe29 100644
--- a/lightning/src/util/ser.rs
+++ b/lightning/src/util/ser.rs
@@ -960,7 +960,9 @@ macro_rules! impl_for_map {
#[inline]
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
let len: CollectionLength = Readable::read(r)?;
- let mut ret = $constr(len.0 as usize);
+ let entry_size = ::core::mem::size_of::<K>() + ::core::mem::size_of::<V>();
+ let max_alloc = MAX_BUF_SIZE / (entry_size + 1);
+ let mut ret = $constr(cmp::min(len.0 as usize, max_alloc));
for _ in 0..len.0 {
let k = K::read(r)?;
let v_opt = V::read(r)?;
Why this scored 55/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.