As noticed during review today in #20685 (review) of the upcoming I2P network support, CService::port is uint16_t but is passed around the codebase and into the ctors as int, which causes uneeded conversions and casts. We can avoid these (including in the incoming I2P code without further changes to it) by using ports with the correct type. The remaining conversions are pushed out to the user input boundaries where they can be range-checked and raise with user feedback in the next patch.
net, refactor: pass uint16 CService::port as uint16 #21328
pull jonatack wants to merge 4 commits into bitcoin:master from jonatack:pass-uint16_t-CService-port-as-uint16_t changing 18 files +82 −56-
jonatack commented at 10:24 PM on March 1, 2021: member
- DrahtBot added the label P2P on Mar 1, 2021
- DrahtBot added the label RPC/REST/ZMQ on Mar 1, 2021
- DrahtBot added the label Utils/log/libs on Mar 1, 2021
- DrahtBot added the label Validation on Mar 1, 2021
- jonatack force-pushed on Mar 2, 2021
- jonatack force-pushed on Mar 2, 2021
-
in src/test/netbase_tests.cpp:89 in f8b1c2140e outdated
82 | @@ -83,31 +83,31 @@ BOOST_AUTO_TEST_CASE(netbase_properties) 83 | 84 | } 85 | 86 | -bool static TestSplitHost(std::string test, std::string host, int port) 87 | +bool static TestSplitHost(const std::string& test, const std::string& host, uint16_t port) 88 | { 89 | std::string hostOut; 90 | - int portOut = -1; 91 | + uint16_t portOut = 1;
vasild commented at 5:44 AM on March 2, 2021:Usually
0is used as "dummy/invalid" port. Port1is reserved for (from/etc/services):tcpmux 1/tcp #TCP Port Service Multiplexer tcpmux 1/udp #TCP Port Service Multiplexer
jonatack commented at 9:29 PM on March 6, 2021:Thanks, done.
in src/util/strencodings.cpp:111 in f8b1c2140e outdated
106 | @@ -107,7 +107,8 @@ std::vector<unsigned char> ParseHex(const std::string& str) 107 | return ParseHex(str.c_str()); 108 | } 109 | 110 | -void SplitHostPort(std::string in, int &portOut, std::string &hostOut) { 111 | +void SplitHostPort(std::string in, uint16_t& portOut, std::string& hostOut) 112 | +{
vasild commented at 5:52 AM on March 2, 2021:(github does not let me to comment on line
118, so commenting on line111instead):118 int32_t n; 119 if (ParseInt32(in.substr(colon + 1), &n) && n > 0 && n < 0x10000) { 120 in = in.substr(0, colon); 121 portOut = n; 122 }- Line
121does not cause a warning? - Maybe change
0x10000tostd::numeric_limits<uint16_t>::max(). - If this pattern
if (ParseUint32() && n is in [0,64k])is used in other places, then maybe worth addingParseUint16(). We haveParseUint{8,32,64}().
jonatack commented at 9:30 PM on March 6, 2021:Good ideas! (no warning seen). Added a
ParseUInt16()method and updatedSplitHostPort()to use it.
jonatack commented at 10:05 PM on March 6, 2021:We have unit tests for
ParseUInt32()andParseUInt64()but not forParseUInt8()orParseUInt16(). Can add in a follow-up or here depending on reviewer preference.in src/httpserver.cpp:293 in f8b1c2140e outdated
289 | @@ -290,7 +290,7 @@ static bool ThreadHTTP(struct event_base* base) 290 | /** Bind HTTP server to specified addresses */ 291 | static bool HTTPBindAddresses(struct evhttp* http) 292 | { 293 | - int http_port = gArgs.GetArg("-rpcport", BaseParams().RPCPort()); 294 | + uint16_t http_port = gArgs.GetArg("-rpcport", BaseParams().RPCPort());
vasild commented at 5:59 AM on March 2, 2021:GetArg()returnsuint64_tand here we would silently narrow it down touint16_t. It is ok since before this PR we would narrow it down tointand later narrow theintdown touint16_t.If the user makes a typo and specifies
-rpcport=88332we will bind on22796instead (with and without this PR). Best to print an error and refuse to start, not necessary in this PR.
jonatack commented at 9:30 PM on March 6, 2021:Decided to use named casts at each of these user input entry points. I agree with better user input parsing and errors....this PR has become a bit large but happy to look at it in a follow-up.
vasild approvedvasild commented at 6:11 AM on March 2, 2021: memberACK f8b1c2140eea958ab0e6f42ef2daab7000375d82
I think this PR is good and safe as it is. Some non-blocker comments below. It would be even better if it adds some checks that ports given by the user are in range, like in https://github.com/bitcoin/bitcoin/pull/8394/files#diff-134ccc4e5308f84fb9c03a9a1e1599209560370e49e30c7f545fa7b9ba7e07deR153
DrahtBot commented at 10:13 AM on March 2, 2021: member<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--174a7506f384e20aa4161008e828411d-->
Conflicts
Reviewers, this pull request conflicts with the following ones:
- #20457 (util: Make Parse{Int,UInt}{32,64} use locale independent std::from_chars(…) (C++17) instead of locale dependent strto{l,ll,ul,ull} by practicalswift)
- #20452 (util: Replace use of locale dependent atoi(…) with locale-independent std::from_chars(…) (C++17) by practicalswift)
- #20196 (net: fix GetListenPort() to derive the proper port by vasild)
- #18061 (util: Pass size to ParseHex to assist preallocation by elichai)
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 commented at 12:40 PM on March 2, 2021: contributorStrong Concept ACK
Thanks for removing this implicit conversion gotcha :)
uint16_tshould be used consistently for port numbers.Somewhat related: #19415 (comment)
laanwj commented at 11:34 AM on March 3, 2021: memberI like the idea of using specific types. But if we're going to change this, I think I would feel slightly better defining a
typedef Portfor this. What if we want to support a protocol in the future with 32-bit ports? I do not see this as likely… my intuition is that things are moving to one-endpoint-per-address conceptually instead of ports… and yes the port as a concept is typical to current Internet protocols, where they're all 16 bits. But I guess it's clearer too to have a named type.Anyhow concept ACK.
DrahtBot added the label Needs rebase on Mar 3, 2021jonatack force-pushed on Mar 6, 2021jonatack commented at 9:36 PM on March 6, 2021: memberThank you @vasild, @practicalswift and @laanwj. Updated per all of your suggestions.
jonatack force-pushed on Mar 6, 2021jonatack force-pushed on Mar 6, 2021DrahtBot removed the label Needs rebase on Mar 6, 2021practicalswift commented at 6:22 PM on March 7, 2021: contributorNot super excited by the introduction of
PortTBH.The problem is probably best illustrated by this UB quiz:
Q. Which of the two cases rely on undefined behaviour?
// Snippet 1 Port p; if (p != other_p) { }// Snippet 2 uint16_t p; if (p != other_p) { }<details> <summary>Click for answer</summary>
Both snippets rely on undefined behaviour (they are technically equivalent thanks to the
typedef).But that's not the point.
The point is that while the UB in snippet 2 is immediately clear (read of uninitialized
uint16_t) the UB in snippet 1 requires looking up information that is not available in the code snippet (more specifically thetypedef).</details>
Trying to make it as easy as possible to spot unsafe code is an important part of safe coding practice.
Personally I find that the introduction of
Portmakes it somewhat harder to spot unsafe code as illustrated above.in src/httpserver.cpp:318 in 6be96959ea outdated
316 | 317 | // Bind addresses 318 | - for (std::vector<std::pair<std::string, uint16_t> >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) { 319 | - LogPrint(BCLog::HTTP, "Binding RPC on address %s port %i\n", i->first, i->second); 320 | + for (std::vector<std::pair<std::string, Port> >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) { 321 | + LogPrint(BCLog::HTTP, "Binding RPC on address %s port %u\n", i->first, i->second);
vasild commented at 10:53 AM on March 9, 2021:These changes
%i->%uare not necessary becauseLogPrint()is type safe. The commitp2p: update port logging to unsigned integerscan be dropped.
jonatack commented at 10:08 PM on March 10, 2021:done
vasild commented at 11:13 AM on March 9, 2021: memberACK 9b7055f9a9bd7345fc7dfb51ed0bace8257e07cd
Notice that this is not the last commit - I tend to agree with @practicalswift wrt
uint16_tvsPortso I ACK the commit before those changes. Two more things to consider aboutPort:- The name
Portmay be too generic. I can imagine that "port" in some other context means something else, e.g. "usb port for connecting to a hardware wallet". MaybeIPPortwould be a better name. - At one place we do
Port n; ParseUInt16(..., &n);which kind of defeats the purpose of thePortabstraction. I don't see IP ports ever changing to something other than 16 bit unsigned integers.
jonatack commented at 12:59 PM on March 9, 2021: memberThanks for the feedback!
I agree that
Portis less greppable thanIPPort(and we do already have aToStringIPPort()method).No strong opinion, but here are a few reasons why I tend to agree with @laanwj and took the time to do it:
- ISTM not having a type alias here is why we're in this situation in the first place (and since a long time)
- I'm happy that we have an alias for
CAmount, for example; it makes clear to contributors which type should be used for amounts and the same would be true for port id numbers - This was a somewhat tedious change to make and I don't plan to do it again, take advantage of it ;)
- I think we'll be happier down the road if this is done than if we don't do it
I'm not sure whether to wait for more feedback or separate the type alias to another pull. Keep the feedback coming :pray:
vasild commented at 2:41 PM on March 9, 2021: memberYeah, those are also valid points :scroll: :thinking: :thought_balloon:
MarcoFalke commented at 3:07 PM on March 9, 2021: memberIf a new name is introduce, wouldn't it be better to make it fully type safe?
laanwj commented at 6:06 PM on March 9, 2021: memberQ. Which of the two cases rely on undefined behaviour?
Don't disagree with the point but whyy does C have to turn everything into UB, I hate this, I really do, wish we could use a sane language
practicalswift commented at 10:27 PM on March 9, 2021: contributorI agree with MarcoFalke: if we are to introduce
Portconsider making it fully type safe in order to avoid the pitfall I described above.Portability is important, but safety is more important (in general) :)
I suggest moving the
Portcommit to a separate PR since that is the only part of this PR that is not ready for merge.Q. Which of the two cases rely on undefined behaviour?
Don't disagree with the point but whyy does C have to turn everything into UB, I hate this, I really do, wish we could use a sane language
Heh, just don't shoot the messenger! :)
jonatack force-pushed on Mar 10, 2021vasild approvedvasild commented at 7:37 AM on March 11, 2021: memberACK cf9952da0528b22af77c169822777587bb2a87ee
If in doubt - KISS.
DrahtBot added the label Needs rebase on Mar 15, 2021jonatack force-pushed on Mar 15, 2021vasild approvedvasild commented at 4:21 PM on March 15, 2021: memberACK ae6eb4c8d549526232bcf6197a31de660b74b8af
practicalswift commented at 4:39 PM on March 15, 2021: contributorcr ACK ae6eb4c8d549526232bcf6197a31de660b74b8af: patch looks correct
DrahtBot removed the label Needs rebase on Mar 15, 2021laanwj referenced this in commit cb0aafbd21 on Mar 16, 2021DrahtBot added the label Needs rebase on Mar 16, 2021jonatack force-pushed on Mar 16, 2021vasild approvedvasild commented at 3:13 PM on March 16, 2021: memberACK a0573615cb31670743b3915135e3a03418fb9629
DrahtBot removed the label Needs rebase on Mar 16, 2021p2p, refactor: pass and use uint16_t CService::port as uint16_t 6423c8175futil: add ParseUInt16(), use it in SplitHostPort() 2875a764f7util: add missing braces and apply clang format to SplitHostPort() 6f09c0f6b5test: add missing netaddress include headers 52dd40a9fejonatack force-pushed on Mar 16, 2021jonatack commented at 6:57 PM on March 16, 2021: memberThanks @vasild! Updated one line per
git diff a057361 52dd40ato make a change needed after the merge of #19415.+++ b/src/test/fuzz/netbase_dns_lookup.cpp @@ -18,7 +18,7 @@ FUZZ_TARGET(netbase_dns_lookup) const std::string name = fuzzed_data_provider.ConsumeRandomLengthString(512); const unsigned int max_results = fuzzed_data_provider.ConsumeIntegral<unsigned int>(); const bool allow_lookup = fuzzed_data_provider.ConsumeBool(); - const int default_port = fuzzed_data_provider.ConsumeIntegral<int>(); + const uint16_t default_port = fuzzed_data_provider.ConsumeIntegral<uint16_t>();practicalswift commented at 8:57 PM on March 16, 2021: contributorcr ACK 52dd40a9febec1f4e70d777821b6764830bdec61: patch looks correct
vasild approvedvasild commented at 11:18 AM on March 17, 2021: memberACK 52dd40a9febec1f4e70d777821b6764830bdec61
MarcoFalke removed the label RPC/REST/ZMQ on Mar 17, 2021MarcoFalke removed the label Utils/log/libs on Mar 17, 2021MarcoFalke removed the label Validation on Mar 17, 2021MarcoFalke added the label Refactoring on Mar 17, 2021vasild commented at 5:18 PM on March 19, 2021: memberLooks like
master(at 05757aa86) has a problem that would be fixed by this PR - my "private" CI got this error:netbase.cpp:212:37: runtime error: implicit conversion from type 'int' of value -2147483632 (32-bit, signed) to type 'uint16_t' (aka 'unsigned short') changed the value to 16 (16-bit, unsigned)which seems unrelated to the changes I am testing there.
Edit: also in another PR: https://cirrus-ci.com/task/6488952220680192?command=ci#L3334
sipa commented at 5:23 PM on March 19, 2021: member@vasild Conversions to unsigned types are actually well-defined in the lamguage. Our sanitizer runs in sort of an over-eager mode and triggers on that because such conversions are often a sign of bugs. But here I suspect it's intentional, and valid.
MarcoFalke commented at 7:46 PM on March 19, 2021: membercr ACK 52dd40a9febec1f4e70d777821b6764830bdec61
MarcoFalke merged this on Mar 19, 2021MarcoFalke closed this on Mar 19, 2021jonatack deleted the branch on Mar 19, 2021sidhujag referenced this in commit d165b55c0f on Mar 20, 2021MarcoFalke referenced this in commit d2a78ee928 on Mar 21, 2021sidhujag referenced this in commit fc65f9795d on Mar 21, 2021Fabcien referenced this in commit 67221c860e on Feb 21, 2022Fabcien referenced this in commit c38178957a on Feb 21, 2022Fabcien referenced this in commit d349817bc5 on Feb 21, 2022DrahtBot locked this on Aug 16, 2022
github-metadata-mirror
This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-05-02 18:14 UTC
This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me