Repository navigation
Two NX_ASSERT invariants can be broken by a preempting thread #495
Description
Summary
Two NX_ASSERTs in the IP layer state invariants that hold while the IP mutex is held, but some
code paths write or read the state without it. A higher-priority thread that preempts at the wrong
point fails the assert, and NX_ASSERT_FAIL sleeps forever, in one case while holding the IP mutex.
Both were found by reading the code; neither can be opened deterministically from a test, so neither
has been reproduced. Line numbers are on dev at 86aa8546.
1. The received-fragment queue under NX_FRAGMENT_IMMEDIATE_ASSEMBLY
_nx_ip_fragment_disable takes the IP mutex, disables interrupts, clears
nx_ip_fragment_assembly (nx_ip_fragment_disable.c:94) and asserts that the received-fragment queue
is empty (:103).
With immediate assembly every enqueue onto nx_ip_received_fragment_head is followed, in the same
call, by (ip_ptr -> nx_ip_fragment_assembly)(ip_ptr), which takes the whole queue:
nx_ipv4_packet_receive.c:TX_RESTOREat:480then the call at:487(NAT path), and:621
then:628;nx_ipv6_process_fragment_option.c:TX_RESTOREat:201then the call at:208.
Between the TX_RESTORE and the call, the packet is on the queue and nothing holds the IP mutex
unless the caller does. A driver may call _nx_ip_packet_receive from a thread of its own. If that
thread is preempted at that point by a higher-priority thread calling nx_ip_fragment_disable, the
assert fails and that thread sleeps forever holding the IP mutex. If asserts are compiled out,
nx_ip_fragment_disable returns, the receiving thread resumes, and it calls the now-NULL
nx_ip_fragment_assembly.
2. The interface address list in _nxd_ipv6_interface_find
_nxd_ipv6_interface_find takes the head of a multicast group's interface address list and asserts
that it is a valid address (nxd_ipv6_interface_find.c:122).
_nx_icmpv6_DAD_failure marks the address invalid (nx_icmpv6_DAD_failure.c:87) and removes it
from the list afterwards (:105, :116). It runs on the IP thread. Several callers of
_nxd_ipv6_interface_find do not hold the IP mutex, for example nxd_udp_socket_send.c:393,
nx_icmp_ping6.c:92 and nx_icmpv6_send_rs.c:123. An application thread of higher priority than
the IP thread, readied inside that window, that sends to a multicast group joined on that interface,
fails the assert.
A related, deterministic case is fixed separately in #494: the application's address change
callback was called inside the same window, on the IP thread itself.
Suggested fixes
- In the receive paths, call the assembly while interrupts are still disabled, or check
nx_ip_fragment_assemblyagain afterTX_RESTORE; or havenx_ip_fragment_disabledrain the
queue instead of asserting that it is empty. - In
_nx_icmpv6_DAD_failure, remove the address from the list before marking it invalid, so a
reader never sees an invalid address at the head.
Activity
giovannicozzolongo commentedon Oct 22, 2026
I'd like to work on this. Could you assign it to me?