From: Ido Schimmel <idosch@nvidia.com>
To: 안도현 <gomsoup@gmail.com>, nicoyip.dev@gmail.com
Cc: dsahern@kernel.org, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, sd@queasysnail.net,
netdev@vger.kernel.org
Subject: Re: [BUG] ipv4: slab-use-after-free in fib_nhc_update_mtu() - unlocked walk of kfree_rcu'd fib_nh_exception on NETDEV_CHANGEMTU
Date: Thu, 6 Aug 2026 15:32:12 +0300 [thread overview]
Message-ID: <20260806123212.GA1868331@shredder> (raw)
In-Reply-To: <CAJzEm4pwK2rC=1YpA1XG3qCR5BPQ6O6oFY5=L5dDViKJN-jqbQ@mail.gmail.com>
Please send plain text emails. HTML emails are filtered and don't make
their way to the ML. See a comment at the end.
On Thu, Aug 06, 2026 at 03:07:27PM +0900, 안도현 wrote:
> HANDLING NOTE
> =============
> This bug was identified with the assistance of an automated (AI) code
> audit. Per Documentation/process/security-bugs.rst, an AI-assisted
> finding must be treated as public, so this is reported in the open and
> was not submitted to the [1]security@kernel.org embargo process.
> For the same reason, I am not including the reproducer in this public
> message. I have a tested source reproducer and will provide it privately
> to maintainers on request.
>
> SUMMARY
> =======
> fib_nhc_update_mtu() traverses and writes fib_nh_exception ("fnhe")
> objects using rcu_dereference_protected(x, 1) -- asserting a lock is held
> -- but its only caller holds RTNL, not the fnhe lock, and is not in an RCU
> read-side critical section. fnhe objects are freed with kfree_rcu() from
> softirq under fnhe_lock, so on a CONFIG_PREEMPT_RCU kernel a grace period
> can end mid-walk and free an fnhe the loop still dereferences. This is a
> slab-use-after-free (read and write) of a kmalloc-96 object, reproduced
> under KASAN on 7.2-rc6. Present unchanged since v4.19.
>
> AFFECTED VERSIONS
> =================
> Introduced in v4.19 by:
> af7d6cce5369 ("net: ipv4: update fnhe_pmtu when first hop's MTU
> changes")
> which added the loop (then nh_update_mtu(); renamed and exported as
> fib_nhc_update_mtu() by 06c77c3e67b0 "ipv4: Rename and export
> nh_update_mtu"). The unlocked walk is identical from v4.19 to mainline.
>
> Reproduced on:
> Linux 7.2-rc6, mainline commit 075b74841bd0, arm64, KASAN.
> Present in current mainline (fib_semantics.c fib_nhc_update_mtu()).
>
> THE DEFECT
> ==========
> net/ipv4/fib_semantics.c, fib_nhc_update_mtu():
>
> bucket = rcu_dereference_protected(nhc->nhc_exceptions, 1);
> if (!bucket)
> return;
> for (i = 0; i < FNHE_HASH_SIZE; i++) {
> struct fib_nh_exception *fnhe;
> for (fnhe = rcu_dereference_protected(bucket[i].chain, 1); fnhe;
> fnhe = rcu_dereference_protected(fnhe->fnhe_next, 1)) {
> if (fnhe->fnhe_mtu_locked) {
> if (new <= fnhe->fnhe_pmtu) {
> fnhe->fnhe_pmtu = new;
> fnhe->fnhe_mtu_locked = false;
> }
> } else if (new < fnhe->fnhe_pmtu || orig == fnhe->fnhe_pmtu) {
> fnhe->fnhe_pmtu = new;
> }
> }
> }
>
> The "1" asserts the caller holds the fnhe update-side lock. It does not.
> The only caller is fib_sync_mtu() (same file), reached on a device MTU
> change:
>
> NETDEV_CHANGEMTU -> fib_netdev_event() -> fib_sync_mtu()
> -> fib_nhc_update_mtu()
>
> and call_netdevice_notifiers_info() (net/core/dev.c) runs the chain under
> ASSERT_RTNL() only -- no rcu_read_lock(), no local_bh_disable().
>
> fnhe objects are created and freed under fnhe_lock in softirq on the
> ICMP/redirect input path:
>
> __ip_rt_update_pmtu() / __ip_do_redirect() -> update_or_create_fnhe()
> -> fnhe_remove_oldest() / ip_del_fnhe() -> kfree_rcu()
>
> RTNL and fnhe_lock are unrelated locks; RTNL does not exclude softirq.
> Under CONFIG_PREEMPT_RCU a process-context loop that never entered an RCU
> read-side critical section is not an RCU reader, so a grace period may end
> mid-walk and the kfree_rcu() callback frees an fnhe the loop is still
> dereferencing.
>
> fnhe_lock is static to net/ipv4/route.c and not visible in
> fib_semantics.c, which is presumably why the code used the constant
> assertion instead of taking the lock.
>
> KASAN REPORT (7.2-rc6, arm64; timestamps removed, stacks trimmed)
> =================================================================
> BUG: KASAN: slab-use-after-free in fib_nhc_update_mtu+0x78/0xd0
> Read of size 1 at addr ffff000016596794 by task payload/100
>
> CPU: 0 ... 7.2.0-rc6 #1 PREEMPT
> Call trace:
> fib_nhc_update_mtu
> fib_sync_mtu
> fib_netdev_event
> call_netdevice_notifiers_info
> netif_set_mtu_ext
> dev_set_mtu
> dev_ioctl
> sock_ioctl
>
> Allocated by task 99:
> update_or_create_fnhe
> __ip_rt_update_pmtu
> icmp_unreach
> icmp_rcv
> ip_local_deliver
>
> Freed by task 27:
> __rcu_free_sheaf_prepare
> rcu_free_sheaf
> rcu_core
> handle_softirqs
>
> The buggy address belongs to the cache kmalloc-96 of size 96.
> The buggy address is located 20 bytes inside of freed 96-byte region.
>
> (struct fib_nh_exception is 80 bytes -> kmalloc-96; offset 20 is the bool
> fnhe_mtu_locked read at the top of the loop body.)
>
> CONDITIONS
> ==========
> - CONFIG_PREEMPT_RCU=y (all CONFIG_PREEMPT / PREEMPT_RT kernels).
> - Concurrency between a device MTU change (the walk) and ICMP-induced fnhe
> churn/expiry (the alloc + kfree_rcu free).
> - Reachability: SIOCSIFMTU on a device carrying routes with exceptions
> needs CAP_NET_ADMIN in the device's netns; the fnhe population is driven
> by ICMP "fragmentation needed". Where unprivileged user namespaces are
> permitted, both are reachable by an unprivileged local user via
> unshare(CLONE_NEWUSER|CLONE_NEWNET) + /dev/net/tun; where restricted,
> it requires CAP_NET_ADMIN in some netns.
>
> REPRODUCER
> ==========
> I have a tested C reproducer that triggers the KASAN report above on
> 7.2-rc6/arm64, typically within seconds. Per the handling note it is not
> included here; I can provide it privately to maintainers on request.
>
> IMPACT
> ======
> The demonstrated result is a KASAN-detected read, and a write of dev->mtu,
> to an RCU-freed kmalloc-96 object (a memory-safety violation). No
> privilege-escalation exploit has been developed or demonstrated.
>
> MITIGATION
> ==========
> Restricting unprivileged user namespaces, restricting creation of network
> namespaces, or denying unprivileged access to /dev/net/tun reduces
> exposure to unprivileged local users. None of these protect a context
> that already holds CAP_NET_ADMIN in a network namespace. (I have not
> verified that these fully block every path.)
>
> PROPOSED FIX
> ============
> fnhe objects are freed via kfree_rcu(), so an RCU read-side critical
> section around the traversal keeps them alive for its duration; fnhe_lock
> is not visible here, so this is also the least invasive fix.
> Best regards,
> Dohyeon An
Chengfeng Ye sent a fix last week, but looks like we need v2 which
acquires fnhe_lock for the per-entry update (while still using RCU for
the traversal).
https://lore.kernel.org/netdev/20260731162938.3388534-1-nicoyip.dev@gmail.com/
next parent reply other threads:[~2026-08-06 12:32 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <CAJzEm4pwK2rC=1YpA1XG3qCR5BPQ6O6oFY5=L5dDViKJN-jqbQ@mail.gmail.com>
2026-08-06 12:32 ` Ido Schimmel [this message]
2026-08-06 13:36 ` [BUG] ipv4: slab-use-after-free in fib_nhc_update_mtu() - unlocked walk of kfree_rcu'd fib_nh_exception on NETDEV_CHANGEMTU Chengfeng Ye
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260806123212.GA1868331@shredder \
--to=idosch@nvidia.com \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=edumazet@google.com \
--cc=gomsoup@gmail.com \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=nicoyip.dev@gmail.com \
--cc=pabeni@redhat.com \
--cc=sd@queasysnail.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox