Split off #799's review. httpv2.py:223's frame-length guard has been fixed
there so a buffer under nine octets, or a declared length exceeding the
buffer, is rejected uniformly before the schema layer runs. That closes the
outer-header truncation class, but not a related one one level deeper:
pkt['__length__'] in Schema.unpack (schema.py:894-899) is decremented by
each field's nominal width regardless of how many octets the buffer
actually had, and going negative is only ever a SchemaWarning — never a
raise. A field whose own length=lambda pkt: pkt['__length__']-style
callback resolves to that negative number builds a struct template like
'-5s', and struct.calcsize raises a bare struct.error for it: not a
ProtocolError, not a ValueError, uncatchable by ordinary caller code.
Reproduction, httpv2 specifically (buffer clears the fixed 9-octet header,
but the frame type's own fixed-width payload fields don't fit in what's
left):
import io
from pcapkit.protocols.application.httpv2 import HTTP as HTTPv2
data = b'\x00\x00\x15\x07\x00\x00\x00\x00\x00' + b'\xff' * 7 # GOAWAY, 16 octets
HTTPv2(io.BytesIO(data), 16)
# struct.error: bad char in struct format
Same shape at other sizes/types: GOAWAY at 9-16 octets (stream+error are
eight fixed octets alone), PUSH_PROMISE at 9-12, and any PADDED
DATA/HEADERS/PUSH_PROMISE whose pad_len exceeds what remains.
pkt['__length__'] is generic machinery, not an httpv2 particular — at
least these schema modules also key a field's length off it and would need
checking for the same latent crash before any fix lands:
- pcapkit/protocols/schema/application/ftp.py
- pcapkit/protocols/schema/application/httpv1.py
- pcapkit/protocols/schema/application/httpv2.py
- pcapkit/protocols/schema/application/ngap.py
- pcapkit/protocols/schema/internet/hip.py
- pcapkit/protocols/schema/internet/ipv6_route.py
- pcapkit/protocols/schema/internet/mh.py
- pcapkit/protocols/schema/link/ethernet.py
- pcapkit/protocols/schema/misc/pcapng.py
- pcapkit/protocols/schema/transport/sctp.py
Proposed fix: make a negative resolved field length raise ProtocolError
(or a dedicated subclass) in Schema.unpack and/or
FieldBase.length/Field.unpack, in place of the current
warn(f'packet length < 0: ...', SchemaWarning, ...). That closes the
inner-payload class the same way #799 closed the outer-header one, and lets
HTTP._guess_version's last arm in pcapkit/protocols/application/http.py
drop its struct.error suppression for good. Scoped as its own issue
because the change is in shared schema/field machinery, not a single
protocol, and needs the blast-radius check above before it lands.
Split off #799's review. httpv2.py:223's frame-length guard has been fixed
there so a buffer under nine octets, or a declared length exceeding the
buffer, is rejected uniformly before the schema layer runs. That closes the
outer-header truncation class, but not a related one one level deeper:
pkt['__length__']inSchema.unpack(schema.py:894-899) is decremented byeach field's nominal width regardless of how many octets the buffer
actually had, and going negative is only ever a
SchemaWarning— never araise. A field whose own
length=lambda pkt: pkt['__length__']-stylecallback resolves to that negative number builds a struct template like
'-5s', andstruct.calcsizeraises a barestruct.errorfor it: not aProtocolError, not aValueError, uncatchable by ordinary caller code.Reproduction, httpv2 specifically (buffer clears the fixed 9-octet header,
but the frame type's own fixed-width payload fields don't fit in what's
left):
Same shape at other sizes/types: GOAWAY at 9-16 octets (
stream+errorareeight fixed octets alone), PUSH_PROMISE at 9-12, and any PADDED
DATA/HEADERS/PUSH_PROMISE whose
pad_lenexceeds what remains.pkt['__length__']is generic machinery, not an httpv2 particular — atleast these schema modules also key a field's length off it and would need
checking for the same latent crash before any fix lands:
Proposed fix: make a negative resolved field length raise
ProtocolError(or a dedicated subclass) in
Schema.unpackand/orFieldBase.length/Field.unpack, in place of the currentwarn(f'packet length < 0: ...', SchemaWarning, ...). That closes theinner-payload class the same way #799 closed the outer-header one, and lets
HTTP._guess_version's last arm inpcapkit/protocols/application/http.pydrop its
struct.errorsuppression for good. Scoped as its own issuebecause the change is in shared schema/field machinery, not a single
protocol, and needs the blast-radius check above before it lands.