Skip to content

Fix IndexOutOfBoundsException in StompSubframeDecoder on heartbeat#16539

Merged
normanmaurer merged 1 commit into
netty:4.1from
daguimu:fix/stomp-heartbeat-ioobe-10678
Mar 25, 2026
Merged

Fix IndexOutOfBoundsException in StompSubframeDecoder on heartbeat#16539
normanmaurer merged 1 commit into
netty:4.1from
daguimu:fix/stomp-heartbeat-ioobe-10678

Conversation

@daguimu
Copy link
Copy Markdown
Contributor

@daguimu daguimu commented Mar 25, 2026

Problem

StompSubframeDecoder.skipControlCharacters() reads bytes in an infinite loop without checking if the buffer has readable bytes remaining. When a STOMP heartbeat frame (just a LF byte) arrives, the method consumes the control character and then attempts to read beyond the buffer, causing an IndexOutOfBoundsException.

Root Cause

The skipControlCharacters method has an unconditional buffer.readByte() call inside a for (;;) loop with no buffer.isReadable() guard. When the buffer contains only control characters (heartbeat), all bytes are consumed and the next readByte() fails.

Fix

Add a buffer.isReadable() check before each readByte() call. When the buffer is exhausted (heartbeat-only data), the method returns gracefully and the decoder waits for more data.

Tests Added

  • testHeartbeatOnlyDoesNotThrowException - Single LF heartbeat byte does not cause exception
  • testMultipleHeartbeatsDoNotThrowException - Multiple consecutive LF bytes handled correctly
  • testCarriageReturnLineFeedHeartbeat - CR+LF heartbeat handled correctly
  • testHeartbeatFollowedByFrame - Heartbeat before a real STOMP frame, frame decodes correctly
  • testHeartbeatBetweenFrames - Heartbeat between two frames, both decode correctly
  • testHeartbeatSentSeparatelyThenFrame - Heartbeat in separate write, then frame in second write

Impact

Minimal - only adds a single isReadable() guard. No behavioral change for normal STOMP frames.

Fixes #10678

@normanmaurer
Copy link
Copy Markdown
Member

@normanmaurer normanmaurer added this to the 4.2.12.Final milestone Mar 25, 2026
@normanmaurer normanmaurer added needs-cherry-pick-4.1 This PR should be cherry-picked to 4.1 once merged. needs-cherry-pick-5.0 This PR should be cherry-picked to 5.0 once merged. labels Mar 25, 2026
@daguimu
Copy link
Copy Markdown
Contributor Author

daguimu commented Mar 25, 2026

@daguimu did you sign our ICLA yet ? https://netty.io/s/icla

Yes, I just signed it! 😊

… heartbeat frames

StompSubframeDecoder.skipControlCharacters() reads bytes in a loop
without checking if the buffer has readable bytes remaining. When a
STOMP heartbeat (just a LF byte) arrives, the method consumes the
control character and then attempts to read beyond the buffer, causing
an IndexOutOfBoundsException.

Add a buffer.isReadable() check before each readByte() call so that
heartbeat-only data is handled gracefully without throwing.

Fixes netty#10678
@daguimu daguimu force-pushed the fix/stomp-heartbeat-ioobe-10678 branch from 730f619 to 4cdc3d8 Compare March 25, 2026 08:25
@daguimu
Copy link
Copy Markdown
Contributor Author

daguimu commented Mar 25, 2026

I've rebased onto the latest 4.1. This PR is ready for merge. Thanks for the review!

@normanmaurer normanmaurer added needs-cherry-pick-4.2 This PR should be cherry-picked to 4.2 once merged. and removed needs-cherry-pick-4.1 This PR should be cherry-picked to 4.1 once merged. labels Mar 25, 2026
@normanmaurer normanmaurer merged commit 6f6ac4b into netty:4.1 Mar 25, 2026
18 checks passed
@normanmaurer
Copy link
Copy Markdown
Member

@daguimu thanks a lot.

netty-project-bot pushed a commit that referenced this pull request Mar 25, 2026
…16539)

## Problem

`StompSubframeDecoder.skipControlCharacters()` reads bytes in an
infinite loop without checking if the buffer has readable bytes
remaining. When a STOMP heartbeat frame (just a LF byte) arrives, the
method consumes the control character and then attempts to read beyond
the buffer, causing an IndexOutOfBoundsException.

## Root Cause

The `skipControlCharacters` method has an unconditional
`buffer.readByte()` call inside a `for (;;)` loop with no
`buffer.isReadable()` guard. When the buffer contains only control
characters (heartbeat), all bytes are consumed and the next `readByte()`
fails.

## Fix

Add a `buffer.isReadable()` check before each `readByte()` call. When
the buffer is exhausted (heartbeat-only data), the method returns
gracefully and the decoder waits for more data.

## Tests Added

- `testHeartbeatOnlyDoesNotThrowException` - Single LF heartbeat byte
does not cause exception
- `testMultipleHeartbeatsDoNotThrowException` - Multiple consecutive LF
bytes handled correctly
- `testCarriageReturnLineFeedHeartbeat` - CR+LF heartbeat handled
correctly
- `testHeartbeatFollowedByFrame` - Heartbeat before a real STOMP frame,
frame decodes correctly
- `testHeartbeatBetweenFrames` - Heartbeat between two frames, both
decode correctly
- `testHeartbeatSentSeparatelyThenFrame` - Heartbeat in separate write,
then frame in second write

## Impact

Minimal - only adds a single `isReadable()` guard. No behavioral change
for normal STOMP frames.

Fixes #10678

(cherry picked from commit 6f6ac4b)
@netty-project-bot
Copy link
Copy Markdown
Contributor

Auto-port PR for 4.2: #16543

@netty-project-bot
Copy link
Copy Markdown
Contributor

Could not create auto-port PR.
Got conflicts when cherry-picking onto 5.0.

@github-actions github-actions Bot removed the needs-cherry-pick-4.2 This PR should be cherry-picked to 4.2 once merged. label Mar 25, 2026
chrisvest pushed a commit to chrisvest/netty that referenced this pull request Mar 25, 2026
…etty#16539)

## Problem

`StompSubframeDecoder.skipControlCharacters()` reads bytes in an
infinite loop without checking if the buffer has readable bytes
remaining. When a STOMP heartbeat frame (just a LF byte) arrives, the
method consumes the control character and then attempts to read beyond
the buffer, causing an IndexOutOfBoundsException.

## Root Cause

The `skipControlCharacters` method has an unconditional
`buffer.readByte()` call inside a `for (;;)` loop with no
`buffer.isReadable()` guard. When the buffer contains only control
characters (heartbeat), all bytes are consumed and the next `readByte()`
fails.

## Fix

Add a `buffer.isReadable()` check before each `readByte()` call. When
the buffer is exhausted (heartbeat-only data), the method returns
gracefully and the decoder waits for more data.

## Tests Added

- `testHeartbeatOnlyDoesNotThrowException` - Single LF heartbeat byte
does not cause exception
- `testMultipleHeartbeatsDoNotThrowException` - Multiple consecutive LF
bytes handled correctly
- `testCarriageReturnLineFeedHeartbeat` - CR+LF heartbeat handled
correctly
- `testHeartbeatFollowedByFrame` - Heartbeat before a real STOMP frame,
frame decodes correctly
- `testHeartbeatBetweenFrames` - Heartbeat between two frames, both
decode correctly
- `testHeartbeatSentSeparatelyThenFrame` - Heartbeat in separate write,
then frame in second write

## Impact

Minimal - only adds a single `isReadable()` guard. No behavioral change
for normal STOMP frames.

Fixes netty#10678

(cherry picked from commit 6f6ac4b)
@chrisvest
Copy link
Copy Markdown
Member

5.0 port: #16552

@chrisvest chrisvest removed the needs-cherry-pick-5.0 This PR should be cherry-picked to 5.0 once merged. label Mar 25, 2026
chrisvest added a commit that referenced this pull request Mar 27, 2026
…artbeat (#16539) (#16552)

5.0 port of #16539

The code is already correct in the 5.0 branch, but it can't hurt to
bring over the tests.

---

## Problem

`StompSubframeDecoder.skipControlCharacters()` reads bytes in an
infinite loop without checking if the buffer has readable bytes
remaining. When a STOMP heartbeat frame (just a LF byte) arrives, the
method consumes the control character and then attempts to read beyond
the buffer, causing an IndexOutOfBoundsException.

## Root Cause

The `skipControlCharacters` method has an unconditional
`buffer.readByte()` call inside a `for (;;)` loop with no
`buffer.isReadable()` guard. When the buffer contains only control
characters (heartbeat), all bytes are consumed and the next `readByte()`
fails.

## Fix

Add a `buffer.isReadable()` check before each `readByte()` call. When
the buffer is exhausted (heartbeat-only data), the method returns
gracefully and the decoder waits for more data.

## Tests Added

- `testHeartbeatOnlyDoesNotThrowException` - Single LF heartbeat byte
does not cause exception
- `testMultipleHeartbeatsDoNotThrowException` - Multiple consecutive LF
bytes handled correctly
- `testCarriageReturnLineFeedHeartbeat` - CR+LF heartbeat handled
correctly
- `testHeartbeatFollowedByFrame` - Heartbeat before a real STOMP frame,
frame decodes correctly
- `testHeartbeatBetweenFrames` - Heartbeat between two frames, both
decode correctly
- `testHeartbeatSentSeparatelyThenFrame` - Heartbeat in separate write,
then frame in second write

## Impact

Minimal - only adds a single `isReadable()` guard. No behavioral change
for normal STOMP frames.

Fixes #10678

(cherry picked from commit 6f6ac4b)

Co-authored-by: Guimu <[email protected]>
normanmaurer pushed a commit that referenced this pull request Mar 27, 2026
…on heartbeat (#16543)

Auto-port of #16539 to 4.2
Cherry-picked commit: 6f6ac4b

---
## Problem

`StompSubframeDecoder.skipControlCharacters()` reads bytes in an
infinite loop without checking if the buffer has readable bytes
remaining. When a STOMP heartbeat frame (just a LF byte) arrives, the
method consumes the control character and then attempts to read beyond
the buffer, causing an IndexOutOfBoundsException.

## Root Cause

The `skipControlCharacters` method has an unconditional
`buffer.readByte()` call inside a `for (;;)` loop with no
`buffer.isReadable()` guard. When the buffer contains only control
characters (heartbeat), all bytes are consumed and the next `readByte()`
fails.

## Fix

Add a `buffer.isReadable()` check before each `readByte()` call. When
the buffer is exhausted (heartbeat-only data), the method returns
gracefully and the decoder waits for more data.

## Tests Added

- `testHeartbeatOnlyDoesNotThrowException` - Single LF heartbeat byte
does not cause exception
- `testMultipleHeartbeatsDoNotThrowException` - Multiple consecutive LF
bytes handled correctly
- `testCarriageReturnLineFeedHeartbeat` - CR+LF heartbeat handled
correctly
- `testHeartbeatFollowedByFrame` - Heartbeat before a real STOMP frame,
frame decodes correctly
- `testHeartbeatBetweenFrames` - Heartbeat between two frames, both
decode correctly
- `testHeartbeatSentSeparatelyThenFrame` - Heartbeat in separate write,
then frame in second write

## Impact

Minimal - only adds a single `isReadable()` guard. No behavioral change
for normal STOMP frames.

Fixes #10678

Co-authored-by: Guimu <[email protected]>
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.

4 participants