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:
| nesting | stack | result |
|---|---|---|
| 800 | 2 MiB (a tokio worker) | returns Err("End of stream") |
| 1,000 | 2 MiB | fatal runtime error: stack overflow, aborting |
| 9,215 | 8 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§
- Bencode
Structure Error - 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_structurewith an explicit limit, for tests.