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 dd43e724 Fix use-after-free of response header in ParseMemcacheMessage
(#3572)
dd43e724 is described below
commit dd43e724296f54773bc4e108863d206f04397f61
Author: UB <[email protected]>
AuthorDate: Wed Sep 30 09:12:19 2026 +0530
Fix use-after-free of response header in ParseMemcacheMessage (#3572)
---
src/brpc/policy/memcache_binary_protocol.cpp | 7 ++++--
test/brpc_memcache_unittest.cpp | 37 ++++++++++++++++++++++++++++
2 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/src/brpc/policy/memcache_binary_protocol.cpp
b/src/brpc/policy/memcache_binary_protocol.cpp
index dcb435b0..e30bade1 100644
--- a/src/brpc/policy/memcache_binary_protocol.cpp
+++ b/src/brpc/policy/memcache_binary_protocol.cpp
@@ -132,8 +132,11 @@ ParseResult ParseMemcacheMessage(butil::IOBuf* source,
msg->meta.append(&local_header, sizeof(local_header));
source->pop_front(sizeof(*header));
source->cutn(&msg->meta, total_body_length);
- if (header->command == MC_BINARY_SASL_AUTH) {
- if (header->status != 0) {
+ // `header' points into source's front block; pop_front above may have
+ // released that block, so read the already byte-swapped local_header
+ // here instead of dereferencing the now-dangling `header'.
+ if (local_header.command == MC_BINARY_SASL_AUTH) {
+ if (local_header.status != 0) {
LOG(ERROR) << "Failed to authenticate the couchbase bucket.";
return MakeParseError(PARSE_ERROR_NO_RESOURCE,
"Fail to authenticate with the couchbase
bucket");
diff --git a/test/brpc_memcache_unittest.cpp b/test/brpc_memcache_unittest.cpp
index 76c4ca4b..a43a1e01 100644
--- a/test/brpc_memcache_unittest.cpp
+++ b/test/brpc_memcache_unittest.cpp
@@ -93,6 +93,43 @@ TEST(MemcacheParserTest, PopStoreRejectsNegativeValueSize) {
ASSERT_EQ(next_response, response.raw_buffer().to_string());
}
+TEST(MemcacheParserTest, SaslAuthDoesNotReadHeaderAfterPop) {
+ // ParseMemcacheMessage fetched the 24-byte response header (a pointer into
+ // source's front block), popped those bytes with source->pop_front(), and
+ // then still dereferenced header->command / header->status on the
SASL_AUTH
+ // path. Once the header sat alone in the front block, pop_front released
it
+ // and the two reads became a use-after-free. Put the header in a heap
block
+ // that is freed the moment its bytes are popped so the stale read is a
+ // definite UAF (ASAN heap-use-after-free on the unpatched tree); the fix
+ // reads the already byte-swapped local_header instead.
+ brpc::SocketId id;
+ brpc::SocketOptions options;
+ ASSERT_EQ(0, brpc::Socket::Create(options, &id));
+ brpc::SocketUniquePtr socket;
+ ASSERT_EQ(0, brpc::Socket::Address(id, &socket));
+
+ // PopPipelinedInfo() must succeed to reach the SASL_AUTH branch.
+ brpc::PipelinedInfo pi;
+ pi.count = 1;
+ socket->PushPipelinedInfo(pi);
+
+ brpc::policy::MemcacheResponseHeader* header =
+ (brpc::policy::MemcacheResponseHeader*)malloc(sizeof(*header));
+ ASSERT_TRUE(header != NULL);
+ memset(header, 0, sizeof(*header));
+ header->magic = brpc::policy::MC_MAGIC_RESPONSE;
+ header->command = brpc::policy::MC_BINARY_SASL_AUTH;
+ header->status = butil::HostToNet16(1); // non-zero: auth failure
+ header->total_body_length = 0; // no body: header is the whole
front block
+
+ butil::IOBuf buf;
+ buf.append_user_data(header, sizeof(*header), [](void* p) { free(p); });
+
+ brpc::ParseResult r = brpc::policy::ParseMemcacheMessage(
+ &buf, socket.get(), false, nullptr);
+ ASSERT_EQ(brpc::PARSE_ERROR_NO_RESOURCE, r.error());
+}
+
static pthread_once_t download_memcached_once = PTHREAD_ONCE_INIT;
static pid_t g_mc_pid = -1;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]