Skip to content

Conversation

@vasild
Copy link
Contributor

@vasild vasild commented May 7, 2021

This is a piece of #21878, chopped off to ease review.

Introduce an accept(2) wrapper Sock::Accept() and extend the usage of Sock in CConnman::ListenSocket and CreateNodeFromAcceptedSocket().

@DrahtBot
Copy link
Contributor

DrahtBot commented May 7, 2021

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #23604 (Use Sock in CNode by vasild)
  • #23531 (Add Yggdrasil support by prusnak)
  • #21878 (Make all networking code mockable by vasild)

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.

@practicalswift
Copy link
Contributor

Concept ACK

1 similar comment
@jonatack
Copy link
Member

Concept ACK

@vasild
Copy link
Contributor Author

vasild commented May 19, 2021

fb10bf2ba7...0f83150f14: rebase due to conflicts

@vasild
Copy link
Contributor Author

vasild commented May 20, 2021

0f83150f14...9f69aa45a8: rebase and follow the trend from #21659 and flag the newly added Sock::Accept() with [[nodiscard]]

@vasild
Copy link
Contributor Author

vasild commented Jun 3, 2021

9f69aa45a8...9f04449a8e: apply upstream changes.

Copy link
Contributor

@dhruv dhruv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crACK tACK 9f04449

Thank you for helping decouple our tests from system calls, @vasild. I also ran tests, and manually tested incoming connections on regtest using:

# Start node0 with -listen
$ src/bitcoind -regtest -datadir=`pwd`/node0 -rpccookiefile=.cookie -port=2222 -rpcport=3333 -listen

# Generate a block on node0
$ src/bitcoin-cli -regtest -datadir=`pwd`/node0 -rpccookiefile=.cookie -rpcport=3333 createwallet test
$ src/bitcoin-cli -regtest -datadir=`pwd`/node0 -rpccookiefile=.cookie -rpcport=3333 -generate 

# node0 has a block
$ src/bitcoin-cli -regtest -datadir=`pwd`/node0 -rpccookiefile=.cookie -rpcport=3333 getblockcount
1

$ src/bitcoin-cli -regtest -datadir=`pwd`/node0 -rpccookiefile=.cookie -rpcport=3333 getbestblockhash
1878296b74ee617ff119d2fc3f85c9fb35eae952e0360e505bb7d325dfe9e825

# Start node1
$ src/bitcoind -regtest -datadir=`pwd`/node1 -rpccookiefile=.cookie -port=2223 -rpcport=3334

# node1 has no blocks
$ src/bitcoin-cli -regtest -datadir=`pwd`/node1 -rpccookiefile=.cookie -rpcport=3334 getblockcount
0

# Restart node1 with -addnode=node0
$ src/bitcoind -regtest -datadir=`pwd`/node1 -rpccookiefile=.cookie -port=2223 -rpcport=3334 -addnode=127.0.0.1:2222

# node1 completes IBD and has the block
$ src/bitcoin-cli -regtest -datadir=`pwd`/node1 -rpccookiefile=.cookie -rpcport=3334 getblockcount
1

$ src/bitcoin-cli -regtest -datadir=`pwd`/node1 -rpccookiefile=.cookie -rpcport=3334 getbestblockhash
1878296b74ee617ff119d2fc3f85c9fb35eae952e0360e505bb7d325dfe9e825

Would the goal eventually be to not have to invoke Sock::Release() anywhere and instead use move semantics across the board as more code is RAII-fied?

*addr_len = sizeof(sockaddr_in);
sockaddr_in* addr_in = reinterpret_cast<sockaddr_in*>(addr);
addr_in->sin_family = AF_INET;
memset(&addr_in->sin_addr, 0x05, sizeof(addr_in->sin_addr));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit): Would inet_pton(AF_INET, "5.5.5.5", &addr_in->sin_addr); be clearer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that would be clearer. However, some inet_...() function ended up using syscalls (it created sockets internally, IIRC) on Linux. @practicalswift, what do you think?

@practicalswift
Copy link
Contributor

@vasild

Very nice work.

Non-blocking suggestion: Would it be possible to add a fuzzing harness utilizing FuzzedSock::Accept as part of this PR? It would be really nice to see this in action, and it would also transition FuzzedSock::Accept out of "unused code" territory :)

@vasild
Copy link
Contributor Author

vasild commented Jun 10, 2021

Would the goal eventually be to not have to invoke Sock::Release() anywhere and instead use move semantics across the board as more code is RAII-fied?

Yes! #21878 removes Sock::Release() in a later commit, called... eh... net: remove now unused Sock::Release() 😄. It uses either move semantics or passes the Sock objects by reference if a function wants to use the socket but not claim ownership of it.

Would it be possible to add a fuzzing harness utilizing FuzzedSock::Accept as part of this PR? ...

The Accept() method is only called from CConnman::AcceptConnection() which itself calls a bunch of other functions that are not mocked yet in this PR. All those are dealt with in #21878 which also adds fuzzing of CConnman::SocketHandler() (which calls CConnman::AcceptConnection()) in commit fuzz: add CConnman::SocketHandler() to the tests.

Copy link
Contributor

@jamesob jamesob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 9f04449 (jamesob/ackr/21879.1.vasild.wrap_accept_and_extend_u)

Change looks good to me and, as others have noted, making network code more mockable will make writing more comprehensive tests feasible. Cloned locally and ran unit, functional tests.

When I made the dumb mutation below, both test suites still passed so I guess we don't have any tests exercising socket errors.

diff --git a/src/util/sock.cpp b/src/util/sock.cpp
index 9a92fd1b5f..e25c1bab7f 100644
--- a/src/util/sock.cpp
+++ b/src/util/sock.cpp
@@ -76,7 +76,7 @@ std::unique_ptr<Sock> Sock::Accept(sockaddr* addr, socklen_t* addr_len) const
 #ifdef WIN32
     static constexpr auto ERR = INVALID_SOCKET;
 #else
-    static constexpr auto ERR = SOCKET_ERROR;
+    static constexpr auto ERR = SOCKET_ERROR + 1;
 #endif

     const auto socket = accept(m_socket, addr, addr_len);
Show signature data

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

ACK 9f04449a8ec7dc167a0d6d0cb47c5facd205a81a ([`jamesob/ackr/21879.1.vasild.wrap_accept_and_extend_u`](https://github.com/jamesob/bitcoin/tree/ackr/21879.1.vasild.wrap_accept_and_extend_u))

Change looks good to me and, as others have noted, making network code more mockable will make writing comprehensive tests more feasible. Cloned locally and ran unit, functional tests.

When I made the dumb mutation below, both test suites passed so I guess we don't have any tests exercising invalid socket errors.

<patch>
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCgAdFiEEGNRVI1NPYuZCSIrGepNdrbLETwUFAmEgIVsACgkQepNdrbLE
TwWAvA/8C6OfGufmfaEdYzaaGVPyq1jmF7GmXsFo0T3AkflMBbMqri4Q+1ai1Yzs
CkVOuxGBDj8wiKxEeRsbsCsp3gbcOcp21ccvn6EQkPi0Mk8Rusj1iXgs+sDPuf7V
tKq3uQ54IbxkavKKWWhp5VeGrm4ddm8s2qF5uaN7UcGlGAPTw1915YW2HmEa3sC3
9hQb+VGw3nkczUBJXMZls0AUF6E8XYTX3QycSGjlpazgSJac5J54trfvdCzpR1Ae
0WlWndO/VROlePi6p2Mg0DHMCuuRyk78qCHx547WcaQxYWOic+XMEU1OELueLVbf
gf16So4iITHJWeK/GOLuecRfGm58uquWtQuzl5twzvPmzHRrwYyHrvgQP9eANzkQ
VCIbZWV7zL5ksgDGRJTwwzUERICvB6PqlzSU3a49f7kD2DnW+CMzHROe2YW+uLM7
lIn6fmw7WCLBAi990aOtlTf9eT4YYrkneNQYz4vnS/uWvNFBqrkCFxhTdBDHg0jr
8EDzNVFtBFnxKBLYia+ybnPb8pqqAak2AYlrpYVSw6NWSlaWLq8d5JwqkNjgY2K1
anpssdubMvHmWk+N5e0rFbRg1froCMB3Fn7BhbSRbNlnKecv1M+WE1UgI8Y4Hlcn
nLxE/xMFHD94q9N79b76ENqDN8lOgTQmIbUVVcW+LKJpZ5bIQnU=
=EpEl
-----END PGP SIGNATURE-----

Show platform data

Tested on Linux-4.19.0-17-amd64-x86_64-with-glibc2.28

Configured with ./configure LDFLAGS=-L/home/james/src/bitcoin/db4/lib/ CPPFLAGS=-I/home/james/src/bitcoin/db4/include/ CXXFLAGS=-fPIE -pipe -O2 -g -Wthread-safety-analysis -Wall -Werror=sign-compare --enable-wallet --with-daemon CXX=/usr/local/bin/clang++ CC=/usr/local/bin/clang --with-bignum=no PYTHONPATH= --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --enable-module-schnorrsig --enable-experimental --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --enable-module-schnorrsig --enable-experimental --no-create --no-recursion

Compiled with /usr/bin/ccache /usr/local/bin/clang++ -std=c++17 -mavx -mavx2 -fPIE -pipe -O2 -g -Wthread-safety-analysis -Wall -Werror=sign-compare -fdebug-prefix-map=$(abs_srcdir)=.  -Wstack-protector -fstack-protector-all -fcf-protection=full -fstack-clash-protection -msse4 -msha -msse4.1 -msse4.2  i

Compiler version: Debian clang version 11.1.0-++20210428103820+1fdec59bffc1-1~exp1~20210428204437.162

@vasild
Copy link
Contributor Author

vasild commented Aug 23, 2021

@jamesob, thanks for looking into this!

Your mod changes ERR from -1 to 0 and the code looks like this:

    const auto socket = accept(m_socket, addr, addr_len);
    if (socket == 0) {
        return nullptr;
    }
    return std::make_unique<Sock>(socket);

This will change the behavior in two cases:

  1. If accept() succeeds and returns socket=0. Could happen if stdin (fd=0) is closed and the descriptor number 0 is reused? In this case Sock::Accept() would return nullptr, signaling failure.
  2. If accept() fails and returns -1, then the check -1 == 0 would be false and Sock::Accept() would return a Sock object with internal m_socket equal to -1.

I think it is unlikely that any of that happens (the real syscall accept() to return -1 or 0).

@jamesob
Copy link
Contributor

jamesob commented Aug 23, 2021

@vasild yup, it was that second case I was curious about. Nothing wrong with this PR of course, just thought it might be interesting to note that we don't seem to have any coverage for the accept()-fails branches. I think this PR will make it feasible to add such coverage, if that ends up being worth doing.

@vasild
Copy link
Contributor Author

vasild commented Aug 24, 2021

Right! CConnman::AcceptConnection() is a candidate for fuzzing once all of #21878 is merged.

@vasild
Copy link
Contributor Author

vasild commented Aug 27, 2021

This PR has 2 ACKs and 2 Concept ACKs. @practicalswift, @jonatack, willing to upgrade your "Concept ACK"s to full code review ACKs?

@jonatack
Copy link
Member

Yes, I was reviewing this a few days ago when I hit the addrman corruption issue from the asmap/addrman init order. Back to it soon!

Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested ACK 9f04449 rebased to master at a8a272a, code review, debug build and ran unit+functional tests at each commit, ran bitcoind on this branch head rebased to master on mainnet with a full range of connections including inbounds, behavior and debug log nominal

This now has 3 ACKs by @dhruv, @jamesob and me.

@vasild
Copy link
Contributor Author

vasild commented Sep 28, 2021

9f04449a8e...78e21e511e: rebase due to a silent windows-only "conflict" and address review suggestions.

Invalidates ACKs from @dhruv, @jamesob, @jonatack.

@jonatack
Copy link
Member

Diff-review re-ACK 78e21e511ee241900b84d763462f26972d83b600 per git range-diff a9d0cec 9f04449 78e21e following my previous full review (#21879 (review))

Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re-ACK 0f4474d6da5741c7dc61cef403bb03faa2345f37 per git range-diff 2ef186a 07d998f 0f4474, re-review, rebase/debug build/ran units following my previous full review (#21879 (review))

@vasild
Copy link
Contributor Author

vasild commented Nov 26, 2021

0f4474d6da...976f6e8bc9: rebase due to conflicts; close the accepted socket if std::make_unique() throws.

Invalidates ACK from @jonatack

Previously invalidated ACKs from @dhruv, @jamesob

This will help to increase `Sock` usage and make more code mockable.
Change `CConnman::ListenSocket` to use a pointer to `Sock` instead of a
bare `SOCKET` and use `Sock::Accept()` instead of bare `accept()`. This
will help mocking / testing / fuzzing more code.
Change `CConnman::CreateNodeFromAcceptedSocket()` to take a `Sock`
argument instead of `SOCKET`.

This makes the method mockable and also a little bit shorter as some
`CloseSocket()` calls are removed (the socket will be closed
automatically by the `Sock` destructor on early return).
@vasild
Copy link
Contributor Author

vasild commented Dec 1, 2021

976f6e8bc9...6bf6e9fd9d: rebase due to conflicts

Previously invalidated ACKs from @jonatack, @dhruv, @jamesob

Copy link
Contributor

@jamesob jamesob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 6bf6e9f (jamesob/ackr/21879.2.vasild.wrap_accept_and_extend_u)

Examined interdiff and built/tested locally with a pre-populated datadir.

Changes since last review include rebasing on master (CJDNS changes, vNodes -> m_nodes), some && sock function sig changes, write_len rephrasing, and catching exceptions during unique_ptr construction in Sock::Accept.

Show signature data

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

ACK 6bf6e9fd9dece67878595a5f3361851c25833c49 ([`jamesob/ackr/21879.2.vasild.wrap_accept_and_extend_u`](https://github.com/jamesob/bitcoin/tree/ackr/21879.2.vasild.wrap_accept_and_extend_u))

Examined interdiff and built/tested locally with a pre-populated datadir.

Changes since last review include rebasing on master (CJDNS changes, vNodes -> m_nodes), some
&& sock function sig changes, `write_len` rephrasing, and catching exceptions during unique_ptr
construction in Sock::Accept.
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCgAdFiEEGNRVI1NPYuZCSIrGepNdrbLETwUFAmGn3PIACgkQepNdrbLE
TwXzpw/+KLFtcxY3gB2hbfsjsm0IUUiY/FWnFL1S5ADu5HaFAMHHkT9cEfCsMhAn
oQeGgtKJ4+kp3j01JEHwo7Sa/XLj1CEKWLbLd3OFJyFgQ7GJr0X9Ej0RmHN67Rjz
B70DzBNe6FeRz/tST+fkVOULLJqj0bwg1FeVMsHK1bbAWCtl358O25flNeXy5zCI
tY5IAevJII5RP1YjbeTddY5VRPXBNP/dohEL6M5mvZdNsz5/OHz7VfuCbJtH1LBC
E7HcaMl8y70feJ4bVHuRmupx1+TYy5JUx1Aj05EcNzmkC/APHdTcL9bsZXiiOeHU
nvp81Ozq7oRe9ZkEzTPiKScPP6rqHbFILUE+dYW22P6qjDczHdge2sPVzcyIWKMR
CW7tG9PSo4dzRly+jh8T90ApnPe7oVQr6pzPVTjAlyuNHcS7CWYnEWS0Nha1zWyv
TYV2jC/53ugrqVnUAm+HbfTR2SIhTpYgYpUkYArHmEyXL/PZOnYB02+vgEhTsMkQ
JSYHb7AwlHoKr9R+S7XvkNG2TN2/2x4XZNTYr8sDvdNlxWLzsfTbTv1g9gfX/U1U
VFvJNFUaY/i8qq0FUKYgHvz0JMvaoRnsTx5w1K4m9tvdiNtA34TJy+y1sP1VPnnr
7dA09N0rRu9BuAzioViAsd2eVZQS3TUOjvsYnKFL6EmsC/36lgg=
=HB8m
-----END PGP SIGNATURE-----

Show platform data

Tested on Linux-5.10.0-9-amd64-x86_64-with-glibc2.28

Configured with ./configure LDFLAGS=-L/home/james/src/bitcoin/db4/lib/ CPPFLAGS=-I/home/james/src/bitcoin/db4/include/ CXXFLAGS=-fPIE -pipe -O2 -g -Wthread-safety-analysis -Wall -Werror=sign-compare -Wsign-compare -Werror=thread-safety-analysis --enable-wallet --enable-debug --with-daemon --enable-natpmp-default CXX=/usr/bin/clang++ CC=/usr/bin/clang PYTHONPATH= --disable-openssl-tests --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --enable-module-schnorrsig --enable-experimental --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --enable-module-schnorrsig --enable-experimental --disable-openssl-tests --no-create --no-recursion

Compiled with /usr/bin/ccache /usr/bin/clang++ -std=c++17 -mavx -mavx2 -mpclmul -fPIE -pipe -O2 -g -Wthread-safety-analysis -Wall -Werror=sign-compare -Wsign-compare -Werror=thread-safety-analysis -O0 -g3 -ftrapv -fdebug-prefix-map=$(abs_top_srcdir)=.  -Wstack-protector -fstack-protector-all -fcf-protection=full -fstack-clash-protection -msse4 -msha -msse4.1 -msse4.2  i

Compiler version: Debian clang version 13.0.1-++20211110052940+162f3f18c945-1~exp1~20211110053502.25

Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 6bf6e9f per git range-diff ea989de 976f6e8 6bf6e9f -- only change since my last review was s/listen_socket.socket/listen_socket.sock->Get()/ in src/net.cpp: CConnman::SocketHandlerListening() -- re-read the code changes, rebase/debug build/ran units following my previous full review (#21879 (review))

@jonatack
Copy link
Member

jonatack commented Dec 7, 2021

Could maybe use another pair of eyes on this, but otherwise it seems RFM.

@maflcko
Copy link
Member

maflcko commented Dec 8, 2021

It is unclear from the subject line and labels, but this is refactoring? It might be good to clarify that in the commit messages (if you need to push again) or at least in the pull request title and labels. This makes it easier for reviewers to see if it was accidental when it later turns out that this introduced any bugs or features.

@vasild vasild changed the title Wrap accept() and extend usage of Sock refactor: wrap accept() and extend usage of Sock Dec 8, 2021
@vasild
Copy link
Contributor Author

vasild commented Dec 8, 2021

Yes, this is refactor since it does not change external behavior. Updated the PR title. I can't edit labels.

Copy link
Contributor

@w0xlt w0xlt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tACK 6bf6e9f

Ran the node on signet and the unit / functional tests.

Seems good. Sock::Accept encapsulates the accept() system call, making the code more mockable and simpler due to the use of the RAII Sock class.

If I understand correctly, instead of manually closing each socket from vhListenSocket, Sock destructor will automatically handle this after vhListenSocket.clear().

@vasild
Copy link
Contributor Author

vasild commented Jan 4, 2022

If I understand correctly, instead of manually closing each socket from vhListenSocket, Sock destructor will automatically handle this after vhListenSocket.clear().

Yes!

@laanwj
Copy link
Member

laanwj commented Jan 5, 2022

Code review ACK 6bf6e9f

@laanwj laanwj merged commit 8f1c28a into bitcoin:master Jan 5, 2022
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jan 6, 2022
@vasild vasild deleted the SockAccept branch January 6, 2022 09:16
@bitcoin bitcoin locked and limited conversation to collaborators Jan 6, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants