This is an automated email from the ASF dual-hosted git repository.

wwbmmm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new 04b39291 check chunk header bounds in ubring recv paths (#3497)
04b39291 is described below

commit 04b39291382daeb3e269ec2dac32197646a0682e
Author: UB <[email protected]>
AuthorDate: Sun Aug 30 19:26:09 2026 +0530

    check chunk header bounds in ubring recv paths (#3497)
---
 src/brpc/ubshm/ub_ring.cpp    | 12 ++++++++++++
 src/brpc/ubshm/ub_ring.h      | 15 +++++++++++++++
 test/brpc_ubring_unittest.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+)

diff --git a/src/brpc/ubshm/ub_ring.cpp b/src/brpc/ubshm/ub_ring.cpp
index cdc346ed..ea342673 100644
--- a/src/brpc/ubshm/ub_ring.cpp
+++ b/src/brpc/ubshm/ub_ring.cpp
@@ -424,6 +424,12 @@ int UBRing::UbrTrxRecvBlockMode(uint8_t *dest, uint32_t 
buf_len)
         }
         uint8_t chunk_msg_len = current_chunk->header[UBR_MSG_LEN_INDEX];
         uint8_t cur_index = current_chunk->header[UBR_MSG_CUR_INDEX];
+        if (UNLIKELY(!IsRecvChunkHeaderValid(chunk_msg_len, cur_index))) {
+            LOG(ERROR) << "Trx recv failed, invalid chunk header msg_len="
+                       << (uint32_t)chunk_msg_len << " cur_index=" << 
(uint32_t)cur_index;
+            errno = EBADMSG;
+            return UBRING_ERR;
+        }
         uint8_t available_data = chunk_msg_len - cur_index;
 
         int32_t copy_len = (remaining_len < available_data) ? remaining_len : 
available_data;
@@ -1081,6 +1087,12 @@ ssize_t UBRing::StartReadv(UbrTrx *trx, const struct 
iovec *iov, int iovcnt, siz
         }
         uint8_t chunk_msg_len = current_chunk->header[UBR_MSG_LEN_INDEX];
         uint8_t cur_index = current_chunk->header[UBR_MSG_CUR_INDEX];
+        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;
+        }
         uint8_t recv_len =
             remain_buf_len > (size_t)(chunk_msg_len - cur_index) ? 
(chunk_msg_len - cur_index) : (uint8_t)remain_buf_len;
         while (iov_index < iovcnt && recv_len > 0) {
diff --git a/src/brpc/ubshm/ub_ring.h b/src/brpc/ubshm/ub_ring.h
index 412c7598..0a02dd2f 100644
--- a/src/brpc/ubshm/ub_ring.h
+++ b/src/brpc/ubshm/ub_ring.h
@@ -160,6 +160,21 @@ public:
         return UBRING_OK;
     }
 
+    // The message length and current offset of a received chunk are read out
+    // of the receive ring, which the remote peer writes. The peer is trusted
+    // to keep msg_len within the payload capacity and cur_index within msg_len
+    // (the send path clamps msg_len to UBR_MSG_PAYLOAD_LEN), but nothing
+    // enforces that on the receive side. When msg_len > UBR_MSG_PAYLOAD_LEN or
+    // cur_index > msg_len, reading `payload.inner + cur_index' for
+    // `msg_len - cur_index' bytes runs past the 60-byte payload (the uint8_t
+    // subtraction also wraps when cur_index > msg_len), so validate the header
+    // before using it.
+    static inline bool IsRecvChunkHeaderValid(uint8_t chunk_msg_len,
+                                              uint8_t cur_index)
+    {
+        return chunk_msg_len <= UBR_MSG_PAYLOAD_LEN && cur_index <= 
chunk_msg_len;
+    }
+
     static inline void UpdateDataQTail(UbrTrx *trx)
     {
         ((UbrDataStatusQMsg *)trx->ubr_rx.remote_data_status_q.addr)->tail = 
trx->ubr_rx.read_pos;
diff --git a/test/brpc_ubring_unittest.cpp b/test/brpc_ubring_unittest.cpp
index 53bb0a9b..d3d0a7ce 100644
--- a/test/brpc_ubring_unittest.cpp
+++ b/test/brpc_ubring_unittest.cpp
@@ -28,6 +28,8 @@
 #include "brpc/ubshm/shm/shm_def.h"
 #include "brpc/ubshm/shm/shm_mgr.h"
 #include "brpc/ubshm/ub_ring_manager.h"
+#include "brpc/ubshm/ub_ring.h"
+#include "brpc/ubshm/ubr_msg.h"
 
 namespace brpc {
 namespace ubring {
@@ -245,6 +247,46 @@ TEST_F(UBShmEndpointTest, reset_is_idempotent) {
     _ep->Reset();
 }
 
+// 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));
+}
+
+// A crafted chunk laid out exactly like one in the ring: the guard rejects it
+// so the recv loop never reaches the over-reading memcpy.
+TEST(UBRingRecvChunkHeaderTest, crafted_chunk_is_rejected) {
+    brpc::ubring::UbrMsgFormat chunk;
+    memset(&chunk, 0xAB, sizeof(chunk));
+    chunk.header[UBR_MSG_LEN_INDEX] = 255;  // peer claims 255 bytes in a 
60-byte payload
+    chunk.header[UBR_MSG_CUR_INDEX] = 0;
+    EXPECT_FALSE(brpc::ubring::UBRing::IsRecvChunkHeaderValid(
+        chunk.header[UBR_MSG_LEN_INDEX], chunk.header[UBR_MSG_CUR_INDEX]));
+
+    chunk.header[UBR_MSG_LEN_INDEX] = UBR_MSG_PAYLOAD_LEN;
+    chunk.header[UBR_MSG_CUR_INDEX] = 0;
+    EXPECT_TRUE(brpc::ubring::UBRing::IsRecvChunkHeaderValid(
+        chunk.header[UBR_MSG_LEN_INDEX], chunk.header[UBR_MSG_CUR_INDEX]));
+}
+
 #else
 
 TEST(UbringDisabledTest, skip) {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to