Skip to content

Commit 8a4cdd3

Browse files
authored
fix(protocol): raise on a short _read_unpack for every size (#2279) (#2349)
Problem: for a size with no struct format code, _read_unpack ran int.from_bytes on whatever arrived, so a short read returned a smaller integer where sizes 1, 2, 4 and 8 raise StructError. Fix: check the length on the int.from_bytes path and raise the same StructError without quiet; quiet still returns the partial value. Test: test_read_unpack_short_read_fails_for_every_size.
1 parent 1826870 commit 8a4cdd3

2 files changed

Lines changed: 24 additions & 2 deletions

File tree

‎pcapkit/protocols/protocol.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1767,8 +1767,7 @@ def _read_unpack(self, size: 'int' = 1, *, signed: 'bool' = False,
17671767
17681768
Raises:
17691769
StructError: If the buffer is empty, whatever ``quiet`` says, or if
1770-
unpack (:func:`struct.unpack`) failed, and :exc:`struct.error`
1771-
raised, without ``quiet``.
1770+
fewer than ``size`` octets were read, without ``quiet``.
17721771
17731772
"""
17741773
endian = '<' if lilendian else '>'
@@ -1788,6 +1787,10 @@ def _read_unpack(self, size: 'int' = 1, *, signed: 'bool' = False,
17881787
raise StructError('unpack: empty buffer', quiet=True, eof=True)
17891788

17901789
if kind is None:
1790+
# A short read fails here as :func:`struct.unpack` makes it fail
1791+
# for the sizes that have a format code (#2279).
1792+
if len(mem) < size and not quiet:
1793+
raise StructError(f'{self.__class__.__name__}: unpack failed')
17911794
end = 'little' if lilendian else 'big' # type: Literal['little', 'big']
17921795
buf = int.from_bytes(mem, end, signed=signed)
17931796
else:

‎tests/protocols/test_protocol_base_unit.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,25 @@ class Number(enum.IntEnum):
234234
with self.assertRaises(ProtocolNotImplemented):
235235
DummyProtocol._make_index('missing')
236236

237+
def test_read_unpack_short_read_fails_for_every_size(self) -> None:
238+
# #2279: a size with no struct format code read a short buffer as a
239+
# smaller integer instead of failing as sizes 1, 2, 4 and 8 do.
240+
DummyProtocol, _, _ = self._make_protocol_class()
241+
from pcapkit.utilities.exceptions import StructError
242+
243+
proto = DummyProtocol(packet=b'\x01')
244+
for size in (2, 3, 4, 5, 8):
245+
for signed in (False, True):
246+
with self.subTest(size=size, signed=signed):
247+
proto._file = io.BytesIO(bytes(range(1, size)))
248+
with self.assertRaises(StructError) as context:
249+
proto._read_unpack(size, signed=signed)
250+
self.assertFalse(context.exception.eof)
251+
252+
proto._file = io.BytesIO(bytes(range(1, size)))
253+
self.assertEqual(proto._read_unpack(size, signed=signed, quiet=True),
254+
int.from_bytes(bytes(range(1, size)), 'big', signed=signed))
255+
237256
def test_register_analyze_and_next_layer_paths(self) -> None:
238257
DummyProtocol, DummyData, _ = self._make_protocol_class()
239258
from pcapkit.corekit.module import ModuleDescriptor

0 commit comments

Comments
 (0)