Skip to content

fix(reassembly): pin DTLS handshake buffers at declared message length - #2469

Closed
JarryShaw wants to merge 2 commits into
feat/2440-dtls-fragment-reassemblyfrom
fix/2468-dtls-buffer-cap
Closed

JarryShaw wants to merge 2 commits into
feat/2440-dtls-fragment-reassemblyfrom
fix/2468-dtls-buffer-cap

Conversation

@JarryShaw

@JarryShaw JarryShaw commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

Please follow the guide below

  • Searched for similar pull requests
  • Followed the coding style (make pylint, make mypy, make isort)
  • make test passes, and a test case covers the change
  • Added a changelog entry under docs/source/changelog/ and regenerated CHANGELOG.md, if the change is user-visible — N/A, centralised

What is the purpose of your pull request?

[x] fix — corrects a defect · [ ] feat — adds a feature · [ ] perf — changes performance, not behaviour · [ ] refactor — changes neither behaviour nor performance · [ ] test — tests only · [ ] docs — documentation only · [ ] ci — workflows or build tooling · [ ] release — bumps the version or rolls up a distribution · [ ] chore — anything else · [ ] const — regenerates constant or registry tables, members keeping their numeric values

Description of your pull request and other information

Stacked on #2451 (reassembly for #2440).

The base already sizes each DTLS handshake message buffer at its declared length (bytearray(length), RFC 6347 section 4.2.3), so at most 16 MiB. This PR pins that with tests.

New tests: a partial message holds its declared length; with strict=False, a timeout reports that length with gaps zero-filled; overlapping fragments reassemble byte-exact in forward, reverse and shuffled order.

Refs #2468

@JarryShaw JarryShaw added fix Pull requests that fix a defect (fix: subject prefix) review: running A cross-review is in flight against the current head - no verdict yet labels Oct 30, 2026
@github-actions github-actions Bot added this to the PyPCAPKit v1.5 milestone Oct 30, 2026
@JarryShaw JarryShaw moved this to In review in PyPCAPKit Oct 30, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

CONFLICTING because it is stacked on #2451, which is stacked on #2441's in-review restructure. Unblocks when #2451 is rebased onto #2441's settled head; this rebases onto #2451 right after. Its own review label stays where it is until then.

@JarryShaw JarryShaw added review: pending No verdict for the current head - never reviewed, or the head moved since the last one and removed review: running A cross-review is in flight against the current head - no verdict yet labels Oct 30, 2026
@JarryShaw
JarryShaw force-pushed the feat/2440-dtls-fragment-reassembly branch from 3dd077d to bcadcbf Compare October 31, 2026 00:07
@JarryShaw
JarryShaw force-pushed the fix/2468-dtls-buffer-cap branch from 1a5126d to c1ea7de Compare October 31, 2026 00:14
@JarryShaw

Copy link
Copy Markdown
Owner Author

Rebased onto #2451's new head bcadcbf62: old head 1a5126dd0, new head c1ea7ded3.
Conflict in test_dtls_handshake_reassembly_2440_unit.py only: kept both the round-5 idle-deadline test and this PR's cap test.
Patch-id differs only by one import re-wrap hunk from the old commit, which now matches the base. The source change is otherwise identical.
Fixed the cap test: it lowered RLIMIT_AS's hard limit to 1 GiB, and the restore could not raise it back, so a 512 MiB soft cap leaked into later tests and crashed the session. It now lowers only the soft limit.
Tests: make_samples.py first; tests/foundation/reassembly/, test_ssl_tls_dtls_unit.py and test_tls_dtls_roundtrip_unit.py pass (261 passed).

@JarryShaw JarryShaw added review: running A cross-review is in flight against the current head - no verdict yet and removed review: pending No verdict for the current head - never reviewed, or the head moved since the last one labels Oct 31, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

NEEDS CHANGES (Sonnet, c1ea7ded3). The cap bounds one message's memory (about 67 KB peak against 16.78 MB on the base).

  • Defect: a complete message over the cap is silently truncated to 65,535 octets and then emitted as a bogus TIMEOUT. The IP precedent says an over-long message is never COMPLETE. OpenSSL allows DTLS messages up to 100 KiB.
  • Two tests/project failures that pass on the base: a :issue: role in a plain # comment at dtls.py:242 (use #2468), and DTLS_MAX_LENGTH = 65535 re-spelling IP_MAX_LENGTH.
  • The new test only catches the regression when the process already holds about 490 MiB. Assert on the fragment's raw length instead of RLIMIT_AS.
  • Body: "20 tests" should be 21; "pylint 9.45" should be 10.00.

@JarryShaw JarryShaw added review: needs-changes Cross-review at the current head says changes are required; see the verdict comment review: running A cross-review is in flight against the current head - no verdict yet and removed review: running A cross-review is in flight against the current head - no verdict yet review: needs-changes Cross-review at the current head says changes are required; see the verdict comment labels Oct 31, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

NEEDS CHANGES (Sonnet, 5706d8fcf). The over-cap fix is sound: over-cap messages produce nothing and use 1 KiB, and non-over-cap output is byte-identical to base. The 100 KiB value matches OpenSSL's default.

@JarryShaw JarryShaw added review: needs-changes Cross-review at the current head says changes are required; see the verdict comment needs: decision Waiting on the maintainer to decide — not blocked by other work and removed review: running A cross-review is in flight against the current head - no verdict yet labels Oct 31, 2026
@JarryShaw JarryShaw moved this from In review to Needs decision in PyPCAPKit Oct 31, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Question: why cap it? I think we only cap IP bc of RFC specs, but we do not cap on TCP cause there's none and if there's non on DTLS, we should not cap as well.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Good question. The cap is a workaround for one design choice, not an RFC limit.

  • TCP grows its buffer as payload arrives (pcapkit/foundation/reassembly/tcp.py), bounded by the window. No cap needed.
  • IP pre-allocates IP_MAX_LENGTH, which is the protocol's own 16-bit maximum, so the cap is inherent.
  • DTLS pre-allocates the full declared length (up to 16 MiB, from the 24-bit field). That is why it needs a cap.

Recommendation: drop the cap and grow the DTLS buffer with received data, as TCP does. Memory then tracks bytes actually received, not what a packet claims, and the bug goes away without a limit. Options for the owner: (a) this, or (b) keep a cap as policy in utilities/config.py.

@JarryShaw

Copy link
Copy Markdown
Owner Author

yea, i think we should actually use TCP's reassembly algorithm as it is more modern. if possible i'd consider rework IP's as well. also, i'm wondering if there's any RFC specify if any of these protocol's reassembly should be implementation. if so, we should follow directly. if none, we take the best algorithm.

@JarryShaw JarryShaw added review: needs-changes Cross-review at the current head says changes are required; see the verdict comment and removed review: running A cross-review is in flight against the current head - no verdict yet labels Oct 31, 2026
@JarryShaw
JarryShaw force-pushed the fix/2468-dtls-buffer-cap branch from 129b8ce to 483a854 Compare October 31, 2026 03:02
@JarryShaw JarryShaw changed the title fix(reassembly): DTLS reassembly buffers by declared message length fix(reassembly): pin DTLS handshake buffers at declared message length Oct 31, 2026
@JarryShaw JarryShaw added review: pending No verdict for the current head - never reviewed, or the head moved since the last one review: running A cross-review is in flight against the current head - no verdict yet and removed review: needs-changes Cross-review at the current head says changes are required; see the verdict comment review: pending No verdict for the current head - never reviewed, or the head moved since the last one labels Oct 31, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

NEEDS CHANGES (Sonnet, 483a854f0). Code is unchanged and verified: the buffer is bytearray(length), and both new tests pass on the base too.

@JarryShaw JarryShaw added review: needs-changes Cross-review at the current head says changes are required; see the verdict comment and removed review: running A cross-review is in flight against the current head - no verdict yet labels Oct 31, 2026
@JarryShaw
JarryShaw force-pushed the fix/2468-dtls-buffer-cap branch from 483a854 to 81b3d9f Compare October 31, 2026 03:18
@JarryShaw
JarryShaw force-pushed the feat/2440-dtls-fragment-reassembly branch from 0fca5b4 to f734108 Compare October 31, 2026 03:18
@JarryShaw JarryShaw added review: pending No verdict for the current head - never reviewed, or the head moved since the last one and removed review: needs-changes Cross-review at the current head says changes are required; see the verdict comment labels Oct 31, 2026
- add foundation.reassembly.dtls.DTLS, keyed by UDP 5-tuple and DTLS epoch
- DTLS.read decodes the fragment completing a handshake message into
  ClientHello/ServerHello
- overlapping fragments are accepted (RFC 6347 4.2.3)
- opt-in only; Extractor-level wiring left as a follow-up
- isort-clean reassembly and DTLS modules
- DTLS.read reassembles only the datagram's first record (Known Limitations)

Tests: tests/foundation/reassembly/ -- 197 passed, 897 subtests.
@JarryShaw
JarryShaw force-pushed the feat/2440-dtls-fragment-reassembly branch from f734108 to 65d7935 Compare October 31, 2026 03:35
@JarryShaw

Copy link
Copy Markdown
Owner Author

Held: Actions queue at 42 (above the hold line). The restack onto #2451's new head (65d79350a) runs when the queue drops below 40.

- Each message buffer is allocated at its declared length, the unit RFC
  6347 section 4.2.3 buffers, bounded at 16 MiB by the 24-bit length field.
  The base does this; the class docstring documents it.
- Overlapping fragments are accepted through the RFC 815 hole list.
- Tests: a partial message holds its declared length, and with strict=False
  a timeout reports that length with the gaps zero-filled. Overlapping
  fragments reassemble byte-exact in forward, reverse and shuffled order.
@JarryShaw
JarryShaw force-pushed the fix/2468-dtls-buffer-cap branch from 81b3d9f to 0fdf448 Compare October 31, 2026 03:46
@JarryShaw JarryShaw added review: running A cross-review is in flight against the current head - no verdict yet and removed review: pending No verdict for the current head - never reviewed, or the head moved since the last one labels Oct 31, 2026
@JarryShaw
JarryShaw force-pushed the feat/2440-dtls-fragment-reassembly branch from 65d7935 to 09ead29 Compare October 31, 2026 03:53
@JarryShaw

Copy link
Copy Markdown
Owner Author

Held: Actions queue at 47 (above the hold line). The fix round dispatches when the queue drops below 40.

@JarryShaw

Copy link
Copy Markdown
Owner Author

NEEDS CHANGES (Sonnet, 0fdf448b6). The two new tests pass on the base, and replaying 0fdf448 alone onto #2451's head merges cleanly.

  • Head still carries the old base commit 65d79350a, so the PR diff includes its wording edits; restack to only 0fdf448.
  • Tick test, not fix: the PR pins existing behaviour and changes no code.
  • Tick the checklist boxes only for what was run; the body says the tests pass but none are ticked.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Question for the owner, paraphrased: the 64KiB cap is dropped (buffers are sized by declared length on origin). Should this PR and #2468 be closed, or re-scoped to what origin 0fdf448 does? Not closing either until you answer.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Decision needed: close #2469 and #2468, or re-scope. Options: (a) close both, since the 64KiB cap is dropped and origin 0fdf448 already sizes buffers by declared length; (b) re-scope #2468 to tests that pin declared-length sizing, and close #2469. Recommendation: (a). Nothing to push from the cap work.

@JarryShaw

Copy link
Copy Markdown
Owner Author

take (a)

@JarryShaw

Copy link
Copy Markdown
Owner Author

Closing per owner ruling: the 64KiB cap is not needed under the HDL implementation (see #2468).

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

Labels

fix Pull requests that fix a defect (fix: subject prefix)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant