Skip to content

ListField.unpack discards the configured per-item field on the schema branch #433

Description

@JarryShaw

ListField.unpack's schema-item branch computes a per-item field and then discards it:

field = self._item_type(packet)

if is_schema:
    data = cast('SchemaField', self._item_type).unpack(file, packet)

field is used by the non-schema branch below (length -= field.length, file.read(field.length)), but the schema branch unpacks from self._item_type — the unconfigured field — so anything SchemaField.__call__ applied to build field is thrown away:

def __call__(self, packet: 'dict[str, Any]') -> 'Self':
    new_self = copy.copy(self)
    new_self._callback(new_self, packet)
    if new_self._length_callback is not None:
        new_self._length = new_self._length_callback(packet)
        new_self._template = f'{new_self._length}s' if self._length >= 0 else '1024s'
    return new_self

So a ListField whose item is a SchemaField carrying a callback or a length_callback would parse with neither applied.

Not currently reachable

All four in-tree declarations pass only schema=, plus a static length= in two cases:

site item
pcapkit/protocols/schema/internet/mh.py:535 SchemaField(schema=CGAParameter)
pcapkit/protocols/schema/internet/hip.py:260 SchemaField(schema=Locator)
pcapkit/protocols/schema/transport/tcp.py:393 SchemaField(length=8, schema=SACKBlock)
pcapkit/protocols/schema/transport/sctp.py:702 SchemaField(length=4, schema=GapAckBlock)

None passes callback or length_callback, so __call__ returns a copy.copy whose callback is the default no-op and whose _length_callback is None, leaving the discarded field equivalent to self._item_type. The length=lambda pkt: … those declarations do carry is the ListField's own length, not the item's.

Worth being explicit that this is a latent trap rather than a live defect: no capture parses differently today, and the fix would be byte-identical on everything in examples/captures/. It matters because the trap is invisible at the call site — someone adding a ListField item with a length_callback, which the field supports and which the sibling branch honours, gets it silently ignored.

Dates to 86966fd7b (2023-04-13). Surfaced by a Copilot review on #432, where it was declined as out of scope: the line is pre-existing and the PR only added a progress guard beneath it.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIssues reporting a defect (set by the bug report template; a default, not an assessment)

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions