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

chenBright 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 3f8479826 reject negative attachment_size in baidu_std 
request/response parsing (#3604)
3f8479826 is described below

commit 3f8479826b258bfb52ed353bc6e99b2dbc4716f3
Author: sahvx655-wq <[email protected]>
AuthorDate: Sat Nov 7 14:48:56 2026 +0530

    reject negative attachment_size in baidu_std request/response parsing 
(#3604)
    
    * reject negative attachment_size in baidu_std request/response parsing
    
    attachment_size is a peer-controlled int32 but both guards only check an 
upper bound, so a negative value reaches res_size - attachment_size (int) and 
overflows for INT32_MIN. Reject attachment_size < 0 at both sites.
    
    * test: cover negative attachment_size rejection in baidu_std request and 
response parsing
    
    * test: release socket creation references in attachment_size tests
---
 src/brpc/policy/baidu_rpc_protocol.cpp |  10 +--
 test/brpc_channel_unittest.cpp         | 126 +++++++++++++++++++++++++++++++++
 2 files changed, 132 insertions(+), 4 deletions(-)

diff --git a/src/brpc/policy/baidu_rpc_protocol.cpp 
b/src/brpc/policy/baidu_rpc_protocol.cpp
index 173ab24fd..6dd6c4cbe 100644
--- a/src/brpc/policy/baidu_rpc_protocol.cpp
+++ b/src/brpc/policy/baidu_rpc_protocol.cpp
@@ -709,9 +709,10 @@ void ProcessRpcRequest(InputMessageBase* msg_base) {
 
         const int req_size = static_cast<int>(msg->payload.size());
         if (meta.has_attachment_size()) {
-            if (req_size < meta.attachment_size()) {
+            if (meta.attachment_size() < 0 ||
+                req_size < meta.attachment_size()) {
                 cntl->SetFailed(EREQUEST,
-                    "attachment_size=%d is larger than request_size=%d",
+                    "Invalid attachment_size=%d (request_size=%d)",
                     meta.attachment_size(), req_size);
                 break;
             }
@@ -1018,9 +1019,10 @@ void ProcessRpcResponse(InputMessageBase* msg_base) {
         const int res_size = msg->payload.length();
         butil::IOBuf* res_buf_ptr = &msg->payload;
         if (meta.has_attachment_size()) {
-            if (meta.attachment_size() > res_size) {
+            if (meta.attachment_size() < 0 ||
+                meta.attachment_size() > res_size) {
                 cntl->SetFailed(
-                    ERESPONSE, "attachment_size=%d is larger than 
response_size=%d",
+                    ERESPONSE, "Invalid attachment_size=%d (response_size=%d)",
                     meta.attachment_size(), res_size);
                 break;
             }
diff --git a/test/brpc_channel_unittest.cpp b/test/brpc_channel_unittest.cpp
index 8b8789ef9..0d6578b37 100644
--- a/test/brpc_channel_unittest.cpp
+++ b/test/brpc_channel_unittest.cpp
@@ -30,6 +30,7 @@
 #include "butil/logging.h"
 #include "butil/files/temp_file.h"
 #include "butil/fd_guard.h"
+#include "butil/raw_pack.h"
 #include "brpc/socket.h"
 #include "brpc/acceptor.h"
 #include "brpc/server.h"
@@ -2523,6 +2524,131 @@ TEST(ResponseSocketTest, 
real_rpc_responses_match_sending_socket) {
     server.Join();
 }
 
+// RpcMeta.attachment_size is a peer-controlled int32. Before it was
+// lower-bounded, a negative value passed the "larger than payload" guard and
+// `payload_size - attachment_size` overflowed (INT32_MIN) or produced a bogus
+// body/attachment split (any other negative value).
+TEST(BaiduAttachmentSizeTest, negative_response_attachment_size_is_rejected) {
+    const int32_t attachment_sizes[] = { INT32_MIN, -1 };
+    for (int32_t attachment_size : attachment_sizes) {
+        SCOPED_TRACE(attachment_size);
+        brpc::Controller cntl;
+        test::EchoResponse res;
+        cntl._response = &res;
+        ASSERT_EQ(0, bthread_id_lock_and_reset_range(
+                         cntl.call_id(), nullptr, 2));
+        ASSERT_EQ(0, bthread_id_unlock(cntl.current_id()));
+        brpc::SocketId sending_id;
+        ASSERT_EQ(0, brpc::Socket::Create(brpc::SocketOptions(), &sending_id));
+        ASSERT_EQ(0, brpc::Socket::Address(
+                         sending_id, &cntl._current_call.sending_sock));
+        brpc::Socket* socket = cntl._current_call.sending_sock.get();
+        socket->ReleaseAdditionalReference();
+
+        brpc::policy::RpcMeta meta;
+        meta.set_correlation_id(cntl.current_id().value);
+        meta.mutable_response()->set_error_code(0);
+        meta.set_attachment_size(attachment_size);
+        auto* msg = brpc::policy::MostCommonMessage::Get();
+        butil::IOBufAsZeroCopyOutputStream meta_stream(&msg->meta);
+        ASSERT_TRUE(meta.SerializeToZeroCopyStream(&meta_stream));
+        test::EchoResponse response;
+        response.set_message("must not be delivered");
+        butil::IOBufAsZeroCopyOutputStream payload_stream(&msg->payload);
+        ASSERT_TRUE(response.SerializeToZeroCopyStream(&payload_stream));
+        socket->ReAddress(&msg->_socket);
+        socket->PostponeEOF();
+
+        brpc::policy::ProcessRpcResponse(msg);
+        EXPECT_EQ(brpc::ERESPONSE, cntl.ErrorCode()) << cntl.ErrorText();
+        EXPECT_TRUE(res.message().empty());
+        EXPECT_TRUE(cntl.response_attachment().empty());
+    }
+}
+
+TEST(BaiduAttachmentSizeTest, negative_request_attachment_size_is_rejected) {
+    MyEchoService service;
+    brpc::Server server;
+    ASSERT_EQ(0, server.AddService(&service, brpc::SERVER_DOESNT_OWN_SERVICE));
+    ASSERT_EQ(0, server.Start(0, nullptr));
+
+    const int32_t attachment_sizes[] = { INT32_MIN, -1 };
+    for (int32_t attachment_size : attachment_sizes) {
+        SCOPED_TRACE(attachment_size);
+        int fds[2];
+        ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, fds));
+        butil::fd_guard request_fd(fds[0]);
+        butil::fd_guard peer_fd(fds[1]);
+        brpc::SocketOptions options;
+        options.fd = request_fd;
+        brpc::SocketId socket_id;
+        ASSERT_EQ(0, brpc::Socket::Create(options, &socket_id));
+        request_fd.release();
+        brpc::SocketUniquePtr socket;
+        ASSERT_EQ(0, brpc::Socket::Address(socket_id, &socket));
+        socket->ReleaseAdditionalReference();
+
+        brpc::policy::RpcMeta meta;
+        meta.set_correlation_id(1);
+        meta.mutable_request()->set_service_name(
+            test::EchoService::descriptor()->full_name());
+        meta.mutable_request()->set_method_name("Echo");
+        meta.set_attachment_size(attachment_size);
+        auto* msg = brpc::policy::MostCommonMessage::Get();
+        butil::IOBufAsZeroCopyOutputStream meta_stream(&msg->meta);
+        ASSERT_TRUE(meta.SerializeToZeroCopyStream(&meta_stream));
+        test::EchoRequest request;
+        request.set_message("must not be served");
+        butil::IOBufAsZeroCopyOutputStream payload_stream(&msg->payload);
+        ASSERT_TRUE(request.SerializeToZeroCopyStream(&payload_stream));
+        msg->_arg = &server;
+        socket->ReAddress(&msg->_socket);
+        socket->PostponeEOF();
+
+        brpc::policy::ProcessRpcRequest(msg);
+
+        // The error response is written back on the arrival socket:
+        // [PRPC][body_size][meta_size] followed by the serialized RpcMeta.
+        char header[12];
+        butil::IOBuf received;
+        const int64_t deadline = butil::cpuwide_time_ms() + 5000;
+        uint32_t body_size = 0;
+        uint32_t meta_size = 0;
+        while (received.size() < sizeof(header) ||
+               received.size() < sizeof(header) + body_size) {
+            const int64_t remaining = deadline - butil::cpuwide_time_ms();
+            ASSERT_GT(remaining, 0);
+            pollfd pfd = {peer_fd, POLLIN, 0};
+            ASSERT_EQ(1, poll(&pfd, 1, remaining));
+            char buf[1024];
+            const ssize_t n = read(peer_fd, buf, sizeof(buf));
+            ASSERT_GT(n, 0);
+            received.append(buf, n);
+            if (received.size() >= sizeof(header)) {
+                received.copy_to(header, sizeof(header));
+                ASSERT_EQ(0, memcmp(header, "PRPC", 4));
+                butil::RawUnpacker(header + 4)
+                    .unpack32(body_size).unpack32(meta_size);
+            }
+        }
+        received.pop_front(sizeof(header));
+        ASSERT_LE(meta_size, body_size);
+        butil::IOBuf response_meta_buf;
+        received.cutn(&response_meta_buf, meta_size);
+        brpc::policy::RpcMeta response_meta;
+        butil::IOBufAsZeroCopyInputStream wrapper(response_meta_buf);
+        ASSERT_TRUE(response_meta.ParseFromZeroCopyStream(&wrapper));
+        EXPECT_EQ(1, response_meta.correlation_id());
+        EXPECT_EQ(brpc::EREQUEST, response_meta.response().error_code())
+            << response_meta.response().error_text();
+        // The request must not have been served: a failed request carries
+        // no body after the meta.
+        EXPECT_EQ(body_size, meta_size);
+    }
+    server.Stop(0);
+    server.Join();
+}
+
 TEST_F(ChannelTest, intrusive_ptr_sanity) {
     MyShared::nctor = 0;
     MyShared::ndtor = 0;


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

Reply via email to