This is a piece of #21878, chopped off to ease review.
Change CNode
to use a pointer to Sock
instead of a bare SOCKET
.
This will help mocking / testing / fuzzing more code.
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
Reviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
Concept and light code-review ACK ba8cc5b4e5c38e163d01b38f8a73da7c10ce0999
The first commit seems unrelated to the other two, I guess this will be useful in a later chopped off PR?
ba8cc5b4e5...846e9952cf
: use FuzzedSock
in ConsumeNode()
Invalidates (light) ACK from @theStack
The first commit seems unrelated to the other two…
Oh, good catch! The idea is to use FuzzedSock
in ConsumeNode()
which creates a CNode
object. Both are defined in src/test/fuzz/util.h
, but for this FuzzedSock
must appear before ConsumeNode()
. It was like this in a previous incarnation of #21878 but in the latest push, for some reason, I changed it to use nullptr
🤦
Light re-ACK 78cda19bb859be69a8bdb006c210b8d47d56acc9
(as per git range-diff ba8cc5b4...78cda19b
)
605@@ -607,7 +606,16 @@ BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
606 in_addr ipv4AddrPeer;
607 ipv4AddrPeer.s_addr = 0xa0b0c001;
608 CAddress addr = CAddress(CService(ipv4AddrPeer, 7777), NODE_NETWORK);
609- std::unique_ptr<CNode> pnode = std::make_unique<CNode>(0, NODE_NETWORK, INVALID_SOCKET, addr, /*nKeyedNetGroupIn=*/0, /*nLocalHostNonceIn=*/0, CAddress{}, /*pszDest=*/std::string{}, ConnectionType::OUTBOUND_FULL_RELAY, /*inbound_onion=*/false);
610+ std::unique_ptr<CNode> pnode = std::make_unique<CNode>(0,
611+ NODE_NETWORK,
612+ nullptr,
c702f77a in net_tests
and denialofservice_tests
named args might be nice for these nullptr
params
0 /*sock=*/nullptr,
296@@ -259,7 +297,7 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
297 {
298 const NodeId node_id = node_id_in.value_or(fuzzed_data_provider.ConsumeIntegral<NodeId>());
299 const ServiceFlags local_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS);
300- const SOCKET socket = INVALID_SOCKET;
301+ auto sock = std::make_shared<FuzzedSock>(fuzzed_data_provider);
c702f77a1f5e5988a0c7cee7ef3f7cfc63a9ea1b feel free to ignore: not sure if there is a reason to not keep it const (and use braced initialization)
0 const auto sock{std::make_shared<FuzzedSock>(fuzzed_data_provider)};
Yes, it can be const
, this is the std::shared_ptr
itself, not the contained FuzzedSock
.
Braced initialization is pointless with auto
since there is nothing to check against. I find braced initialization less readable than =
so I left it as it is.
using a shared pointer instead of a unique pointer muddles the concept of ownership a bit …
Oh well, this is a bit subtle, maybe some comments are warranted 🤔. It is needed to resolve #21744. That issue contains some details on “why shared_ptr
”.
d22a477542...1477f3aa73
: rebase due to conflicts, elaborate the comment for CNode::m_sock
Invalidates ACKs from @jonatack, @stratospher
Previously invalidated ACK from @theStack
@laanwj, added this comment in the hopes to make it more clear:
0`shared_ptr` (instead of `unique_ptr`) is used to avoid premature close of
1the underlying file descriptor by one thread while another thread is
2poll(2)-ing it for activity.
3[@see](/bitcoin-bitcoin/contributor/see/) [#21744](/bitcoin-bitcoin/21744/) for details.
Light re-ACK 1477f3aa73c4bec6e04682be63071592a032d506
(as per git range-diff 78cda19b...1477f3aa
)
tACK 1477f3a
Great explanation of why use shared_ptr
instead of std::unique_ptr
for m_sock
This way it can be used in `ConsumeNode()`.
1477f3aa73...af7fc2864e
: rebase due to conflicts
Invalidates ACKs from @jonatack, @theStack, @stratospher, @w0xlt
504@@ -505,7 +505,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
505 if (!addr_bind.IsValid()) {
506 addr_bind = GetBindAddress(sock->Get());
507 }
508- CNode* pnode = new CNode(id, nLocalServices, sock->Release(), addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", conn_type, /* inbound_onion */ false);
509+ CNode* pnode = new CNode(id, nLocalServices, std::move(sock), addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", conn_type, /* inbound_onion */ false);
nit if you retouch
0 CNode* pnode = new CNode(id, nLocalServices, std::move(sock), addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", conn_type, /*inbound_onion=*/false);
net: use Sock in CNode
but ok.
1196@@ -1197,7 +1197,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
1197 }
1198
1199 const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
1200- CNode* pnode = new CNode(id, nodeServices, sock->Release(), addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", ConnectionType::INBOUND, inbound_onion);
1201+ CNode* pnode = new CNode(id, nodeServices, std::move(sock), addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", ConnectionType::INBOUND, inbound_onion);
nit if you retouch this line
0 CNode* pnode = new CNode(id, nodeServices, std::move(sock), addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, /*addrNameIn=*/"", ConnectionType::INBOUND, inbound_onion);
net: use Sock in CNode
but ok.
803+ LOCK(node.m_sock_mutex);
804+ if (!node.m_sock) {
805 break;
806- nBytes = send(node.hSocket, reinterpret_cast<const char*>(data.data()) + node.nSendOffset, data.size() - node.nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT);
807+ }
808+ nBytes = node.m_sock->Send(reinterpret_cast<const char*>(data.data()) + node.nSendOffset, data.size() - node.nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT);
Probably a silly question: LOCK(node.m_sock_mutex);
applies even to node.m_sock->Send(...)
operation but then here one can return contained sockets out of the function.
I understand that m_sock_mutex
guards only m_sock
member read & write operations but not necessarily the contained socket. Is that correct or not?
That is a good question.
Before this PR the mutex protects the value of the socket variable - that is, to make sure it does not change from e.g. 7
(it is a file descriptor) to INVALID_SOCKET
in the middle of constructs like:
0if (socket != INVALID_SOCKET) {
1 ... do something with the socket, assuming it is valid ...
2}
After this PR, it is the same with the exception that the variable is not a bare integer anymore, but a shared_ptr
and an empty such pointer is used to designate what was designated before by INVALID_SOCKET
.
2996@@ -2994,10 +2997,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const
2997 m_serializer = std::make_unique<V1TransportSerializer>(V1TransportSerializer());
2998 }
2999
3000-CNode::~CNode()
3001-{
3002- CloseSocket(hSocket);
3003-}
3004+CNode::~CNode() {}
= default
(or document specific reason why it must be in cpp)
Change `CNode` to use a pointer to `Sock` instead of a bare `SOCKET`.
This will help mocking / testing / fuzzing more code.
-BEGIN VERIFY SCRIPT-
sed -i -e 's/cs_hSocket/m_sock_mutex/g' $(git grep -l cs_hSocket)
-END VERIFY SCRIPT-
af7fc2864e...ef5014d256
: address suggestions
Invalidates ACK from @jonatack
Previously invalidated ACKs from @theStack, @stratospher, @w0xlt
2996@@ -2994,11 +2997,6 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const
2997 m_serializer = std::make_unique<V1TransportSerializer>(V1TransportSerializer());
2998 }
2999
3000-CNode::~CNode()
See https://en.cppreference.com/w/cpp/language/destructor “Implicitly-declared destructor”.
The default destructor for CNode will call all the destructors of all of it’s members. The destructor for shared_ptr will reduce the reference counter, and then if that reference counter is zero, the destructor for Sock will be called. If the RC is not zero, then the Sock will continue to exist on the heap (presumably being used by another thread), until the owner of that shared_ptr calls the destructor.
Since my last ACK was only “light” and happened weeks ago, so I decided to do a full review of this PR again instead of looking at the range-diff. Changes LGTM!
Cod-review ACK ef5014d256638735b292672c774446db4003f03b