Skip to content

Fix 1-byte out-of-bounds read in receive() scan loop - #108

Merged
lathoub merged 1 commit into
lathoub:masterfrom
94xhn:fix/receive-scanloop-oob-read
Jul 14, 2026
Merged

Fix 1-byte out-of-bounds read in receive() scan loop#108
lathoub merged 1 commit into
lathoub:masterfrom
94xhn:fix/receive-scanloop-oob-read

Conversation

@94xhn

@94xhn 94xhn commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Bug

In BLEMIDI_Transport::receive(), the scan loop that advances rPtr past trailing data bytes checks the buffer content before checking the bounds:

while ((buffer[rPtr + 1] < MIDI_TYPE) && (rPtr < (length - 1)))
    rPtr++;

Because && is evaluated left-to-right, buffer[rPtr + 1] is read before rPtr < (length - 1) is checked. When rPtr reaches length - 1 and the final byte(s) are all data bytes (< MIDI_TYPE), this reads buffer[length], one byte past the end of the caller-supplied buffer.

This function is called from every hardware backend (BLEMIDI_ESP32.h, BLEMIDI_ESP32_NimBLE.h, BLEMIDI_Client_ESP32.h, BLEMIDI_ArduinoBLE.h, BLEMIDI_nRF52.h) with a buffer that comes directly off the BLE transport, so this is reachable from an ordinary MIDI message ending near the buffer boundary.

Fix

Swap the && operand order so the bounds check is evaluated first and short-circuits before the array index:

while ((rPtr < (length - 1)) && (buffer[rPtr + 1] < MIDI_TYPE))
    rPtr++;

No other logic changes. Verified against a real 5-byte NoteOn packet (0x80, 0x80, 0x90, 0x3C, 0x64) placed immediately before a guard page: the unmodified code reliably reads one byte past the buffer end, and the one-line fix eliminates the out-of-bounds access while parsing the same packet identically (mBleClass.add(0x90), add(0x3C), add(0x64)).

The scan loop that advances rPtr past running-status data bytes
evaluated buffer[rPtr + 1] before checking rPtr < (length - 1),
due to && operand order. When the trailing data byte(s) of a
packet are all below MIDI_TYPE, this reads one byte past the end
of the receive buffer before the bounds check can short-circuit
it. Swapping the operand order so the bounds check runs first
fixes this; parsing behavior for valid packets is unchanged.
@lathoub

lathoub commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Super nice catch @94xhn . Kudos!!
I happily accept the PR

@lathoub
lathoub merged commit 9cd0db0 into lathoub:master Jul 14, 2026
1 check failed
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.

2 participants