Skip to content

Commit 332d3c5

Browse files
committed
ltc: serialize NodeRPC::Send under a mutex to fix RPC-client UAF
NodeRPC holds a single shared m_http_request + m_stream and Send() drove them with no synchronization. The asio thread_pool worker path (clean_tracker -> ShareTracker::think -> score -> score-callback -> NodeRPC::getblockheader -> Send) issues a blocking RPC concurrently with the main thread's own RPC traffic on the same client. ASan (firewall-isolated sibling, BuildId 9a12b6bc) caught the resulting heap-use-after-free: while one thread is mid http::write serializing the request fields (read_iovec), the other enters Send -> prepare_payload -> set_content_length_impl -> delete_element and frees the Content-Length basic_fields element the first thread is still reading. In the non-ASan build this surfaces as the .157 SEGV-inside-malloc crash-loop during reorg/clean churn (the heap corruption was the downstream symptom). Guard the full write+read cycle with a per-client mutex. A single HTTP connection cannot interleave two request/response pairs anyway, so this adds no stall beyond what correctness already requires. Follow-up (separate change): keep think()/score() off the synchronous RPC-on-pool-thread path entirely (cache header height instead of a live getblockheader in the score callback).
1 parent eafcb49 commit 332d3c5

2 files changed

Lines changed: 7 additions & 0 deletions

File tree

src/impl/ltc/coin/rpc.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ void NodeRPC::sync_reconnect()
132132

133133
std::string NodeRPC::Send(const std::string &request)
134134
{
135+
std::lock_guard<std::mutex> _rpc_lock(m_rpc_mutex);
135136
// Retry once after synchronous reconnect on write/read failure
136137
for (int attempt = 0; attempt < 2; ++attempt)
137138
{

src/impl/ltc/coin/rpc.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "node_interface.hpp"
66

77
#include <iostream>
8+
#include <mutex>
89

910
#include <core/uint256.hpp>
1011
#include <core/timer.hpp>
@@ -38,6 +39,11 @@ class NodeRPC : public jsonrpccxx::IClientConnector
3839
beast::tcp_stream m_stream;
3940
boost::asio::ip::tcp::resolver m_resolver;
4041
http::request<http::string_body> m_http_request;
42+
// Serializes Send(): m_http_request + m_stream are NOT thread-safe. The asio
43+
// thread_pool worker (clean_tracker->think->score->getblockheader) and the main
44+
// thread both drive Send() on this single shared client; without this lock T0's
45+
// prepare_payload() frees the Content-Length field element mid-write on T8 -> UAF.
46+
std::mutex m_rpc_mutex;
4147

4248
std::unique_ptr<RPCAuthData> m_auth;
4349
jsonrpccxx::JsonRpcClient m_client;

0 commit comments

Comments
 (0)