Fix use-after-free of parsing contexts in Socket::DebugSocket and ProcessMongoRequest by wwbmmm · Pull Request #3631 · apache/brpc · GitHub
Skip to content

Fix use-after-free of parsing contexts in Socket::DebugSocket and ProcessMongoRequest - #3631

Open
wwbmmm wants to merge 2 commits into
apache:masterfrom
wwbmmm:oncall/req-20261020-052528
Open

wwbmmm wants to merge 2 commits into
apache:masterfrom
wwbmmm:oncall/req-20261020-052528

Conversation

@wwbmmm

@wwbmmm wwbmmm commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: N/A

Problem Summary:

  1. Socket::DebugSocket() (the builtin /sockets/<SocketId> page) described the socket's parsing context from the bthread serving the HTTP request, while the bthread parsing messages of that socket could reset or release the context at the same time (e.g. InputMessenger resetting it before trying other protocols). The context, including an H2Context with its mutexes and HPACK state, could be destroyed while being described.
  2. ProcessMongoRequest() runs in a different bthread from the one parsing messages of the socket, but read the MongoContext from the socket's parsing context, which the parsing bthread may reset in the meantime.

What is changed and the side effects?

Changed:

  • Add Socket::_parsing_context_mutex. reset_parsing_context() and release_parsing_context() hold it while taking the context out of the socket (Destroy() still runs after unlocking), and DebugSocket() holds it while loading and describing the context, so a context is destroyed or handed off only after the description is done.
  • Add MongoInputMessage, which carries an intrusive_ptr<MongoContext> referenced in ParseMongoMessage() on the parsing bthread. ProcessMongoRequest() uses that reference instead of reading the socket's parsing context.
  • Add SocketTest.debug_socket_keeps_parsing_context_alive and MongoTest.process_request_after_parsing_context_reset.

Side effects:

  • Performance effects: none on hot paths. The new lock is only contended while /sockets describes that very socket.

  • Breaking backward compatibility: none.


Check List:

Tests run:

  • cmake -S . -B build -DBUILD_UNIT_TESTS=ON -DDOWNLOAD_GTEST=OFF -DBRPC_SYSTEM_GTEST_SOURCE_DIR=/usr/src/googletest && make -j6 brpc_socket_unittest brpc_mongo_protocol_unittest
  • ./test/brpc_socket_unittest (24 tests passed)
  • ./test/brpc_mongo_protocol_unittest (6 tests passed)

🤖 This PR was automatically created by brpc-oncall

Mar added 2 commits October 20, 2026 05:45
Socket::DebugSocket() (/sockets/<SocketId>) describes the parsing context
from the bthread serving the request, while the bthread parsing messages
of the socket may reset or release the context at the same time, e.g.
InputMessenger resetting it before trying other protocols. The context
could be destroyed while being described.

Guard reset_parsing_context()/release_parsing_context() and the describing
in DebugSocket() with a mutex, so that a context is taken out of the
socket only after DebugSocket() is done with it.
ProcessMongoRequest() runs in a different bthread from the one parsing
messages of the socket, but took the MongoContext from the parsing
context of the socket, which the parsing bthread may reset in the
meantime, e.g. before trying other protocols.

Reference the context in ParseMongoMessage() and carry it with the
request instead.
@wwbmmm
wwbmmm requested a balanced review from Copilot October 20, 2026 07:48

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.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

4 open issues
What changed in this PR

Fixes use-after-free races around socket parsing contexts by synchronizing Socket::DebugSocket() with parsing-context teardown and by decoupling Mongo request processing from the socket’s mutable parsing context.

Changes:

  • Add a mutex to Socket to prevent parsing contexts from being destroyed while /sockets/<id> is describing them.
  • Introduce MongoInputMessage to carry a referenced MongoContext from parse-time to process-time across bthreads.
  • Add unit tests covering both the debug-socket race and the Mongo processing-after-reset scenario.
File Description
test/​brpc_socket_unittest.cpp Adds a regression test ensuring DebugSocket() keeps the parsing context alive during description.
test/​brpc_mongo_protocol_unittest.cpp Adds a regression test ensuring Mongo requests process correctly after the socket parsing context is reset.
src/​brpc/​socket_inl.h Guards parsing context reset/release with a new mutex to avoid concurrent destruction during debug description.
src/​brpc/​socket.h Introduces _parsing_context_mutex and documents its purpose.
src/​brpc/​socket.cpp Wraps parsing-context load + description in the new mutex to prevent use-after-free.
src/​brpc/​policy/​mongo_protocol.cpp Adds MongoInputMessage and switches processing to use the parse-time MongoContext reference.

Review effort: Lite

Comment on lines +232 to +238
if (nullptr == msg->context) {
LOG(WARNING) << "socket context wasn't set correctly";
return;
}

SendMongoResponse* mongo_done = new SendMongoResponse(server);
mongo_done->cntl.set_mongo_session_data(context_msg->context());
mongo_done->cntl.set_mongo_session_data(msg->context.get());
socket->reset_parsing_context(socket_context_msg);
}
policy::MostCommonMessage* msg = policy::MostCommonMessage::Get();
MongoInputMessage* msg = new MongoInputMessage;
Comment thread src/brpc/socket.cpp
Comment on lines +2430 to 2444
{
BAIDU_SCOPED_LOCK(ptr->_parsing_context_mutex);
Destroyable* const parsing_context = ptr->parsing_context();
Describable* parsing_context_desc = dynamic_cast<Describable*>(parsing_context);
if (parsing_context_desc) {
os << "\nparsing_context=" << butil::class_name_str(*parsing_context) << '{';
DescribeOptions opt;
opt.verbose = true;
IndentingOStream os2(os, 2);
parsing_context_desc->Describe(os2, opt);
os << '}';
} else {
os << "\nparsing_context=" << ShowObject(parsing_context);
}
}
Comment on lines +121 to +122
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.

2 participants