Skip to content

schema: Schema.pack rejects the tuple its own data models declare, breaking parse-then-reconstruct for 16 HIP parameters #476

Description

@JarryShaw

Schema.pack's ListField branch accepts list but not tuple, so no data object a _read_param_* produces can be handed back to its _make_param_*. Sixteen HIP parameters are affected, and the round-trip harness has been recording it as sixteen separate EXPECTED_FAILURES entries rather than one defect.

The three facts, and the writer is the one out of step

The tuple is not an accident on the reader's side — it is the declared, documented data type:

  • pcapkit/protocols/data/internet/hip.py:258 declares group_id: 'tuple[Group, ...]', and the TYPE_CHECKING __init__ stub on the next line repeats it.
  • pcapkit/protocols/internet/hip.py:1216 therefore builds one deliberately: group_id=tuple(schema.groups).
  • pcapkit/protocols/schema/schema.py:615-624 then refuses it:
if isinstance(field, ListField):
    if data is None:
        self.__buffer__[field.name] = b''
    elif isinstance(data, bytes):
        self.__buffer__[field.name] = data
    elif isinstance(data, list):
        self.__buffer__[field.name] = field.pack(data, packet)
    else:
        raise ProtocolUnbound(f'unsupported type {type(data)}')
    continue

So the reader honours its own declared type and the writer rejects it. Describing this as "the reader returns a tuple where the maker needs a list" gets the blame backwards: nothing says the data model should have been list, and changing it would be an API break across every Parameter data class.

Reproduction

$ python -c "...roundtrip(DH_GROUP_LIST case)..."
DH_GROUP_LIST roundtrip -> RECONSTRUCT
fragment: ProtocolUnbound: unsupported type <class 'tuple'>

The schema layer itself is fine — _make_param_dh_group_list(..., group_id=[1,2,3]) packs and DHGroupListParameter.unpack recovers it. The break is only on the parse-then-reconstruct path, which is why unit tests that build schemas directly never see it.

Scope: 16 entries, verified by enumeration

Every EXPECTED_FAILURES entry in tests/protocols/test_option_roundtrip_unit.py whose recorded defect names schema.py:624, all with status='RECONSTRUCT' and fragment unsupported type <class 'tuple'>:

hip-parameter/ACK, ACK_DATA, DH_GROUP_LIST, ESP_TRANSFORM, HIP_CIPHER, HIP_TRANSPORT_MODE, HIT_SUITE_LIST, NAT_TRAVERSAL_MODE, REG_FAILED, REG_INFO, REG_REQUEST, REG_RESPONSE, ROUTE_DST, ROUTE_VIA, TRANSPORT_FORMAT_LIST, VIA_RVS.

That is 16 of the 87 entries — over one in six of the whole table — collapsing to a single root cause. (A review of #475 put the figure at 15 "other" parameters; the enumerated count is 16 in total, so 14 besides the two #475 touches. I enumerated rather than accepting the tally, and the numbers differ, so the 16 above is the one to work from.)

Suggested fix

Accept any non-bytes sequence in that branch rather than list specifically — ListField.pack does not care which it gets. Something in the shape of:

elif isinstance(data, (list, tuple)):
    self.__buffer__[field.name] = field.pack(list(data), packet)

or a broader collections.abc.Sequence test with bytes/str excluded first, if other sequence types are wanted. Either keeps ProtocolUnbound for genuinely unsupported types, which is the branch's real job.

Worth checking as part of the fix, and not verified here: whether the same list-only assumption exists in the unpack direction or in other schema families (PCAP-NG options, MH extensions) that also declare tuple in their data models. I have only enumerated the HIP EXPECTED_FAILURES entries.

Deleting the 16 EXPECTED_FAILURES entries is the acceptance test, but the entries must be deleted and the cases re-enumerated through options.cases(), not inferred from a green suite — a table that tracks the defect in lockstep with the code cannot distinguish a fix from a disguised regression.

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