Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion pcapkit/protocols/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -1886,7 +1886,12 @@ def _make_pack(cls, integer: 'int', *, size: 'int' = 1,

if kind is None:
end = 'little' if lilendian else 'big' # type: Literal['little', 'big']
buf = integer.to_bytes(size, end, signed=signed)
# ``int.to_bytes`` raises these where ``struct.pack`` raises
# ``struct.error`` for the other sizes (#2291).
try:
buf = integer.to_bytes(size, end, signed=signed)
except (AttributeError, OverflowError, TypeError, ValueError) as error:
raise StructError(f'{cls.__name__}: pack failed') from error
else:
try:
fmt = f'{endian}{kind}'
Expand Down Expand Up @@ -1964,6 +1969,8 @@ def _make_index(cls, name: 'str | int | StdlibEnum | AenumEnum', default: 'Optio
Raises:
ProtocolNotImplemented: If ``name`` is **NOT** in ``namespace``
and ``default`` is :data:`None`.
StructError: If ``pack`` is :data:`True` and the index could not be
packed into ``size`` octets.

Note:
If ``name`` is a :obj:`str` and ``namespace`` is :data:`None`,
Expand Down
24 changes: 24 additions & 0 deletions tests/protocols/test_protocol_base_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,30 @@ def test_lookup_next_layer_reads_the_fallback_without_recording_it(self) -> None
self.assertIsInstance(proto._import_next_layer(99, 3), Raw)
self.assertEqual(set(DummyProtocol.__proto__), {7, 8})

def test_make_pack_raises_struct_error_for_every_size(self) -> None:
# #2291: a size with no struct format code leaked the errors of
# int.to_bytes instead of the StructError the other sizes raise.
DummyProtocol, _, _ = self._make_protocol_class()
from pcapkit.utilities.exceptions import StructError

cases = (
(2 ** 24, {'size': 3}), # too big
(-1, {'size': 3}), # negative, unsigned
(2 ** 23, {'size': 3, 'signed': True}), # too big, signed
(1.5, {'size': 3}), # not an integer
(None, {'size': 5}), # not an integer
(1, {'size': 0}), # no room at all
(1, {'size': -1}), # negative size
(1, {'size': 3.0}), # size is not an integer
)
for integer, kwargs in cases:
with self.subTest(integer=integer, **kwargs):
with self.assertRaises(StructError):
DummyProtocol._make_pack(integer, **kwargs)
with self.assertRaises(StructError):
DummyProtocol._make_index(2 ** 24, pack=True, size=3)
self.assertEqual(DummyProtocol._make_pack(-2, size=3, signed=True), b'\xff\xff\xfe')

def test_make_payload_branches(self) -> None:
DummyProtocol, DummyData, _ = self._make_protocol_class()
from pcapkit.protocols.misc.null import NoPayload
Expand Down
Loading