You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A 16-bit padding shortfall band is still unbudgeted after #593: a crafted capture amplifies 1,637x, indistinguishable from a truncated one at the field layer #594
#593 bounds the 32-bit padding band that #573 measured, and deliberately leaves a 16-bit band unbudgeted. A crafted capture still amplifies 1,637x after that fix, and this issue exists so that residual is not lost when #573 auto-closes.
#573's measured vector was a 32-bit declared length — UnknownSecrets.data's secrets_length, up to _MAX_ZERO_PAD_LENGTH = 262,144 — at ~10,900x over 200 blocks. #593 ships the per-parse budget #573 asked for and takes that scenario to 54.6x, roughly 200x tighter. That gap is genuinely closed.
The residual is a different shape: 16-bit declared lengths repeated across many blocks. An 80,048-octet PCAP-NG of 2,000 Enhanced Packet Blocks, each with one option declaring 65,535 octets against four real ones, parses through Extractor(store=True) and synthesises 125.00 MiB of padding for 216.96 MiB of RSS — 1,637x, linear in block count, therefore unbounded in the input size. Measured before and after #593; unchanged by it.
Why #593 left it open, which is a real constraint rather than an omission
A shortfall of 65,536 octets or fewer is the whole span of a 16-bit wire length, so every such shortfall is one a snapshot-truncated capture, a truncated option area or an over-long ihl can legitimately produce. Concretely: a bare 40-octet IPv4 header declaring a total length of 65,535 — a legitimate offload-sized segment cut to a small snapshot — amplifies by the same ratio. Independently computed by two parties as 1637.375x (crafted) versus 1637.393x (legitimate).
So the two are not separable by any budget applied at the pcapkit/corekit/fields layer, because that layer cannot see whether truncation was declared. Telling them apart needs the frame's own incl_len / orig_len: a crafted block claims nothing was truncated while declaring more than it holds, whereas a genuinely truncated frame says so on the wire. That information lives at the protocol/extractor layer.
A naive single running budget is not an option either, and this was demonstrated rather than argued: with one budget over all padding, the same legitimate 54-octet frame declaring an IPv4 total length of 65,535 produced four different parse results across 40 byte-identical calls, refusing on calls 26, 33 and 39 and differing only by position, with beholder swallowing the refusal into a silently different Raw layer. A guard whose answer depends on parse history is worse than the amplification it prevents.
What a fix would need
Plumb the frame's declared-versus-actual length from the PCAP/PCAP-NG layer down to wherever padding is charged, so a shortfall inside a frame that declares truncation can be distinguished from one inside a frame that does not.
Keep the guard history-independent: byte-identical input must produce byte-identical results regardless of what was parsed before it.
#593 bounds the 32-bit padding band that #573 measured, and deliberately leaves a 16-bit band unbudgeted. A crafted capture still amplifies 1,637x after that fix, and this issue exists so that residual is not lost when #573 auto-closes.
What #593 closes, and what it does not
#573's measured vector was a 32-bit declared length —
UnknownSecrets.data'ssecrets_length, up to_MAX_ZERO_PAD_LENGTH= 262,144 — at ~10,900x over 200 blocks. #593 ships the per-parse budget #573 asked for and takes that scenario to 54.6x, roughly 200x tighter. That gap is genuinely closed.The residual is a different shape: 16-bit declared lengths repeated across many blocks. An 80,048-octet PCAP-NG of 2,000 Enhanced Packet Blocks, each with one option declaring 65,535 octets against four real ones, parses through
Extractor(store=True)and synthesises 125.00 MiB of padding for 216.96 MiB of RSS — 1,637x, linear in block count, therefore unbounded in the input size. Measured before and after #593; unchanged by it.Why #593 left it open, which is a real constraint rather than an omission
A shortfall of 65,536 octets or fewer is the whole span of a 16-bit wire length, so every such shortfall is one a snapshot-truncated capture, a truncated option area or an over-long
ihlcan legitimately produce. Concretely: a bare 40-octet IPv4 header declaring a total length of 65,535 — a legitimate offload-sized segment cut to a small snapshot — amplifies by the same ratio. Independently computed by two parties as 1637.375x (crafted) versus 1637.393x (legitimate).So the two are not separable by any budget applied at the
pcapkit/corekit/fieldslayer, because that layer cannot see whether truncation was declared. Telling them apart needs the frame's ownincl_len/orig_len: a crafted block claims nothing was truncated while declaring more than it holds, whereas a genuinely truncated frame says so on the wire. That information lives at the protocol/extractor layer.A naive single running budget is not an option either, and this was demonstrated rather than argued: with one budget over all padding, the same legitimate 54-octet frame declaring an IPv4 total length of 65,535 produced four different parse results across 40 byte-identical calls, refusing on calls 26, 33 and 39 and differing only by position, with
beholderswallowing the refusal into a silently differentRawlayer. A guard whose answer depends on parse history is worse than the amplification it prevents.What a fix would need
len(buffer) < lengthrejection that broke exactly that.Notes
Fixes #573honest on the grounds above while recommending this separate issue — that recommendation is why it exists.