Additional BSD & select improvements - #392
Conversation
fdesbiens
left a comment
There was a problem hiding this comment.
Thanks for this — there are four independent improvements bundled here, and three of them are genuinely good catches. Excluding a listening socket from writefds, excluding a bound-but-unconnected TCP socket from writefds, and the unlisten on the failed-accept path are all real POSIX/resource bugs, and the added regression tests are welcome.
Two things need to change before this can land, and I'd like to discuss a third:
-
It breaks the
v6_bsd_raw_buildconfiguration. Theconstadded tonx_bsd_sendtois not propagated to the two internal helpers it forwards to, and the library is compiled with-Werror. See the comment onnxd_bsd.c:3690— this is a one-line-per-site fix. -
Making
inet_aton()strict is the wrong direction.inet_aton()is specified by 4.3BSD and implemented by glibc/musl/BSD to accepta,a.b,a.b.canda.b.c.d;inet_pton()is the strict one. The PR makesinet_atonreject the abbreviated forms while leavinginet_addrlenient, which is also self-inconsistent, and it rewrites three previously-passing tests to expect the new failures. Strictness should be applied only at theinet_ptoncall sites. Details onnxd_bsd.c:6012.
Related: the rename to nx_bsd_inet_aton_impl removes nx_bsd_inet_aton from the public API, which breaks applications built with NX_BSD_ENABLE_NATIVE_API (nxd_bsd.h:1007), and the new function-like macro inverts the direction of the entire override block (nxd_bsd.h:236).
- The
nx_bsd_select_wakeupchange looks redundant — NetX already delivers a URG notification throughnx_bsd_tcp_exception_notify. Seenxd_bsd.c:10334; happy to be corrected if you have a case in mind that the existing callback misses.
There are also a couple of correctness issues in the new select() exceptfds scan
(nxd_bsd.c:8388) and in the unlisten guard (nxd_bsd.c:5011), plus one test assertion that passes by coincidence (netx_bsd_pton_test.c:125).
Two project-policy items: the new use_strict_parsing parameter needs to be documented in the function's comment block , and the nx_bsd_sendto signature change needs a matching documentation update in rtos-docs-asciidoc.
Finally, a general style note: the new code uses if( and a->b, whereas this file consistently uses if ( and a -> b. Please match the surrounding style.
| /* */ | ||
| /**************************************************************************/ | ||
| INT nx_bsd_sendto(INT sockID, CHAR *msg, INT msgLength, INT flags, struct nx_bsd_sockaddr *destAddr, INT destAddrLen) | ||
| INT nx_bsd_sendto(INT sockID, CHAR *msg, INT msgLength, INT flags, const struct nx_bsd_sockaddr *destAddr, INT destAddrLen) |
There was a problem hiding this comment.
Adding
consthere is correct and matches POSIX, but it isn't propagated to the two internal
helpers thatsendtoforwardsdestAddrto, so this breaks the build.Both helpers still take a non-const pointer:
- declarations at
nxd_bsd.c:178andnxd_bsd.c:184- definitions at
nxd_bsd.c:12760andnxd_bsd.c:13064- call sites at
nxd_bsd.c:3762(NX_BSD_RAW_PPPOE_SUPPORT) andnxd_bsd.c:3765
(NX_BSD_RAW_SUPPORT)Compiling
nxd_bsd.cwith the CI flags and-DNX_BSD_RAW_SUPPORT:PR head : nxd_bsd.c:3765:101: warning: passing argument 5 of ‘_nx_bsd_hardware_internal_sendto’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] dev : cleanThe library target is built with
PRIVATE -Werror -Wall -Wextra
(test/cmake/netxduo/CMakeLists.txt:393) andv6_bsd_raw_builddefinesNX_BSD_RAW_SUPPORT
(test/cmake/netxduo/CMakeLists.txt:138and:327), so this is a hard failure in that
configuration rather than just a warning.Please add
constto the two helper declarations and definitions.While you're there: the casts inside this function now discard
consttoo —
nxd_bsd.c:3838,:3839and:3852–:3857cast aconst struct nx_bsd_sockaddr *to
struct nx_bsd_sockaddr_in */..._in6 *. That is MISRA C 2012 Rule 11.8 (required: a cast
shall not remove aconstqualification). They should become
(const struct nx_bsd_sockaddr_in *)etc.
| value |= ip_address_number[i] << (24 - (i*8)); | ||
| } | ||
| } | ||
| else if (use_strict_parsing == NX_TRUE) |
There was a problem hiding this comment.
The strict/lenient split itself is a good idea, but it's being applied to the wrong function.
inet_aton()is specified by 4.3BSD and implemented by glibc, musl and the BSDs to accept all
four forms —a,a.b,a.b.c,a.b.c.d. Theinet_aton(3)man page documents this
explicitly.inet_pton()is the function that accepts only the full dotted-quad form. So making
inet_atonstrict moves away from POSIX/BSD behaviour rather than toward it, and it is a
silent behaviour change for every existing application that relies on the documented forms.It also makes the two entry points inconsistent with each other:
nx_bsd_inet_addris routed
withNX_FALSEatnxd_bsd.c:6103, so after this change
inet_addr("11.10.2570")succeeds whileinet_aton("11.10.2570")fails. On every real platform
inet_addr()is a thin wrapper overinet_aton()and they share one parser, so this divergence
would be surprising.Suggested change: pass
NX_FALSEfrominet_aton(keeping today's behaviour) andNX_TRUE
only from the twoinet_ptoncall sites atnxd_bsd.c:13557andnxd_bsd.c:13666. That also
lets the three test changes in this PR be reverted (see the comments on
netx_bsd_aton_test.c:65andnetx_bsd_recvfromto_test.c:247).Minor:
return 0;should bereturn(0);to match every other return in this function, and
use_strict_parsing == NX_TRUEis better written as a plain truth test since the parameter is an
INTsupplied by internal callers.
| nx_bsd_in_addr_t nx_bsd_inet_addr(const CHAR *buffer); | ||
| CHAR *nx_bsd_inet_ntoa(struct nx_bsd_in_addr address_to_convert); | ||
| INT nx_bsd_inet_aton(const CHAR *cp_arg, struct nx_bsd_in_addr *addr); | ||
| INT nx_bsd_inet_aton_impl(const CHAR *address_buffer_ptr, struct nx_bsd_in_addr *addr, INT use_strict_parsing); |
There was a problem hiding this comment.
This renames a public API function without leaving anything behind under the old name. After this
change nonx_bsd_inet_atonsymbol exists anywhere —git grep nx_bsd_inet_atonon the PR head
returns only the_implspelling.That breaks applications built with
NX_BSD_ENABLE_NATIVE_API. That option is documented and
user-selectable (nxd_bsd.h:117), and when it is defined the override block atnxd_bsd.h:180is
skipped entirely — so those applications callnx_bsd_inet_aton()directly, and there is no
replacement for them at all, strict or lenient. The newinet_atonmacro doesn't help, because
it lives inside the block that native mode compiles out.Suggestion that avoids all of this: make the strict/lenient worker a
statichelper (e.g.
nx_bsd_inet_aton_internal(const CHAR *, struct nx_bsd_in_addr *, UINT strict)) and keep
nx_bsd_inet_aton()as the public function, unchanged in signature and behaviour, calling the
helper with the lenient flag.nx_bsd_inet_pton()then calls the helper with the strict flag.
The header needs no change at all,NX_BSD_ENABLE_NATIVE_APIkeeps working, and the internal
helper stays out of the public surface.
| #define nx_bsd_inet_addr inet_addr | ||
| #define nx_bsd_inet_ntoa inet_ntoa | ||
| #define nx_bsd_inet_aton inet_aton | ||
| #define inet_aton(address_buffer_ptr, addr) nx_bsd_inet_aton_impl(address_buffer_ptr, addr, NX_TRUE) |
There was a problem hiding this comment.
Three problems with this line, all of which disappear if the
statichelper approach in the
nxd_bsd.h:1007comment is used instead.
It reverses the direction of the block. Every other entry in this section maps the
internalnx_bsd_*name onto the plain POSIX name (#define nx_bsd_inet_addr inet_addr),
which is how thenx_bsd_-prefixed definitions innxd_bsd.ccome to be compiled under their
POSIX names. This line goes the other way, defininginet_atonin terms of an
nx_bsd_*symbol, so the file now contains two opposite conventions in adjacent lines.It is the only function-like macro in a block of object-like macros. As a result
inet_atonis no longer usable as a value —&inet_aton, or passing it as a callback, stops
compiling for application code that previously worked.The macro parameters are not parenthesised. MISRA C 2012 Rule 20.7 (required) requires
macro parameters to be wrapped in parentheses in the expansion, i.e.
nx_bsd_inet_aton_impl((address_buffer_ptr), (addr), NX_TRUE).
| { | ||
| /* Convert IPv4 address from presentation to numeric. */ | ||
| if(nx_bsd_inet_aton(src, &ipv4_addr)) | ||
| if(nx_bsd_inet_aton_impl(src, &ipv4_addr, NX_TRUE)) |
There was a problem hiding this comment.
use_strict_parsingonly enforces the number of dot-separated groups. It doesn't restrict the
digit format, soinet_pton(AF_INET, ...)still accepts input that POSIX requires it to reject:
- hex groups —
"0xb.0xa.0xa.0xa"- octal groups and leading zeros —
"013.012.012.012","127.0.0.010"- trailing whitespace, accepted by the
nx_bsd_isspacecheck atnxd_bsd.c:5966The existing assertions at
netx_bsd_aton_test.c:69-83confirm the hex and octal forms still
return 1, and those go through the same parser thatinet_ptonnow uses withNX_TRUE.For strict mode to actually mean "POSIX
inet_pton", it also needs to require decimal digits
only, reject a leading0on a multi-digit group, and reject trailing whitespace. Worth adding
test cases for each of those, since they're the interesting inputs.
| inet_aton(), which performs strict parsing. The a.b.c form must fail. */ | ||
| status = inet_aton("11.10.2570", &in_val); /* 2570 == 0x0a0a */ | ||
| if((status != 1) || (in_val.s_addr != htonl(0x0b0a0a0a))) | ||
| if(status != 0) |
There was a problem hiding this comment.
These two assertions were passing before this PR, and they were testing documented
inet_aton()
behaviour — thea.b.canda.bforms are part of the function's contract on every platform
that has it. Rewriting them to expect failure encodes the behaviour change rather than
testing it.If
inet_atonstays lenient as suggested onnxd_bsd.c:6012, this hunk can simply be reverted.
The strict cases belong in theinet_ptontests instead, where rejecting"11.10.2570"is
correct — and ideally alongside the hex/octal/leading-zero cases from thenxd_bsd.c:13557
comment
| /* a.b format (11.657930) must be rejected under strict parsing. */ | ||
| rc = inet_aton("11.657930", &in_val); | ||
| if (rc == 0) | ||
| if (rc != 0) |
There was a problem hiding this comment.
Same point as on
netx_bsd_aton_test.c:65: this block was added specifically to assert that
inet_aton()still accepts the abbreviated forms, and it's now inverted to assert the opposite.
Please revert this hunk along with theinet_atonbehaviour.
| * | ||
| * 5. A listening (master) socket with a pending connection must be reported | ||
| * readable (ready to accept) but MUST NOT be reported writable, even though | ||
| * the master is internally marked CONNECTED once a client connects. |
There was a problem hiding this comment.
Tests 4 and 5 cover the two
writefdschanges nicely, but the other half of this PR — the
exceptfdsURG scan inselect()(nxd_bsd.c:8380-8400) and the URG check in
nx_bsd_select_wakeup(nxd_bsd.c:10334-10361) — has no test at all. We need
matching regression tests and 100% coverage for new or extended behaviour.Worth adding at least: a socket in
exceptfdsonly (noreadfds) that becomes ready when an
urgent segment arrives, and the negative case where normal data must not setexceptfds— the
latter partly exists as Test 2, but only for thereadfds+exceptfdscombination.Note that the strict-parsing branch at
nxd_bsd.c:6012is covered, and the lenienta.b/
a.b.cbranches keep their coverage throughnetx_bsd_inet_addr_pton_test.c:73-77, so no
coverage is lost there.Also worth confirming: does the
unlistenfix atnxd_bsd.c:5011have a test? The failed-accept
path it depends on is hard to reach, but without one the fix is untested.
| /* inet_pton shall not accept abbreviated IPv4 addresses */ | ||
| status = inet_pton(AF_INET, ipv4_addr_str3, ipv4_addr); | ||
| if((status != 1) || (memcmp(ipv4_addr, ipv4_addr_num3, 4) != 0 )) | ||
| if((status == 1) || (memcmp(ipv4_addr, ipv4_addr_num3, 4) == 0 )) |
There was a problem hiding this comment.
The
memcmphalf of this condition doesn't test anything meaningful. Wheninet_ptonrejects an
address it returns 0 without writing todst, soipv4_addrstill holds the result of the last
successful call —ipv4_addr_str1="1.2.3.4"from line 111. The assertion therefore passes
only because0x01020304happens to differ fromipv4_addr_num3(0x0b0a0a0a); reorder the
fixtures and it would start failing for no real reason.
if (status != 0) error_counter++;is the whole check.(Unrelated pre-existing bug, since you're in the area:
netx_bsd_inet_addr_pton_test.c:111uses
&&where||was clearly intended, so that assertion can never fail. Fine to leave for a
separate PR.)
| /* Signal client thread to connect. */ | ||
| tx_semaphore_put(&sema_1); | ||
|
|
||
| /* ================================================================ |
There was a problem hiding this comment.
Test 5 is defined here and Test 4 about 130 lines further down at line 302, so the file now reads
1, 2, 3, 5, 4. The ordering is forced by where each test has to run relative toaccept(), which
is fine — just swap the two numbers so they read in order.
nx_bsd_sendto
Aligning with POSIX. Const parameter before struct nx_bsd_sockaddr *destAddr.
nx_bsd_soc_close
Unlisten in case of failure in accepting connection due to resource limitation.
nx_bsd_inet_aton_impl
Aligning with POSIX. Use strict parsing to allow notation only with 4 numbers and 3 dots for ipv4.
nx_bsd_select
Aligning with POSIX. Optimizing writefds.
Fixing exceptfds.
nx_bsd_select_wakeup
Fixing exceptfds.
nx_bsd_inet_pton
Aligning with POSIX. Use strict parsing to allow notation only with 4 numbers and 3 dots for ipv4.