CVE-2023-3439: Use After Free (CWE-416)
value: A flaw was found in the MCTP protocol in the Linux kernel. The function mctp_unregister() reclaims the device's relevant resource when a netcard detaches. However, a running routine may be unaware of this and cause the use-after-free of the mdev->addrs object, potentially leading to a denial of service.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 4.7
Published: 2023-06-28T21:15Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: net/mctp/device.c
Commit date: 2022-04-26 09:14:47 +0200
Commit message: mctp: defer the kfree of object mdev->addrs

The function mctp_unregister() reclaims the device's relevant resource
when a netcard detaches. However, a running routine may be unaware of
this and cause the use-after-free of the mdev->addrs object.

The race condition can be demonstrated below

 cleanup thread               another thread
                          |
unregister_netdev()       |  mctp_sendmsg()
...                       |    ...
  mctp_unregister()       |    rt = mctp_route_lookup()
    ...                   |    mctl_local_output()
    kfree(mdev->addrs)    |      ...
                          |      saddr = rt->dev->addrs[0];
                          |

An attacker can adopt the (recent provided) mtcpserial driver with pty
to fake the device detaching and use the userfaultfd to increase the
race success chance (in mctp_sendmsg). The KASan report for such a POC
is shown below:

[   86.051955] ==================================================================
[   86.051955] BUG: KASAN: use-after-free in mctp_local_output+0x4e9/0xb7d
[   86.051955] Read of size 1 at addr ffff888005f298c0 by task poc/295
[   86.051955]
[   86.051955] Call Trace:
[   86.051955]  <TASK>
[   86.051955]  dump_stack_lvl+0x33/0x42
[   86.051955]  print_report.cold.13+0xb2/0x6b3
[   86.051955]  ? preempt_schedule_irq+0x57/0x80
[   86.051955]  ? mctp_local_output+0x4e9/0xb7d
[   86.051955]  kasan_report+0xa5/0x120
[   86.051955]  ? mctp_local_output+0x4e9/0xb7d
[   86.051955]  mctp_local_output+0x4e9/0xb7d
[   86.051955]  ? mctp_dev_set_key+0x79/0x79
[   86.051955]  ? copyin+0x38/0x50
[   86.051955]  ? _copy_from_iter+0x1b6/0xf20
[   86.051955]  ? sysvec_apic_timer_interrupt+0x97/0xb0
[   86.051955]  ? asm_sysvec_apic_timer_interrupt+0x12/0x20
[   86.051955]  ? mctp_local_output+0x1/0xb7d
[   86.051955]  mctp_sendmsg+0x64d/0xdb0
[   86.051955]  ? mctp_sk_close+0x20/0x20
[   86.051955]  ? __fget_light+0x2fd/0x4f0
[   86.051955]  ? mctp_sk_close+0x20/0x20
[   86.051955]  sock_sendmsg+0xdd/0x110
[   86.051955]  __sys_sendto+0x1cc/0x2a0
[   86.051955]  ? __ia32_sys_getpeername+0xa0/0xa0
[   86.051955]  ? new_sync_write+0x335/0x550
[   86.051955]  ? alloc_file+0x22f/0x500
[   86.051955]  ? __ip_do_redirect+0x820/0x1820
[   86.051955]  ? vfs_write+0x44d/0x7b0
[   86.051955]  ? vfs_write+0x44d/0x7b0
[   86.051955]  ? fput_many+0x15/0x120
[   86.051955]  ? ksys_write+0x155/0x1b0
[   86.051955]  ? __ia32_sys_read+0xa0/0xa0
[   86.051955]  __x64_sys_sendto+0xd8/0x1b0
[   86.051955]  ? exit_to_user_mode_prepare+0x2f/0x120
[   86.051955]  ? syscall_exit_to_user_mode+0x12/0x20
[   86.051955]  do_syscall_64+0x3a/0x80
[   86.051955]  entry_SYSCALL_64_after_hwframe+0x44/0xae
[   86.051955] RIP: 0033:0x7f82118a56b3
[   86.051955] RSP: 002b:00007ffdb154b110 EFLAGS: 00000293 ORIG_RAX: 000000000000002c
[   86.051955] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f82118a56b3
[   86.051955] RDX: 0000000000000010 RSI: 00007f8211cd4000 RDI: 0000000000000007
[   86.051955] RBP: 00007ffdb154c1d0 R08: 00007ffdb154b164 R09: 000000000000000c
[   86.051955] R10: 0000000000000000 R11: 0000000000000293 R12: 000055d779800db0
[   86.051955] R13: 00007ffdb154c2b0 R14: 0000000000000000 R15: 0000000000000000
[   86.051955]  </TASK>
[   86.051955]
[   86.051955] Allocated by task 295:
[   86.051955]  kasan_save_stack+0x1c/0x40
[   86.051955]  __kasan_kmalloc+0x84/0xa0
[   86.051955]  mctp_rtm_newaddr+0x242/0x610
[   86.051955]  rtnetlink_rcv_msg+0x2fd/0x8b0
[   86.051955]  netlink_rcv_skb+0x11c/0x340
[   86.051955]  netlink_unicast+0x439/0x630
[   86.051955]  netlink_sendmsg+0x752/0xc00
[   86.051955]  sock_sendmsg+0xdd/0x110
[   86.051955]  __sys_sendto+0x1cc/0x2a0
[   86.051955]  __x64_sys_sendto+0xd8/0x1b0
[   86.051955]  do_syscall_64+0x3a/0x80
[   86.051955]  entry_SYSCALL_64_after_hwframe+0x44/0xae
[   86.051955]
[   86.051955] Freed by task 301:
[   86.051955]  kasan_save_stack+0x1c/0x40
[   86.051955]  kasan_set_track+0x21/0x30
[   86.051955]  kasan_set_free_info+0x20/0x30
[   86.051955]  __kasan_slab_free+0x104/0x170
[   86.051955]  kfree+0x8c/0x290
[   86.051955]  mctp_dev_notify+0x161/0x2c0
[   86.051955]  raw_notifier_call_chain+0x8b/0xc0
[   86.051955]  unregister_netdevice_many+0x299/0x1180
[   86.051955]  unregister_netdevice_queue+0x210/0x2f0
[   86.051955]  unregister_netdev+0x13/0x20
[   86.051955]  mctp_serial_close+0x6d/0xa0
[   86.051955]  tty_ldisc_kill+0x31/0xa0
[   86.051955]  tty_ldisc_hangup+0x24f/0x560
[   86.051955]  __tty_hangup.part.28+0x2ce/0x6b0
[   86.051955]  tty_release+0x327/0xc70
[   86.051955]  __fput+0x1df/0x8b0
[   86.051955]  task_work_run+0xca/0x150
[   86.051955]  exit_to_user_mode_prepare+0x114/0x120
[   86.051955]  syscall_exit_to_user_mode+0x12/0x20
[   86.051955]  do_syscall_64+0x46/0x80
[   86.051955]  entry_SYSCALL_64_after_hwframe+0x44/0xae
[   86.051955]
[   86.051955] The buggy address belongs to the object at ffff888005f298c0
[   86.051955]  which belongs to the cache kmalloc-8 of size 8
[   86.051955] The buggy address is located 0 bytes inside of
[   86.051955]  8-byte region [ffff888005f298c0, ffff888005f298c8)
[   86.051955]
[   86.051955] The buggy address belongs to the physical page:
[   86.051955] flags: 0x100000000000200(slab|node=0|zone=1)
[   86.051955] raw: 0100000000000200 dead000000000100 dead000000000122 ffff888005c42280
[   86.051955] raw: 0000000000000000 0000000080660066 00000001ffffffff 0000000000000000
[   86.051955] page dumped because: kasan: bad access detected
[   86.051955]
[   86.051955] Memory state around the buggy address:
[   86.051955]  ffff888005f29780: 00 fc fc fc fc 00 fc fc fc fc 00 fc fc fc fc 00
[   86.051955]  ffff888005f29800: fc fc fc fc 00 fc fc fc fc 00 fc fc fc fc 00 fc
[   86.051955] >ffff888005f29880: fc fc fc fb fc fc fc fc fa fc fc fc fc fa fc fc
[   86.051955]                                            ^
[   86.051955]  ffff888005f29900: fc fc 00 fc fc fc fc 00 fc fc fc fc 00 fc fc fc
[   86.051955]  ffff888005f29980: fc 00 fc fc fc fc 00 fc fc fc fc 00 fc fc fc fc
[   86.051955] ==================================================================

To this end, just like the commit e04480920d1e ("Bluetooth: defer
cleanup of resources in hci_unregister_dev()")  this patch defers the
destructive kfree(mdev->addrs) in mctp_unregister to the mctp_dev_put,
where the refcount of mdev is zero and the entire device is reclaimed.
This prevents the use-after-free because the sendmsg thread holds the
reference of mdev in the mctp_route object.

Fixes: 583be982d934 (mctp: Add device handling and netlink interface)
Signed-off-by: Lin Ma <linma@zju.edu.cn>
Acked-by: Jeremy Kerr <jk@codeconstruct.com.au>
Link: https://lore.kernel.org/r/20220422114340.32346-1-linma@zju.edu.cn
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

Diff: diff --git a/net/mctp/device.c b/net/mctp/device.c
index f49be882e98e..99a3bda8852f 100644
--- a/net/mctp/device.c
+++ b/net/mctp/device.c
@@ -311,10 +311,11 @@ void mctp_dev_hold(struct mctp_dev *mdev)
 }
 
 void mctp_dev_put(struct mctp_dev *mdev)
 {
 	if (mdev && refcount_dec_and_test(&mdev->refs)) {
+		kfree(mdev->addrs);
 		dev_put(mdev->dev);
 		kfree_rcu(mdev, rcu);
 	}
 }
 
@@ -439,11 +440,10 @@ static void mctp_unregister(struct net_device *dev)
 
 	RCU_INIT_POINTER(mdev->dev->mctp_ptr, NULL);
 
 	mctp_route_remove_dev(mdev);
 	mctp_neigh_remove_dev(mdev);
-	kfree(mdev->addrs);
 
 	mctp_dev_put(mdev);
 }
 
 static int mctp_register(struct net_device *dev)

Vulnerable code: 	kfree(mdev->addrs);
Fixed code: 		kfree(mdev->addrs);