Skip to content

check chunk header bounds in ubring recv paths - #3497

Merged
wwbmmm merged 1 commit into
apache:masterfrom
ubeddulla:ubring-recv-chunk-bounds
Aug 30, 2026
Merged

check chunk header bounds in ubring recv paths#3497
wwbmmm merged 1 commit into
apache:masterfrom
ubeddulla:ubring-recv-chunk-bounds

Conversation

@ubeddulla

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: resolve N/A

Problem Summary:

UbrTrxRecvBlockMode and StartReadv read a chunk's msg_len and cur_index straight from the receive-ring header that the remote peer writes into shared memory, then memcpy msg_len minus cur_index bytes out of the fixed 60-byte payload.inner without checking either value. A peer that sets msg_len above UBR_MSG_PAYLOAD_LEN (60), or cur_index above msg_len so the uint8_t subtraction wraps, makes that copy read past the payload into the adjacent chunk header and neighbouring ring slots. Under ASAN this shows up as a heap-buffer-overflow read of size 255 at 0 bytes after the 64-byte chunk. The send path already caps msg_len at UBR_MSG_PAYLOAD_LEN, so only the receive side was missing the check.

What is changed and the side effects?

Changed:

Added IsRecvChunkHeaderValid (msg_len <= UBR_MSG_PAYLOAD_LEN and cur_index <= msg_len) and apply it at both receive sites before the copy, rejecting a malformed chunk with errno=EBADMSG and the same UBRING_ERR the existing pre-checks return. Added a regression test in brpc_ubring_unittest.cpp.

Side effects:

  • Performance effects: one comparison per chunk, negligible.

  • Breaking backward compatibility: none. Valid chunks (msg_len <= 60, cur_index <= msg_len) behave exactly as before.


Check List:

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the ubring shared-memory receive paths against malformed or malicious chunk headers by validating msg_len and cur_index before copying from the fixed-size (60-byte) payload, preventing out-of-bounds reads in UbrTrxRecvBlockMode and StartReadv.

Changes:

  • Add UBRing::IsRecvChunkHeaderValid() to validate (msg_len <= UBR_MSG_PAYLOAD_LEN) && (cur_index <= msg_len).
  • Apply the validation in both receive implementations (UbrTrxRecvBlockMode and StartReadv), returning UBRING_ERR with errno=EBADMSG on invalid headers.
  • Add unit tests that cover the validation helper behavior for in-range and out-of-range values.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
test/brpc_ubring_unittest.cpp Adds tests for the new receive chunk header validation helper.
src/brpc/ubshm/ub_ring.h Introduces IsRecvChunkHeaderValid() to guard against invalid receive chunk header fields.
src/brpc/ubshm/ub_ring.cpp Applies the new validation in both recv and readv receive loops before memcpy.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1090 to +1095
if (UNLIKELY(!IsRecvChunkHeaderValid(chunk_msg_len, cur_index))) {
LOG(ERROR) << "Trx readv failed, invalid chunk header msg_len="
<< (uint32_t)chunk_msg_len << " cur_index=" << (uint32_t)cur_index;
errno = EBADMSG;
return UBRING_ERR;
}
Comment on lines +250 to +272
// The receive paths (UbrTrxRecvBlockMode / StartReadv) read `msg_len' and
// `cur_index' out of a chunk header the remote peer writes into the ring, then
// copy `msg_len - cur_index' bytes from the 60-byte `payload.inner'. A peer
// that writes msg_len > 60, or cur_index > msg_len (which underflows the
// uint8_t subtraction), makes that copy over-read the payload into adjacent
// shared memory. IsRecvChunkHeaderValid is the guard both paths now apply.
TEST(UBRingRecvChunkHeaderTest, reject_out_of_range_len_and_index) {
using brpc::ubring::UBRing;
// Legitimate values a well-formed peer produces: full payload, partial
// consume, and the fully-consumed boundary.
EXPECT_TRUE(UBRing::IsRecvChunkHeaderValid(UBR_MSG_PAYLOAD_LEN, 0));
EXPECT_TRUE(UBRing::IsRecvChunkHeaderValid(10, 5));
EXPECT_TRUE(UBRing::IsRecvChunkHeaderValid(0, 0));
EXPECT_TRUE(UBRing::IsRecvChunkHeaderValid(UBR_MSG_PAYLOAD_LEN,
UBR_MSG_PAYLOAD_LEN));
// msg_len past the payload capacity -> over-read source.
EXPECT_FALSE(UBRing::IsRecvChunkHeaderValid(UBR_MSG_PAYLOAD_LEN + 1, 0));
EXPECT_FALSE(UBRing::IsRecvChunkHeaderValid(255, 0));
// cur_index past msg_len -> `msg_len - cur_index' underflows to a large
// uint8_t.
EXPECT_FALSE(UBRing::IsRecvChunkHeaderValid(0, 1));
EXPECT_FALSE(UBRing::IsRecvChunkHeaderValid(10, 20));
}
@wwbmmm

wwbmmm commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

@zchuango Have a look if you have time

@zchuango

Copy link
Copy Markdown
Contributor

@zchuango Have a look if you have time

OK

@zchuango

zchuango commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

@ubeddulla Thanks for fixing this! The bounds checks in both UbrTrxRecvBlockMode and StartReadv close the malformed chunk-header over-read issue. Appreciate the clear regression tests and the contribution.

@wwbmmm
wwbmmm merged commit 04b3929 into apache:master Aug 30, 2026
45 of 46 checks passed
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