From 202353ac8ca6be6e939155f29d25b4dc2ee078df Mon Sep 17 00:00:00 2001 From: Nicholas Dudfield Date: Wed, 10 Jun 2026 15:59:00 +0700 Subject: [PATCH] fix: break HTTPClient::get() self-reference cycle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit get() bound shared_from_this() into mBuild (a member), forming a this -> mBuild -> shared_ptr cycle that never broke after the request completed — leaking the HTTPClientImp and its socket FD. mBuild is only ever invoked from handleRequest(), which always runs inside an async handler already holding a shared_from_this(), so a non-owning this is safe and lets the object be destroyed once the request finishes. Pre-existing (also present upstream in rippled); only the fetch path uses get(), not the webhook delivery path. --- src/xrpld/net/detail/HTTPClient.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/xrpld/net/detail/HTTPClient.cpp b/src/xrpld/net/detail/HTTPClient.cpp index 523d0972f..21cf25a03 100644 --- a/src/xrpld/net/detail/HTTPClient.cpp +++ b/src/xrpld/net/detail/HTTPClient.cpp @@ -122,12 +122,20 @@ public: mComplete = complete; mTimeout = timeout; + // Bind a non-owning `this` (not shared_from_this()) into mBuild. + // mBuild is a member, so capturing a shared_ptr to self here would + // form a reference cycle (this -> mBuild -> shared_ptr) that + // never breaks, leaking the object and its socket FD after the + // request completes. mBuild is only ever invoked from + // handleRequest(), which always runs inside an async handler that + // already holds a shared_from_this(), so the object is guaranteed + // alive whenever mBuild fires — a raw `this` is safe. request( bSSL, deqSites, std::bind( &HTTPClientImp::makeGet, - shared_from_this(), + this, strPath, std::placeholders::_1, std::placeholders::_2),