This series hardens AMT relay tunnel lifetime, validates gateway input, and
keeps unauthenticated Requests from consuming relay state. The problems
fixed by patches 3 and 4, and the pre-Query drop in patch 5, were found
during the AMT IPv6 outer transport work, which is not included here.
I am using the existing IPv4 AMT relay and gateway implementation in a
multicast delivery network, where gateways on access networks send IGMP/MLD
membership state through a relay to reach downstream receivers.
The first patch detaches the receive path before amt_dev_stop() tears down
events and tunnels, so no receive-path reader can queue work after the
teardown. The second closes the gc_wq re-arm use-after-free and makes
teardown safe for RCU readers and delayed group/source workers. The third
rejects Queries and Data from 0.0.0.0 before relay discovery and
revalidates deferred Queries. The fourth requires exactly four bytes for
IFLA_AMT_LOCAL_IP and IFLA_AMT_DISCOVERY_IP. The fifth makes pre-Query
drops visible, avoids counting successful reports as drops, and uses
concurrent-safe TX statistics. The sixth keeps Requests stateless,
authenticates Membership Updates and checks their IGMP/MLD message before
allocation, keys state by address and port, bounds per-source admission,
and silently drops failed endpoint admission.
Each commit message says how its problems were found. The problem in
patch 4 was triggered while writing an IPv6 selftest; the others were
found by review or code inspection.
Changes in v6 (patches 2-6 were v5's 1-5):
- New patch 1: clear the socket's sk_user_data before amt_dev_stop()'s
grace period, so no amt_rcv() can still queue event_wq or link a
tunnel after the teardown. Found by code inspection while reviewing
the changes below.
- Patch 2: run the deferred group/source GC directly instead of
flushing its work. flush_delayed_work() queues the work and waits for
it to run, which needs a free amt_wq active slot, so
amt_tunnel_expire() and the group GC, both amt_wq work, could deadlock
once every slot was held by such a waiter. amt_dev_stop() calls the
GC the same way. (Sashiko)
- Patch 2: amt_tunnel_expire() reads jiffies once (Sashiko), and reads
last_seen and marks the tunnel dead in one tunnel->lock section. The
Update path refreshes last_seen under that lock, once the encapsulated
message has passed its IGMP/MLD check, so an Update either keeps the
tunnel alive or finds it dead (Sashiko, raised on v5 5/5). An Update
never stores 0, which marks a tunnel no Update has refreshed.
- Patch 2: keep amt_group_work()'s loop braces, and order the
declarations the patch touches longest first.
- Patch 6: create or refresh tunnel state only once ip_mc_check_igmp()
or ipv6_mc_check_mld() has accepted the encapsulated message.
(Sashiko)
- Patch 6: the tunnel lookup skips dead tunnels and amt_update_tunnel()
retries once, so an Update that races amt_tunnel_expire() creates the
replacement instead of being dropped. (Sashiko)
- Patch 6: the Request path's per-source count skips dead tunnels too,
so a tunnel being expired no longer withholds a new endpoint's Query.
- Patch 6: drop the per-Update amt_update_relay_status() call, a no-op
since tunnel creation sets that status (Sashiko), and the last_seen 0
special case, since every tunnel is now stamped when it is created.
- Patch 6: retitled from "amt: harden relay authentication and
teardown"; its teardown changes moved to patch 2 in an earlier
revision. Its message now says why a refused Request gets no ICMP Host
Unreachable.
- Patches 2, 3, 5 and 6: correct commit-message and comment claims that
overstated or misdescribed the code, and say how each problem was
found without per-version history. Patch 4 is unchanged.
Changes in v5 (since the 2026-10-20 posting; v5 numbering, so its
patches 1-5 are v6's 2-6):
- Patch 1: flush the deferred group/source GC with flush_delayed_work()
instead of cancel_delayed_work_sync(). Both GC works re-arm only
themselves, so cancelling them on tunnel expiry or device stop stopped
periodic GC for good after the first expiry. The flush only reclaims
promptly; lifetime rests on RCU and the cleared owner pointers, and the
comment and changelog now say so.
- Patch 1: clear snode->gnode and gnode->tunnel_list with WRITE_ONCE().
Neither pointer is __rcu, so RCU_INIT_POINTER() drew two new sparse
errors.
- Patch 1: keep amt_group_work()'s single out: exit and move
rcu_read_unlock() below the label, instead of open-coding
rcu_read_unlock() and dev_put() at three exits. Every exit still
drops RCU and then the timer's device reference.
- Patch 5: drop the paragraph about tunnel liveness checks, which moved
to patch 1 in the previous revision, and fix one continuation-line
alignment.
- Patch 5: copy the secret generations in the Update path under a
seqcount, amt->key_seq, rather than under amt->lock, as the Sashiko
AI review of the 2026-10-15 posting suggested. Forged Updates now
take amt->lock only on PREEMPT_RT, to wait for a writer. Requests
still read key[0], nr_tunnels and max_tunnels in one O(1) amt->lock
section.
- Patch 5: say in the commit message why one address may hold at most
16 tunnels.
- All: state how each issue was found, and put Signed-off-by above the
"---" separator (it was below it in the previous posting, so git am
dropped it).
Testing: on base-commit below, each patch builds without new warnings
with W=1 C=1 (sparse) for IPv6=y, IPv6=n, PROVE_LOCKING and
PREEMPT_RT. With KASAN and lockdep enabled,
tools/testing/selftests/net/amt.sh passes (6/6) with no splats.
v5: https://lore.kernel.org/netdev/20261105125010.1107546-1-omar@blockcast.net/ (local)
v4 (mislabelled, 2026-10-20): https://lore.kernel.org/netdev/20261020002557.3984927-1-omar@blockcast.net/ (local)
v4 (re-posted 2026-10-15): https://lore.kernel.org/netdev/20261015220648.1545576-1-omar@blockcast.net/ (local)
v4: https://lore.kernel.org/netdev/20261013175407.947606-1-omar@blockcast.net/ (local)
v3: https://lore.kernel.org/netdev/20261012125943.558629-1-omar@blockcast.net/ (local)
v2: https://lore.kernel.org/netdev/20261009201455.1904698-1-omar@blockcast.net/ (local)
v1: https://lore.kernel.org/netdev/20261008003606.3666617-1-omar@blockcast.net/ (local)
Assisted-by: LLM
Omar Ramadan (6):
amt: stop receiving before amt_dev_stop() tears down its state
amt: fix use-after-free when a Membership Update re-arms a freed
tunnel
amt: reject a gateway Query or Data from 0.0.0.0 before a relay is
learned
amt: require an exact IPv4-sized IFLA_AMT_{LOCAL,DISCOVERY}_IP
amt: make pre-query report drops visible
amt: create relay tunnels only from authenticated Updates
drivers/net/amt.c | 623 +++++++++++++++++++++++++++++++++++-----------
include/net/amt.h | 31 ++-
2 files changed, 504 insertions(+), 150 deletions(-)
base-commit: 734bef548a96e8774d6eef5dfa2026c6edc4e80b
--
2.43.0