wolfssl/wolfio.h (included by <wolfssl/ssl.h> in net.h) does
`#ifndef CloseSocket / #define CloseSocket(s) close(s)`. Every TU that
includes net.h after that point compiles CloseSocket(x) to a bare
close(x). That includes all 15 calls in net.cpp, among them the ones
in CloseSocketDisconnect and ~CNode. util/sock.cpp's
CloseSocket(SOCKET&), the only thing that sets the handle to
INVALID_SOCKET, was never called from net.cpp. This has been the case
since OpenSSL was replaced by wolfSSL (Hush 9ff756590, 2020), so every
DragonX release is affected.
So CNode::hSocket kept its number after the socket was closed. Every
P2P disconnect closed that same number again, 2 to 4 times:
- CloseSocketDisconnect (net.cpp:572) runs once per disconnect trigger
(ThreadSocketHandler, SocketSendData, the TLS socket handler,
ThreadMessageHandler), and each run still saw a valid-looking hSocket;
- ~CNode (net.cpp:2819) then closed it once more.
Meanwhile the socket handler kept FD_SETting, send()ing and recv()ing on
the stale number. In between, other threads get that number from the
kernel's lowest-free-fd rule:
- BDB reopening wallet.dat or database/log.* (ThreadFlushWalletDB, ~11/s);
- fopen("rb+") of a block file for getdata/getblock;
- an HTTP RPC accept.
The stray close then closes it. BDB keeps doing I/O on the dead number:
'wallet.dat: write failed for page N' / 'Illegal seek' / 'BDB0060
PANIC', or silent wallet-page writes into a blocks/blk*.dat that got
the number next. Its failing reads later threw out of ThreadNotifyWallets
(std::terminate). A closed HTTP connection fd makes libevent re-arm
and later close whatever owns that number next ('Epoll ADD ... failed').
Reproduced on isolated regtest with an LD_PRELOAD close() guard:
- one disconnectnode gives exactly one EBADF double close (49
disconnects, 50 EBADF; with strace and no shim, 29 disconnects gave
30 'close(N) = -1 EBADF');
- P2P churn alone gives non-owner closes of block files and wallet.dat;
- RPC-only and Tor-only loads give none.
Disassembly of CloseSocketDisconnect and ~CNode shows 'call close@plt'
before this change and 'call CloseSocket(unsigned int&)' after.
Undefine the macro right after the wolfSSL include in net.h; wolfio.h
is include-guarded, so it can't come back. net.cpp gets an #error
tripwire in case it ever does. NetCloseSocket.ClosedNodeNeverClosesAReusedFdNumber
fails on the old code (a closed CNode closes the fd number's new owner)
and passes now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
68 lines
2.0 KiB
Makefile
68 lines
2.0 KiB
Makefile
# Copyright (c) 2016-2024 The Hush developers
|
|
# Released under the GPLv3
|
|
TESTS += hush-gtest
|
|
bin_PROGRAMS += hush-gtest
|
|
|
|
# tool for generating our public parameters
|
|
# NOTE: the original test list used an invalid automake form (comment after a trailing
|
|
# backslash, and `zcash_gtest_SOURCES +=` with no prior `=`), which is why the whole
|
|
# gtest harness was disabled via a `#include`. Minimal valid set: the harness + the
|
|
# Re-add other gtest sources here as they are revived.
|
|
hush_gtest_SOURCES = \
|
|
gtest/main.cpp \
|
|
gtest/utils.cpp \
|
|
gtest/test_randomx_preverify.cpp \
|
|
gtest/test_hdtransparent.cpp \
|
|
gtest/test_mnemonic_compat.cpp \
|
|
gtest/test_stratum_jobid.cpp \
|
|
gtest/test_wallet_notedata.cpp \
|
|
gtest/test_net_closesocket.cpp
|
|
|
|
hush_gtest_CPPFLAGS = $(AM_CPPFLAGS) -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DSTATIC $(BITCOIN_INCLUDES)
|
|
hush_gtest_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
|
|
|
# Mirror dragonxd_LDADD's working library set/order (the old list used a non-existent
|
|
# $(LIBBITCOIN_UNIVALUE) so univalue was never linked, and omitted LIBHUSH/LIBRANDOMX/libcc).
|
|
hush_gtest_LDADD = -lgtest -lgmock \
|
|
$(LIBBITCOIN_SERVER) \
|
|
$(LIBCURL) \
|
|
$(LIBBITCOIN_COMMON) \
|
|
$(LIBUNIVALUE) \
|
|
$(LIBBITCOIN_UTIL) \
|
|
$(LIBBITCOIN_CRYPTO) \
|
|
$(LIBZCASH) \
|
|
$(LIBHUSH) \
|
|
$(LIBLEVELDB) \
|
|
$(LIBMEMENV) \
|
|
$(LIBSECP256K1) \
|
|
$(LIBRANDOMX)
|
|
if ENABLE_WALLET
|
|
hush_gtest_LDADD += $(LIBBITCOIN_WALLET)
|
|
endif
|
|
|
|
hush_gtest_LDADD += \
|
|
$(BOOST_LIBS) \
|
|
$(BOOST_UNIT_TEST_FRAMEWORK_LIB) \
|
|
$(BDB_LIBS) \
|
|
$(SSL_LIBS) \
|
|
$(CRYPTO_LIBS) \
|
|
$(EVENT_PTHREADS_LIBS) \
|
|
$(EVENT_LIBS) \
|
|
$(LIBBITCOIN_CRYPTO) \
|
|
$(LIBZCASH_LIBS)
|
|
|
|
if TARGET_DARWIN
|
|
hush_gtest_LDADD += libcc.dylib $(LIBSECP256K1)
|
|
endif
|
|
if TARGET_WINDOWS
|
|
hush_gtest_LDADD += libcc.dll $(LIBSECP256K1)
|
|
endif
|
|
if TARGET_LINUX
|
|
hush_gtest_LDADD += libcc.so $(LIBSECP256K1)
|
|
endif
|
|
|
|
hush_gtest_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
|
|
|
hush-gtest-expected-failures: hush-gtest FORCE
|
|
./hush-gtest --gtest_filter=*DISABLED_* --gtest_also_run_disabled_tests
|