Skip to content

protocol: compare the NS prefix in place rather than through an uppercased copy (ibx#365) - #366

Open
userFRM wants to merge 1 commit into
deepentropy:mainfrom
userFRM:fix/ns-prefix-slice-panic
Open

protocol: compare the NS prefix in place rather than through an uppercased copy (ibx#365)#366
userFRM wants to merge 1 commit into
deepentropy:mainfrom
userFRM:fix/ns-prefix-slice-panic

Conversation

@userFRM

@userFRM userFRM commented Jul 30, 2026

Copy link
Copy Markdown

Problem

ns_parse uppercased the payload to test for a MISC prefix, then sliced the original at byte 4.

Uppercasing is not length-preserving: U+0131 and U+017F each occupy two bytes and uppercase to one ASCII byte. So a payload whose first four characters uppercase to MISC while occupying more than four bytes passed the check with byte 4 inside a character, and slicing a &str there panics.

Scope

The framed receive path does not reach it. is_ns_text admits only an ASCII digit or a literal MISC before dispatching here, so this hardens the parser for callers that hold it directly rather than closing a live crash.

What this changes

The prefix is compared in place against an ASCII MISC, and the remainder is taken with a checked slice.

The behaviour change is confined to the panic. The old code also recognised non-ASCII spellings, but it removed four bytes and each of those characters occupies two — so such a payload either put byte 4 inside a character, or shifted the cut so the remainder began mid-prefix and failed to parse. None of them ever parsed.

Also drops an allocation per inbound NS message on that path.

Tests

  • a_prefix_that_changes_length_when_uppercased_does_not_panic — the hostile spellings plus the short and empty shapes. Fails by name against the previous parser.
  • a_genuine_misc_prefix_is_still_stripped — the positive control, in any ASCII casing.
  • the_prefix_is_an_ascii_misc_and_nothing_else and stripping_leaves_the_defined_shapes_alone — pin the boundary the prefix rule draws. Both agree with the previous behaviour, which is the point; the guard for the change is the panic test.

Closes #365.

Test plan

  • Mutation: restoring the uppercased-copy parser fails a_prefix_that_changes_length_when_uppercased_does_not_panic by name.
  • Positive control: a_genuine_misc_prefix_is_still_stripped, in any ASCII casing.
  • the_prefix_is_an_ascii_misc_and_nothing_else and stripping_leaves_the_defined_shapes_alone pin the boundary the prefix rule draws; both agree with the previous behaviour, which is the point.
  • cargo check --offline clean on --lib, --lib --features python, --bins, --examples, and each integration target individually.
  • tests/ib_paper_compat compared against a clean checkout of the base commit — identical sorted diagnostic sets.
  • cargo test --offline --lib — only the two known config::expiry_tests failures, which fail on the base commit for missing legacy tzdata (fixed separately in config: resolve the legacy timezone names IB states its times in (ibx#335) #336).

@userFRM
userFRM force-pushed the fix/ns-prefix-slice-panic branch 2 times, most recently from f8d6f05 to 7f2a300 Compare July 30, 2026 12:06
…cased copy

`ns_parse` uppercased the payload to test for a "MISC" prefix, then sliced the original at byte 4. Uppercasing is not length-preserving: U+0131 and U+017F each occupy two bytes and uppercase to one ASCII byte, so a payload whose first four characters uppercase to "MISC" while occupying more than four bytes passed the check with byte 4 inside a character, and slicing a `&str` there panics.

The prefix is now compared in place against an ASCII "MISC", and the remainder is taken with a checked slice.

The framed receive path does not reach that panic — `is_ns_text` admits only an ASCII digit or a literal "MISC" before dispatching here — so this hardens the parser for callers that hold it directly rather than closing a live crash.

The behaviour change is confined to the panic. The old code also recognised non-ASCII spellings, but it removed four bytes and each of those characters occupies two, so such a payload either put byte 4 inside a character or shifted the cut so the remainder began mid-prefix and failed to parse. None of them ever parsed.

Also drops an allocation per inbound NS message on that path.

Closes deepentropy#365.
@userFRM
userFRM force-pushed the fix/ns-prefix-slice-panic branch from 7f2a300 to c6798bf Compare July 30, 2026 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

protocol: an NS payload whose prefix changes length when uppercased panics the hot loop

1 participant