Skip to content

Commit 4aaed1d

Browse files
committed
Fix nested ListFields
1 parent 3a30962 commit 4aaed1d

File tree

3 files changed

+54
-3
lines changed

3 files changed

+54
-3
lines changed

scapy/packet.py

+21-3
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,22 @@ def copy_fields_dict(self, fields):
638638
return {fname: self.copy_field_value(fname, fval)
639639
for fname, fval in six.iteritems(fields)}
640640

641+
def _raw_packet_cache_field_value(self, fld, val, copy=False):
642+
# type: (AnyField, Any, bool) -> Optional[Any]
643+
"""Get a value representative of a mutable field to detect changes"""
644+
_cpy = lambda x: fld.do_copy(x) if copy else x # type: Callable[[Any], Any]
645+
if fld.holds_packets:
646+
# avoid copying whole packets (perf: #GH3894)
647+
if fld.islist:
648+
return [
649+
_cpy(x.fields) for x in val
650+
]
651+
else:
652+
return _cpy(val.fields)
653+
elif fld.islist or fld.ismutable:
654+
return _cpy(val)
655+
return None
656+
641657
def clear_cache(self):
642658
# type: () -> None
643659
"""Clear the raw packet cache for the field and all its subfields"""
@@ -661,7 +677,8 @@ def self_build(self):
661677
"""
662678
if self.raw_packet_cache is not None:
663679
for fname, fval in six.iteritems(self.raw_packet_cache_fields):
664-
if self.getfieldval(fname) != fval:
680+
fld, val = self.getfield_and_val(fname)
681+
if self._raw_packet_cache_field_value(fld, val) != fval:
665682
self.raw_packet_cache = None
666683
self.raw_packet_cache_fields = None
667684
self.wirelen = None
@@ -987,8 +1004,9 @@ def do_dissect(self, s):
9871004
continue
9881005
# We need to track fields with mutable values to discard
9891006
# .raw_packet_cache when needed.
990-
if f.islist or f.holds_packets or f.ismutable:
991-
self.raw_packet_cache_fields[f.name] = f.do_copy(fval)
1007+
if (f.islist or f.holds_packets or f.ismutable) and fval is not None:
1008+
self.raw_packet_cache_fields[f.name] = \
1009+
self._raw_packet_cache_field_value(f, fval, copy=True)
9921010
self.fields[f.name] = fval
9931011
self.raw_packet_cache = _raw[:-len(s)] if s else _raw
9941012
self.explicit = 1

test/fields.uts

+28
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,34 @@ assert p.data[3].data == 0xc102
758758
assert isinstance(p.payload, conf.raw_layer)
759759
assert p.payload.load == b'toto'
760760

761+
= Test nested PacketListFields
762+
~ field
763+
# Note: having packets that look like this is a terrible idea, and will perform
764+
# very badly. However we must ensure we don't freeze because of it.
765+
766+
# https://github.com/secdev/scapy/issues/3894
767+
768+
class GuessPayload(Packet):
769+
@classmethod
770+
def dispatch_hook(cls, *args, **kargs):
771+
return TestNestedPLF
772+
773+
class TestNestedPLF(Packet):
774+
fields_desc = [
775+
ByteField('b', 0),
776+
PacketListField('pl', [], GuessPayload)
777+
]
778+
779+
p = TestNestedPLF(b'\x01' * 100)
780+
781+
# check
782+
i = 1
783+
while p.pl:
784+
p = p.pl[0]
785+
p.show()
786+
i += 1
787+
788+
assert i == 100
761789

762790
############
763791
############

test/scapy/layers/dhcp6.uts

+5
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,11 @@ raw(DHCP6OptRelaySuppliedOpt(relaysupplied=DHCP6OptERPDomain(erpdomain=["toto.ex
11671167
a = DHCP6OptRelaySuppliedOpt(b'\x00B\x00\x16\x00A\x00\x12\x04toto\x07example\x03com\x00')
11681168
a.optcode == 66 and a.optlen == 22 and len(a.relaysupplied) == 1 and isinstance(a.relaysupplied[0], DHCP6OptERPDomain) and a.relaysupplied[0].erpdomain[0] == "toto.example.com."
11691169

1170+
= DHCP6OptRelaySuppliedOpt - deeply nested DHCP6OptRelaySuppliedOpt
1171+
# https://github.com/secdev/scapy/issues/3894
1172+
1173+
p = DHCP6(b'\x01\x00\x00\x00' + b'\x00B\x0f\x0f' * 100)
1174+
assert p.getlayer(DHCP6OptRelaySuppliedOpt, 100)
11701175

11711176
############
11721177
############

0 commit comments

Comments
 (0)