Skip to main content

Module bencode

Module bencode 

Source
Expand description

A bound on bencode nesting, applied before a decoder ever sees the bytes.

serde_bencode has no depth limit. Its Deserializer::deserialize_any recurses into visit_seq/visit_map for every l or d it meets, and serde’s derived struct deserialisers reach the same code through IgnoredAny when they skip an unknown field, so a typed decode is no safer than decoding into serde_bencode::value::Value. Nothing in either path counts how deep it has gone.

Bencode is nested by construction and one l byte opens a level, so the cost to an attacker is one byte per stack frame. Measured against 0.2.4, decoding l repeated N times on a thread of a given stack size:

nestingstackresult
8002 MiB (a tokio worker)returns Err("End of stream")
1,0002 MiBfatal runtime error: stack overflow, aborting
9,2158 MiB (a main thread)fatal runtime error: stack overflow, aborting

Roughly one kilobyte on the wire is enough to kill a worker, and the largest UDP datagram macOS will send (net.inet.udp.maxdgram = 9216) overflows even the main thread. This is not a panic. A Rust stack overflow raises SIGSEGV against the guard page and the runtime aborts; tokio::spawn cannot contain it and there is no catch_unwind that sees it. One datagram from any host that can reach the socket takes the whole of netget down with it.

So the bytes are walked iteratively first. check_bencode_structure uses a depth counter and an index, allocates nothing, and reads each byte once; only input it accepts is handed to serde_bencode. It scans exactly one top-level value and ignores whatever follows, which is what serde_bencode::from_bytes does too — being stricter about trailing bytes would reject input the decoder would have accepted.

It also bounds a declared byte-string length against the bytes actually present rather than trusting it, so 99999999999: cannot be used to make the caller reserve on a promise the datagram does not keep.

Enums§

BencodeStructureError
Why a bencode byte string was refused before decoding.

Constants§

MAX_BENCODE_DEPTH
Deepest nesting a bencode value may use before it is refused.

Functions§

check_bencode_structure
Walk one bencode value iteratively, refusing anything nested deeper than MAX_BENCODE_DEPTH.
check_bencode_structure_with_limit
check_bencode_structure with an explicit limit, for tests.