Repository navigation
Conversation
ESBNetwork::read() (non-Linux path, RF24Network.cpp:645-695) explicitly assumes a frame is queued and the
available() check is commented out (:651). If a sketch calls read() without a frame queued — easy to do,
and some of your own examples call read() right after update() in a loop — then:
- bufsize is read from stale bytes at frame_queue + 8 (:673)
- next_frame -= bufsize + 10 pushes next_frame below frame_queue (:682)
- memmove(frame_queue, frame_queue + bufsize + 10 + padding, sizeof(frame_queue) - bufsize) (:690) —
sizeof(frame_queue) - bufsize underflows to a huge size_t.
On a fresh node bufsize is 0 so it's benign, but once any frame has passed through, the leftover header
makes bufsize non-zero and next_frame walks off the front of the buffer; a later received frame is then
written out of bounds. AddressSanitizer reported negative-size-param in the memmove at :690, and a
corrupted next_frame that reads before frame_queue.
In enqueue() the final copy of a reassembled frame into the user queue (RF24Network.cpp:503-506) gates
on:
if ((uint16_t)(MAX_PAYLOAD_SIZE) - (next_frame - frame_queue) >= frag_queue.message_size) {
Two concerns:
- frame_queue is MAIN_BUFFER_SIZE (MAX_PAYLOAD_SIZE + FRAME_HEADER_SIZE), so the limit is understated by
FRAME_HEADER_SIZE here but the unfragmented path at :549-550 correctly uses MAIN_BUFFER_SIZE.
Inconsistent.
- More importantly, MAX_PAYLOAD_SIZE - (next_frame - frame_queue) is unsigned subtraction. If next_frame
is ever past that point (which Finding 1 or a full queue can cause), it underflows and the check
passes, allowing the memcpy at :504-505 to overflow frame_queue.
2bndy5
approved these changes
Oct 29, 2026
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.
Doing some more work with Claude this AM: (sorry/not sorry in advance @2bndy5 )
available() check is commented out (:651). If a sketch calls read() without a frame queued — easy to do,
and some of your own examples call read() right after update() in a loop — then:
On a fresh node bufsize is 0 so it's benign, but once any frame has passed through, the leftover header
makes bufsize non-zero and next_frame walks off the front of the buffer; a later received frame is then
written out of bounds. AddressSanitizer reported negative-size-param in the memmove at :690, and a
corrupted next_frame that reads before frame_queue.
on:
if ((uint16_t)(MAX_PAYLOAD_SIZE) - (next_frame - frame_queue) >= frag_queue.message_size) {
Two concerns: