All of lore.kernel.org
 help / color / mirror / Atom feed
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/

       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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.