Skip to content

Fix CR LF handling in FramingParser#830

Open
danieleggert wants to merge 3 commits into
mainfrom
framing-parser-cr-lf
Open

Fix CR LF handling in FramingParser#830
danieleggert wants to merge 3 commits into
mainfrom
framing-parser-cr-lf

Conversation

@danieleggert

Copy link
Copy Markdown
Collaborator

Fix a remotely-triggerable crash in FramingParser when a bare CR ends one inbound buffer and a bare LF terminates the next.

Motivation:

FramingParser frames client commands server-side (FrameDecoderIMAPServerHandler). When a buffer ends on a bare CR, it completes that frame but enters the .ignoreFirst strategy, since the partner LF may arrive in the next read.

That strategy was only cleared if the next byte was actually a leading LF. If the next buffer instead began with content terminated by a bare LF (e.g. A1 NOOP\r then A2 NOOP\n), the stale strategy meant the trailing LF reached processLineBreakByte_state with frameLength > 1 and tripped precondition(self.frameLength == 1), aborting the whole process. As FrameDecoder sits ahead of any auth handler, any connecting client could trigger this.

This is the command-side counterpart to the response-side split-CRLF fix in #812: FramingParser already handled split CRLF via .ignoreFirst; this repairs a latent bug in that machinery.

Modifications:

In readByte_state_normalTraversal, reset .ignoreFirst to .includeInFrame when a non-LF byte is consumed — a fresh frame has begun, so a later LF is a genuine terminator. This is the fix.

As defense-in-depth, replace the precondition with throw InvalidFrame(); the .normalTraversal/.insideQuoted call sites catch it, reset state, and emit a recoverable .invalid frame. With the fix above this path is unreachable in practice. Add tests: targeted FramingParser cases for every bare-CR continuation, plus integration tests asserting a well-formed stream parses to the same CommandStreamParts — and never crashes — at every single- and pair-of-byte split.

Result:

A bare CR at a buffer boundary followed by bare-LF-terminated content now frames cleanly instead of crashing. Well-formed input parses identically regardless of how it's split, and non-conforming bare-CR / bare-LF terminators are tolerated. No previously-successful parse changes, and there's no API/ABI change — the throwing additions are confined to private methods.

@danieleggert danieleggert requested a review from mdiep June 7, 2026 09:53
@danieleggert danieleggert enabled auto-merge (squash) June 8, 2026 07:22
@danieleggert danieleggert added the semver/none No version bump required. label Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver/none No version bump required.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant