Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ax25: rcu protect dev->ax25_ptr

syzbot found a lockdep issue [1].

We should remove ax25 RTNL dependency in ax25_setsockopt()

This should also fix a variety of possible UAF in ax25.

[1]

WARNING: possible circular locking dependency detected
6.13.0-rc3-syzkaller-00762-g9268abe611b0 #0 Not tainted
------------------------------------------------------
syz.5.1818/12806 is trying to acquire lock:
ffffffff8fcb3988 (rtnl_mutex){+.+.}-{4:4}, at: ax25_setsockopt+0xa55/0xe90 net/ax25/af_ax25.c:680

but task is already holding lock:
ffff8880617ac258 (sk_lock-AF_AX25){+.+.}-{0:0}, at: lock_sock include/net/sock.h:1618 [inline]
ffff8880617ac258 (sk_lock-AF_AX25){+.+.}-{0:0}, at: ax25_setsockopt+0x209/0xe90 net/ax25/af_ax25.c:574

which lock already depends on the new lock.

the existing dependency chain (in reverse order) is:

-> #1 (sk_lock-AF_AX25){+.+.}-{0:0}:
lock_acquire+0x1ed/0x550 kernel/locking/lockdep.c:5849
lock_sock_nested+0x48/0x100 net/core/sock.c:3642
lock_sock include/net/sock.h:1618 [inline]
ax25_kill_by_device net/ax25/af_ax25.c:101 [inline]
ax25_device_event+0x24d/0x580 net/ax25/af_ax25.c:146
notifier_call_chain+0x1a5/0x3f0 kernel/notifier.c:85
__dev_notify_flags+0x207/0x400
dev_change_flags+0xf0/0x1a0 net/core/dev.c:9026
dev_ifsioc+0x7c8/0xe70 net/core/dev_ioctl.c:563
dev_ioctl+0x719/0x1340 net/core/dev_ioctl.c:820
sock_do_ioctl+0x240/0x460 net/socket.c:1234
sock_ioctl+0x626/0x8e0 net/socket.c:1339
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:906 [inline]
__se_sys_ioctl+0xf5/0x170 fs/ioctl.c:892
do_syscall_x64 arch/x86/entry/common.c:52 [inline]
do_syscall_64+0xf3/0x230 arch/x86/entry/common.c:83
entry_SYSCALL_64_after_hwframe+0x77/0x7f

-> #0 (rtnl_mutex){+.+.}-{4:4}:
check_prev_add kernel/locking/lockdep.c:3161 [inline]
check_prevs_add kernel/locking/lockdep.c:3280 [inline]
validate_chain+0x18ef/0x5920 kernel/locking/lockdep.c:3904
__lock_acquire+0x1397/0x2100 kernel/locking/lockdep.c:5226
lock_acquire+0x1ed/0x550 kernel/locking/lockdep.c:5849
__mutex_lock_common kernel/locking/mutex.c:585 [inline]
__mutex_lock+0x1ac/0xee0 kernel/locking/mutex.c:735
ax25_setsockopt+0xa55/0xe90 net/ax25/af_ax25.c:680
do_sock_setsockopt+0x3af/0x720 net/socket.c:2324
__sys_setsockopt net/socket.c:2349 [inline]
__do_sys_setsockopt net/socket.c:2355 [inline]
__se_sys_setsockopt net/socket.c:2352 [inline]
__x64_sys_setsockopt+0x1ee/0x280 net/socket.c:2352
do_syscall_x64 arch/x86/entry/common.c:52 [inline]
do_syscall_64+0xf3/0x230 arch/x86/entry/common.c:83
entry_SYSCALL_64_after_hwframe+0x77/0x7f

other info that might help us debug this:

Possible unsafe locking scenario:

CPU0 CPU1
---- ----
lock(sk_lock-AF_AX25);
lock(rtnl_mutex);
lock(sk_lock-AF_AX25);
lock(rtnl_mutex);

*** DEADLOCK ***

1 lock held by syz.5.1818/12806:
#0: ffff8880617ac258 (sk_lock-AF_AX25){+.+.}-{0:0}, at: lock_sock include/net/sock.h:1618 [inline]
#0: ffff8880617ac258 (sk_lock-AF_AX25){+.+.}-{0:0}, at: ax25_setsockopt+0x209/0xe90 net/ax25/af_ax25.c:574

stack backtrace:
CPU: 1 UID: 0 PID: 12806 Comm: syz.5.1818 Not tainted 6.13.0-rc3-syzkaller-00762-g9268abe611b0 #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 09/13/2024
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x241/0x360 lib/dump_stack.c:120
print_circular_bug+0x13a/0x1b0 kernel/locking/lockdep.c:2074
check_noncircular+0x36a/0x4a0 kernel/locking/lockdep.c:2206
check_prev_add kernel/locking/lockdep.c:3161 [inline]
check_prevs_add kernel/locking/lockdep.c:3280 [inline]
validate_chain+0x18ef/0x5920 kernel/locking/lockdep.c:3904
__lock_acquire+0x1397/0x2100 kernel/locking/lockdep.c:5226
lock_acquire+0x1ed/0x550 kernel/locking/lockdep.c:5849
__mutex_lock_common kernel/locking/mutex.c:585 [inline]
__mutex_lock+0x1ac/0xee0 kernel/locking/mutex.c:735
ax25_setsockopt+0xa55/0xe90 net/ax25/af_ax25.c:680
do_sock_setsockopt+0x3af/0x720 net/socket.c:2324
__sys_setsockopt net/socket.c:2349 [inline]
__do_sys_setsockopt net/socket.c:2355 [inline]
__se_sys_setsockopt net/socket.c:2352 [inline]
__x64_sys_setsockopt+0x1ee/0x280 net/socket.c:2352
do_syscall_x64 arch/x86/entry/common.c:52 [inline]
do_syscall_64+0xf3/0x230 arch/x86/entry/common.c:83
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f7b62385d29

Fixes: c433570458e4 ("ax25: fix a use-after-free in ax25_fillin_cb()")
Reported-by: syzbot <syzkaller@googlegroups.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Link: https://patch.msgid.link/20250103210514.87290-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Eric Dumazet and committed by
Jakub Kicinski
95fc45d1 3f9f5cd0

+35 -20
+1 -1
include/linux/netdevice.h
··· 2261 2261 void *atalk_ptr; 2262 2262 #endif 2263 2263 #if IS_ENABLED(CONFIG_AX25) 2264 - void *ax25_ptr; 2264 + struct ax25_dev __rcu *ax25_ptr; 2265 2265 #endif 2266 2266 #if IS_ENABLED(CONFIG_CFG80211) 2267 2267 struct wireless_dev *ieee80211_ptr;
+5 -5
include/net/ax25.h
··· 231 231 #endif 232 232 refcount_t refcount; 233 233 bool device_up; 234 + struct rcu_head rcu; 234 235 } ax25_dev; 235 236 236 237 typedef struct ax25_cb { ··· 291 290 292 291 static inline void ax25_dev_put(ax25_dev *ax25_dev) 293 292 { 294 - if (refcount_dec_and_test(&ax25_dev->refcount)) { 295 - kfree(ax25_dev); 296 - } 293 + if (refcount_dec_and_test(&ax25_dev->refcount)) 294 + kfree_rcu(ax25_dev, rcu); 297 295 } 298 296 static inline __be16 ax25_type_trans(struct sk_buff *skb, struct net_device *dev) 299 297 { ··· 335 335 extern spinlock_t ax25_dev_lock; 336 336 337 337 #if IS_ENABLED(CONFIG_AX25) 338 - static inline ax25_dev *ax25_dev_ax25dev(struct net_device *dev) 338 + static inline ax25_dev *ax25_dev_ax25dev(const struct net_device *dev) 339 339 { 340 - return dev->ax25_ptr; 340 + return rcu_dereference_rtnl(dev->ax25_ptr); 341 341 } 342 342 #endif 343 343
+6 -6
net/ax25/af_ax25.c
··· 467 467 goto out_put; 468 468 } 469 469 470 - static void ax25_fillin_cb_from_dev(ax25_cb *ax25, ax25_dev *ax25_dev) 470 + static void ax25_fillin_cb_from_dev(ax25_cb *ax25, const ax25_dev *ax25_dev) 471 471 { 472 472 ax25->rtt = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T1]) / 2; 473 473 ax25->t1 = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T1]); ··· 677 677 break; 678 678 } 679 679 680 - rtnl_lock(); 681 - dev = __dev_get_by_name(&init_net, devname); 680 + rcu_read_lock(); 681 + dev = dev_get_by_name_rcu(&init_net, devname); 682 682 if (!dev) { 683 - rtnl_unlock(); 683 + rcu_read_unlock(); 684 684 res = -ENODEV; 685 685 break; 686 686 } 687 687 688 688 ax25->ax25_dev = ax25_dev_ax25dev(dev); 689 689 if (!ax25->ax25_dev) { 690 - rtnl_unlock(); 690 + rcu_read_unlock(); 691 691 res = -ENODEV; 692 692 break; 693 693 } 694 694 ax25_fillin_cb(ax25, ax25->ax25_dev); 695 - rtnl_unlock(); 695 + rcu_read_unlock(); 696 696 break; 697 697 698 698 default:
+2 -2
net/ax25/ax25_dev.c
··· 90 90 91 91 spin_lock_bh(&ax25_dev_lock); 92 92 list_add(&ax25_dev->list, &ax25_dev_list); 93 - dev->ax25_ptr = ax25_dev; 93 + rcu_assign_pointer(dev->ax25_ptr, ax25_dev); 94 94 spin_unlock_bh(&ax25_dev_lock); 95 95 96 96 ax25_register_dev_sysctl(ax25_dev); ··· 125 125 } 126 126 } 127 127 128 - dev->ax25_ptr = NULL; 128 + RCU_INIT_POINTER(dev->ax25_ptr, NULL); 129 129 spin_unlock_bh(&ax25_dev_lock); 130 130 netdev_put(dev, &ax25_dev->dev_tracker); 131 131 ax25_dev_put(ax25_dev);
+2 -1
net/ax25/ax25_ip.c
··· 122 122 if (dev == NULL) 123 123 dev = skb->dev; 124 124 125 + rcu_read_lock(); 125 126 if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) { 126 127 kfree_skb(skb); 127 128 goto put; ··· 203 202 ax25_queue_xmit(skb, dev); 204 203 205 204 put: 206 - 205 + rcu_read_unlock(); 207 206 ax25_route_lock_unuse(); 208 207 return NETDEV_TX_OK; 209 208 }
+17 -5
net/ax25/ax25_out.c
··· 39 39 * specified. 40 40 */ 41 41 if (paclen == 0) { 42 - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) 42 + rcu_read_lock(); 43 + ax25_dev = ax25_dev_ax25dev(dev); 44 + if (!ax25_dev) { 45 + rcu_read_unlock(); 43 46 return NULL; 44 - 47 + } 45 48 paclen = ax25_dev->values[AX25_VALUES_PACLEN]; 49 + rcu_read_unlock(); 46 50 } 47 51 48 52 /* ··· 57 53 return ax25; /* It already existed */ 58 54 } 59 55 60 - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) 56 + rcu_read_lock(); 57 + ax25_dev = ax25_dev_ax25dev(dev); 58 + if (!ax25_dev) { 59 + rcu_read_unlock(); 61 60 return NULL; 61 + } 62 62 63 - if ((ax25 = ax25_create_cb()) == NULL) 63 + if ((ax25 = ax25_create_cb()) == NULL) { 64 + rcu_read_unlock(); 64 65 return NULL; 65 - 66 + } 66 67 ax25_fillin_cb(ax25, ax25_dev); 68 + rcu_read_unlock(); 67 69 68 70 ax25->source_addr = *src; 69 71 ax25->dest_addr = *dest; ··· 368 358 { 369 359 unsigned char *ptr; 370 360 361 + rcu_read_lock(); 371 362 skb->protocol = ax25_type_trans(skb, ax25_fwd_dev(dev)); 363 + rcu_read_unlock(); 372 364 373 365 ptr = skb_push(skb, 1); 374 366 *ptr = 0x00; /* KISS */
+2
net/ax25/ax25_route.c
··· 406 406 ax25_route_lock_unuse(); 407 407 return -EHOSTUNREACH; 408 408 } 409 + rcu_read_lock(); 409 410 if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) { 410 411 err = -EHOSTUNREACH; 411 412 goto put; ··· 443 442 } 444 443 445 444 put: 445 + rcu_read_unlock(); 446 446 ax25_route_lock_unuse(); 447 447 return err; 448 448 }