What changed, and why it matters
This commit adds a new command-line option to bitcoin-cli that lets it talk to a local Bitcoin node over a local inter-process communication (IPC) socket instead of the usual network HTTP connection. It is a feature addition, not a fix for a known bug or vulnerability. The change is opt-in at build time and runtime, and it does not appear to introduce an obvious security flaw, but it does add new code paths for authentication and transport that need careful review.
Review the IPC implementation (not shown in this diff) for proper access control on the Unix socket, ensure file permissions and path handling prevent unauthorized local clients, and verify that fallback from IPC to HTTP cannot be abused to downgrade connections or bypass authentication. Treat as a normal feature addition rather than an emergency patch.
Security signals we found
New IPC transport path added to bitcoin-cli with fallback behavior
Default preference for IPC over HTTP when both may be available
Authentication and endpoint handling refactored and passed through new CallIPC path
Stub IPC factory added for builds without ENABLE_IPC to maintain linking
No explicit security bug or vulnerability described in commit message
Evidence from the diff
The patch introduces -ipcconnect to bitcoin-cli, defaulting to ‘auto’, which prefers IPC over HTTP and falls back to HTTP if IPC is unavailable. It adds a CallIPC helper that uses interfaces::MakeBasicInit and interfaces::Ipc::connectAddress to obtain an interfaces::Init handle to the node, then calls makeRpc()->executeRpc(). A stub implementation of MakeIpc is provided when ENABLE_IPC is off so bitcoin-cli can still link. The wallet endpoint and username are computed earlier and passed into both CallIPC and CallRPC. The change touches build configuration, CLI argument handling, and the IPC abstraction layer.
Changed components
src/bitcoin-cli.cppsrc/interfaces/ipc.hsrc/ipc/stub.cppsrc/CMakeLists.txtInspect captured patch +76 / −15
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ad18115b..70dd1bab 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -56,6 +56,8 @@ add_subdirectory(crypto)
add_subdirectory(util)
if(ENABLE_IPC)
add_subdirectory(ipc)
+else()
+ add_library(bitcoin_ipc STATIC EXCLUDE_FROM_ALL ipc/stub.cpp)
endif()
add_library(bitcoin_consensus STATIC EXCLUDE_FROM_ALL
@@ -347,13 +349,14 @@ target_link_libraries(bitcoin_cli
# Bitcoin Core RPC client
if(BUILD_CLI)
- add_executable(bitcoin-cli bitcoin-cli.cpp)
+ add_executable(bitcoin-cli bitcoin-cli.cpp init/basic.cpp)
add_windows_resources(bitcoin-cli bitcoin-cli-res.rc)
add_windows_application_manifest(bitcoin-cli)
target_link_libraries(bitcoin-cli
core_interface
bitcoin_cli
bitcoin_common
+ bitcoin_ipc
bitcoin_util
libevent::core
libevent::extra
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 909ed09f..528bcd7d 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -11,6 +11,9 @@
#include <common/system.h>
#include <compat/compat.h>
#include <compat/stdin.h>
+#include <interfaces/init.h>
+#include <interfaces/ipc.h>
+#include <interfaces/rpc.h>
#include <policy/feerate.h>
#include <rpc/client.h>
#include <rpc/mining.h>
@@ -108,6 +111,7 @@ static void SetupCliArgs(ArgsManager& argsman)
argsman.AddArg("-stdin", "Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-stdinrpcpass", "Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password. When combined with -stdinwalletpassphrase, -stdinrpcpass consumes the first line, and -stdinwalletpassphrase consumes the second.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-stdinwalletpassphrase", "Read wallet passphrase from standard input as a single line. When combined with -stdin, the first line from standard input is used for the wallet passphrase.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-ipcconnect=<address>", "Connect to bitcoin-node through IPC socket instead of TCP socket to execute requests. Valid <address> values are 'auto' to try to connect to default socket path at <datadir>/node.sock but fall back to TCP if it is not available, 'unix' to connect to the default socket and fail if it isn't available, or 'unix:<socket path>' to connect to a socket at a nonstandard path. -noipcconnect can be specified to avoid attempting to use IPC at all. Default value: auto", ArgsManager::ALLOW_ANY, OptionsCategory::IPC);
}
std::optional<std::string> RpcWalletName(const ArgsManager& args)
@@ -792,7 +796,40 @@ struct DefaultRequestHandler : BaseRequestHandler {
}
};
-static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::optional<std::string>& rpcwallet = {})
+static std::optional<UniValue> CallIPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::string& endpoint, const std::string& username)
+{
+ auto ipcconnect{gArgs.GetArg("-ipcconnect", "auto")};
+ if (ipcconnect == "0") return {}; // Do not attempt IPC if -ipcconnect is disabled.
+ if (gArgs.IsArgSet("-rpcconnect") && !gArgs.IsArgNegated("-rpcconnect")) {
+ if (ipcconnect == "auto") return {}; // Use HTTP if -ipcconnect=auto is set and -rpcconnect is enabled.
+ throw std::runtime_error("-rpcconnect and -ipcconnect options cannot both be enabled");
+ }
+
+ std::unique_ptr<interfaces::Init> local_init{interfaces::MakeBasicInit("bitcoin-cli")};
+ if (!local_init || !local_init->ipc()) {
+ if (ipcconnect == "auto") return {}; // Use HTTP if -ipcconnect=auto is set and there is no IPC support.
+ throw std::runtime_error("bitcoin-cli was not built with IPC support");
+ }
+
+ std::unique_ptr<interfaces::Init> node_init;
+ try {
+ node_init = local_init->ipc()->connectAddress(ipcconnect);
+ if (!node_init) return {}; // Fall back to HTTP if -ipcconnect=auto connect failed.
+ } catch (const std::exception& e) {
+ // Catch connect error if -ipcconnect=unix was specified
+ throw CConnectionFailed{strprintf("%s\n\n"
+ "Probably bitcoin-node is not running or not listening on a unix socket. Can be started with:\n\n"
+ " bitcoin-node -chain=%s -ipcbind=unix", e.what(), gArgs.GetChainTypeString())};
+ }
+
+ std::unique_ptr<interfaces::Rpc> rpc{node_init->makeRpc()};
+ assert(rpc);
+ UniValue request{rh->PrepareRequest(strMethod, args)};
+ UniValue reply{rpc->executeRpc(std::move(request), endpoint, username)};
+ return rh->ProcessReply(reply);
+}
+
+static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::string& endpoint, const std::string& username)
{
std::string host;
// In preference order, we choose the following for the port:
@@ -873,7 +910,7 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
failedToGetAuthCookie = true;
}
} else {
- strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
+ strRPCUserColonPass = username + ":" + gArgs.GetArg("-rpcpassword", "");
}
struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get());
@@ -889,17 +926,6 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
assert(output_buffer);
evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
- // check if we should use a special wallet endpoint
- std::string endpoint = "/";
- if (rpcwallet) {
- char* encodedURI = evhttp_uriencode(rpcwallet->data(), rpcwallet->size(), false);
- if (encodedURI) {
- endpoint = "/wallet/" + std::string(encodedURI);
- free(encodedURI);
- } else {
- throw CConnectionFailed("uri-encode failed");
- }
- }
int r = evhttp_make_request(evcon.get(), req.release(), EVHTTP_REQ_POST, endpoint.c_str());
if (r != 0) {
throw CConnectionFailed("send http request failed");
@@ -959,9 +985,26 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str
const int timeout = gArgs.GetIntArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT);
const auto deadline{std::chrono::steady_clock::now() + 1s * timeout};
+ // check if we should use a special wallet endpoint
+ std::string endpoint = "/";
+ if (rpcwallet) {
+ char* encodedURI = evhttp_uriencode(rpcwallet->data(), rpcwallet->size(), false);
+ if (encodedURI) {
+ endpoint = "/wallet/" + std::string(encodedURI);
+ free(encodedURI);
+ } else {
+ throw CConnectionFailed("uri-encode failed");
+ }
+ }
+
+ std::string username{gArgs.GetArg("-rpcuser", "")};
do {
try {
- response = CallRPC(rh, strMethod, args, rpcwallet);
+ if (auto ipc_response{CallIPC(rh, strMethod, args, endpoint, username)}) {
+ response = std::move(*ipc_response);
+ } else {
+ response = CallRPC(rh, strMethod, args, endpoint, username);
+ }
if (fWait) {
const UniValue& error = response.find_value("error");
if (!error.isNull() && error["code"].getInt<int>() == RPC_IN_WARMUP) {
diff --git a/src/interfaces/ipc.h b/src/interfaces/ipc.h
index f94ffa3c..23b46fb6 100644
--- a/src/interfaces/ipc.h
+++ b/src/interfaces/ipc.h
@@ -7,6 +7,7 @@
#include <functional>
#include <memory>
+#include <string>
#include <typeindex>
namespace ipc {
diff --git a/src/ipc/stub.cpp b/src/ipc/stub.cpp
new file mode 100644
index 00000000..e00ff568
--- /dev/null
+++ b/src/ipc/stub.cpp
@@ -0,0 +1,14 @@
+// Copyright (c) 2025 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <interfaces/ipc.h>
+
+#include <memory>
+
+namespace interfaces {
+std::unique_ptr<Ipc> MakeIpc(const char* exe_name, const char* process_argv0, Init& init)
+{
+ return {};
+}
+} // namespace interfaces
Why this scored 20/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.