From dde69f20a01acca64ac21cb13993c6e4f8709f23 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Fri, 9 Apr 2021 20:13:52 +0200 Subject: [PATCH 1/3] p2p, bugfix: use NetPermissions::HasFlag() in CConnman::Bind() PF_NOBAN is a multi-flag that includes PF_DOWNLOAD, so the conditional in CConnman::Bind() using a bitwise AND will return the same result for both the "noban" status and the "download" status. Example: `PF_DOWNLOAD` is `0b1000000` `PF_NOBAN` is `0b1010000` This makes a check like `flags & PF_NOBAN` return `true` even if `flags` is equal to `PF_DOWNLOAD`. If `-whitebind=download@1.1.1.1:8765` is specified, then `1.1.1.1:8765` should be added to the list of local addresses. We only want to avoid adding to local addresses (that are advertised) a whitebind that has a `noban@` flag. As a result of a mis-check in `CConnman::Bind()` we would not have added `1.1.1.1:8765` to the local addresses in the example above. Co-authored-by: Vasil Dimov --- src/net.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index ae38acdc3c..245a9c021e 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2408,8 +2408,9 @@ NodeId CConnman::GetNewNodeId() bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags permissions) { - if (!(flags & BF_EXPLICIT) && !IsReachable(addr)) + if (!(flags & BF_EXPLICIT) && !IsReachable(addr)) { return false; + } bilingual_str strError; if (!BindListenPort(addr, strError, permissions)) { if ((flags & BF_REPORT_ERROR) && clientInterface) { @@ -2418,7 +2419,7 @@ bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags return false; } - if (addr.IsRoutable() && fDiscover && !(flags & BF_DONT_ADVERTISE) && !(permissions & PF_NOBAN)) { + if (addr.IsRoutable() && fDiscover && !(flags & BF_DONT_ADVERTISE) && !NetPermissions::HasFlag(permissions, NetPermissionFlags::PF_NOBAN)) { AddLocal(addr, LOCAL_BIND); } From 4e0d5788ba5771c81bc0ff2e6523cf9accddae46 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Fri, 9 Apr 2021 20:32:40 +0200 Subject: [PATCH 2/3] test: add net permissions noban/download unit test coverage to clarify/test the relationship and NetPermissions operations involving the NetPermissionFlags PF_NOBAN and PF_DOWNLOAD. Co-authored-by: Vasil Dimov --- src/test/netbase_tests.cpp | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index 33b56624a8..b316a37c6e 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -395,6 +395,28 @@ BOOST_AUTO_TEST_CASE(netpermissions_test) BOOST_CHECK(NetWhitebindPermissions::TryParse("@1.2.3.4:32", whitebindPermissions, error)); BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE); + NetWhitebindPermissions noban, noban_download, download_noban, download; + + // "noban" implies "download" + BOOST_REQUIRE(NetWhitebindPermissions::TryParse("noban@1.2.3.4:32", noban, error)); + BOOST_CHECK_EQUAL(noban.m_flags, NetPermissionFlags::PF_NOBAN); + BOOST_CHECK(NetPermissions::HasFlag(noban.m_flags, NetPermissionFlags::PF_DOWNLOAD)); + BOOST_CHECK(NetPermissions::HasFlag(noban.m_flags, NetPermissionFlags::PF_NOBAN)); + + // "noban,download" is equivalent to "noban" + BOOST_REQUIRE(NetWhitebindPermissions::TryParse("noban,download@1.2.3.4:32", noban_download, error)); + BOOST_CHECK_EQUAL(noban_download.m_flags, noban.m_flags); + + // "download,noban" is equivalent to "noban" + BOOST_REQUIRE(NetWhitebindPermissions::TryParse("download,noban@1.2.3.4:32", download_noban, error)); + BOOST_CHECK_EQUAL(download_noban.m_flags, noban.m_flags); + + // "download" excludes (does not imply) "noban" + BOOST_REQUIRE(NetWhitebindPermissions::TryParse("download@1.2.3.4:32", download, error)); + BOOST_CHECK_EQUAL(download.m_flags, NetPermissionFlags::PF_DOWNLOAD); + BOOST_CHECK(NetPermissions::HasFlag(download.m_flags, NetPermissionFlags::PF_DOWNLOAD)); + BOOST_CHECK(!NetPermissions::HasFlag(download.m_flags, NetPermissionFlags::PF_NOBAN)); + // Happy path, can parse flags BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,forcerelay@1.2.3.4:32", whitebindPermissions, error)); // forcerelay should also activate the relay permission @@ -407,7 +429,7 @@ BOOST_AUTO_TEST_CASE(netpermissions_test) // Allow dups BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,relay,noban,noban@1.2.3.4:32", whitebindPermissions, error)); - BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN); + BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN | PF_DOWNLOAD); // "noban" implies "download" // Allow empty BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,relay,,noban@1.2.3.4:32", whitebindPermissions, error)); @@ -428,6 +450,8 @@ BOOST_AUTO_TEST_CASE(netpermissions_test) // Happy path for whitelist parsing BOOST_CHECK(NetWhitelistPermissions::TryParse("noban@1.2.3.4", whitelistPermissions, error)); BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, PF_NOBAN); + BOOST_CHECK(NetPermissions::HasFlag(whitelistPermissions.m_flags, NetPermissionFlags::PF_NOBAN)); + BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay@1.2.3.4/32", whitelistPermissions, error)); BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, PF_BLOOMFILTER | PF_FORCERELAY | PF_NOBAN | PF_RELAY); BOOST_CHECK(error.empty()); From 36fb036d25e2a3016b36873456e5a9e6251ffef8 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Wed, 14 Apr 2021 17:10:28 +0200 Subject: [PATCH 3/3] p2p: allow NetPermissions::ClearFlag() only with PF_ISIMPLICIT NetPermissions::ClearFlag() is currently only called in the codebase with an `f` value of NetPermissionFlags::PF_ISIMPLICIT. If that should change in the future, ClearFlag() should not be called with `f` being a subflag of a multiflag, e.g. NetPermissionFlags::PF_RELAY or NetPermissionFlags::PF_DOWNLOAD, as that would leave `flags` in an invalid state corresponding to none of the existing NetPermissionFlags. Therefore, allow only calling ClearFlag with the implicit flag for now. Co-authored-by: Vasil Dimov --- src/net_permissions.h | 6 ++++++ src/test/fuzz/net_permissions.cpp | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/net_permissions.h b/src/net_permissions.h index bba0ea1695..142b317bf6 100644 --- a/src/net_permissions.h +++ b/src/net_permissions.h @@ -51,8 +51,14 @@ public: { flags = static_cast(flags | f); } + //! ClearFlag is only called with `f` == NetPermissionFlags::PF_ISIMPLICIT. + //! If that should change in the future, be aware that ClearFlag should not + //! be called with a subflag of a multiflag, e.g. NetPermissionFlags::PF_RELAY + //! or NetPermissionFlags::PF_DOWNLOAD, as that would leave `flags` in an + //! invalid state corresponding to none of the existing flags. static inline void ClearFlag(NetPermissionFlags& flags, NetPermissionFlags f) { + assert(f == NetPermissionFlags::PF_ISIMPLICIT); flags = static_cast(flags & ~f); } }; diff --git a/src/test/fuzz/net_permissions.cpp b/src/test/fuzz/net_permissions.cpp index 544a33047b..6fdf4b653c 100644 --- a/src/test/fuzz/net_permissions.cpp +++ b/src/test/fuzz/net_permissions.cpp @@ -25,7 +25,7 @@ FUZZ_TARGET(net_permissions) (void)NetPermissions::ToStrings(net_whitebind_permissions.m_flags); (void)NetPermissions::AddFlag(net_whitebind_permissions.m_flags, net_permission_flags); assert(NetPermissions::HasFlag(net_whitebind_permissions.m_flags, net_permission_flags)); - (void)NetPermissions::ClearFlag(net_whitebind_permissions.m_flags, net_permission_flags); + (void)NetPermissions::ClearFlag(net_whitebind_permissions.m_flags, NetPermissionFlags::PF_ISIMPLICIT); (void)NetPermissions::ToStrings(net_whitebind_permissions.m_flags); } @@ -35,7 +35,7 @@ FUZZ_TARGET(net_permissions) (void)NetPermissions::ToStrings(net_whitelist_permissions.m_flags); (void)NetPermissions::AddFlag(net_whitelist_permissions.m_flags, net_permission_flags); assert(NetPermissions::HasFlag(net_whitelist_permissions.m_flags, net_permission_flags)); - (void)NetPermissions::ClearFlag(net_whitelist_permissions.m_flags, net_permission_flags); + (void)NetPermissions::ClearFlag(net_whitelist_permissions.m_flags, NetPermissionFlags::PF_ISIMPLICIT); (void)NetPermissions::ToStrings(net_whitelist_permissions.m_flags); } }