MQTT: Fix MQTT decoder size check after variable header replay#16916
Merged
chrisvest merged 1 commit intoJun 5, 2026
Merged
Conversation
Motivation: netty#16787 fixed the READ_VARIABLE_HEADER too-long check on 4.2 by replacing a ReplayingDecoderByteBuf.readableBytes() probe with the message size declared by the fixed header. That fix was auto-ported to 4.1 (netty#16838), but a later CVE-2026-44248 security merge re-introduced the broken code on 4.1, so 4.1 still carries the regression: int initialAvailableBytes = buffer.readableBytes(); ... if (initialAvailableBytes < maxBytesInMessage) { throw signal; // REPLAY } else { bailOut = true; // too long } Because MqttDecoder extends ReplayingDecoder, buffer is a ReplayingDecoderByteBuf whose readableBytes() returns Integer.MAX_VALUE - readerIndex rather than the bytes actually buffered. With the default maxBytesInMessage of 8092 the initialAvailableBytes < maxBytesInMessage check is therefore effectively always false, so when decodeVariableHeader asks for a REPLAY because the variable header has not fully arrived, the decoder bails out and rejects the message with a TooLongFrameException instead of waiting for the rest. Modification: Re-apply the netty#16787 fix on 4.1: drop the initialAvailableBytes / readableBytes() probe and decide based on bytesRemainingBeforeVariableHeader, the remaining length declared by the fixed header. A genuinely oversized message is still rejected; an incomplete one now correctly REPLAYs. Result: A message whose variable header arrives in chunks is decoded once it is complete instead of being rejected as too long, while declared-oversize messages still fail with TooLongFrameException. Ports the two regression tests from netty#16787. All codec-mqtt tests pass locally.
chrisvest
approved these changes
Jun 5, 2026
Member
|
Thanks! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
#16787 fixed the
READ_VARIABLE_HEADERtoo-long check on the 4.2 branch by replacing aReplayingDecoderByteBuf.readableBytes()probe with the message size declared by the fixed header. That fix was auto-ported to 4.1 in #16838, but a later CVE-2026-44248 security merge re-introduced the broken code on 4.1, so the 4.1 branch still carries the regression:Because
MqttDecoder extends ReplayingDecoder,bufferis aReplayingDecoderByteBufwhosereadableBytes()returnsInteger.MAX_VALUE - readerIndexrather than the bytes actually buffered. With the defaultmaxBytesInMessageof8092, theinitialAvailableBytes < maxBytesInMessagecheck is therefore effectively always false. So whendecodeVariableHeaderasks for aREPLAYbecause the variable header has not fully arrived yet, the decoder takes thebailOutbranch and rejects the message with aTooLongFrameExceptioninstead of waiting for the rest — a valid message whose variable header is split across reads is dropped.Modification
Re-apply the #16787 fix on 4.1: drop the
initialAvailableBytes/readableBytes()probe and decide based onbytesRemainingBeforeVariableHeader(the remaining length declared by the fixed header). A genuinely oversized message is still rejected by the existingbytesRemainingBeforeVariableHeader > maxBytesInMessagecheck; an incomplete one now correctlyREPLAYs.Result
A message whose variable header arrives in chunks is decoded once complete, instead of being rejected as too long, while declared-oversize messages still fail with
TooLongFrameException. The two regression tests from #16787 are ported here. Allcodec-mqtttests pass locally.Note: this targets 4.1 only and intentionally carries no cherry-pick label — 4.2 already has the fix via #16787. Found while addressing @chrisvest's review comment on #16813 about the same
readableBytes()antipattern.