]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()
authorXiang Mei <xmei5@asu.edu>
Wed, 22 Jul 2026 21:02:03 +0000 (14:02 -0700)
committerJakub Kicinski <kuba@kernel.org>
Thu, 23 Jul 2026 21:11:37 +0000 (14:11 -0700)
rds_tcp_laddr_check() looks up a scoped IPv6 interface with
dev_get_by_index_rcu(), drops the RCU read-side lock, and only then
passes the bare struct net_device * into ipv6_chk_addr().

dev_get_by_index_rcu() only keeps the device alive within the same RCU
read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can
free the net_device; ipv6_chk_addr() then dereferences the stale pointer
in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading
freed memory.

Keep the RCU read-side lock held across the ipv6_chk_addr() call instead
of dropping it right after the lookup, so the device cannot be freed
while it is in use.

  BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
  Read of size 8 at addr ffff8880106ec000 by task exploit/153
  Call Trace:
   ...
   kasan_report (mm/kasan/report.c:595)
   __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
   ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972)
   rds_tcp_laddr_check (net/rds/tcp.c:370)
   rds_bind (net/rds/bind.c:248)
   __sys_bind (net/socket.c:1920)
   __x64_sys_bind (net/socket.c:1956)
   do_syscall_64 (arch/x86/entry/syscall_64.c:63)
   entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)

Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
Reported-by: Weiming Shi <bestswngs@gmail.com>
Signed-off-by: Xiang Mei <xmei5@asu.edu>
Reviewed-by: Allison Henderson <achender@kernel.org>
Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/rds/tcp.c

index 5de35d556f29dba83dc7a35e760bdb4cd5d35fea..b263634ac750d204a0d5726db6abe04023aa9c17 100644 (file)
@@ -355,23 +355,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
        /* If the scope_id is specified, check only those addresses
         * hosted on the specified interface.
         */
+       rcu_read_lock();
        if (scope_id != 0) {
-               rcu_read_lock();
                dev = dev_get_by_index_rcu(net, scope_id);
                /* scope_id is not valid... */
                if (!dev) {
                        rcu_read_unlock();
                        return -EADDRNOTAVAIL;
                }
-               rcu_read_unlock();
        }
 #if IS_ENABLED(CONFIG_IPV6)
        if (ipv6_mod_enabled()) {
                ret = ipv6_chk_addr(net, addr, dev, 0);
-               if (ret)
+               if (ret) {
+                       rcu_read_unlock();
                        return 0;
+               }
        }
 #endif
+       rcu_read_unlock();
        return -EADDRNOTAVAIL;
 }