linux/drivers/block
Zheng Qixing 9517b82d8d nbd: defer config put in recv_work
There is one uaf issue in recv_work when running NBD_CLEAR_SOCK and
NBD_CMD_RECONFIGURE:
  nbd_genl_connect     // conf_ref=2 (connect and recv_work A)
  nbd_open	       // conf_ref=3
  recv_work A done     // conf_ref=2
  NBD_CLEAR_SOCK       // conf_ref=1
  nbd_genl_reconfigure // conf_ref=2 (trigger recv_work B)
  close nbd	       // conf_ref=1
  recv_work B
    config_put         // conf_ref=0
    atomic_dec(&config->recv_threads); -> UAF

Or only running NBD_CLEAR_SOCK:
  nbd_genl_connect   // conf_ref=2
  nbd_open 	     // conf_ref=3
  NBD_CLEAR_SOCK     // conf_ref=2
  close nbd
    nbd_release
      config_put     // conf_ref=1
  recv_work
    config_put 	     // conf_ref=0
    atomic_dec(&config->recv_threads); -> UAF

Commit 87aac3a80a ("nbd: call nbd_config_put() before notifying the
waiter") moved nbd_config_put() to run before waking up the waiter in
recv_work, in order to ensure that nbd_start_device_ioctl() would not
be woken up while nbd->task_recv was still uncleared.

However, in nbd_start_device_ioctl(), after being woken up it explicitly
calls flush_workqueue() to make sure all current works are finished.
Therefore, there is no need to move the config put ahead of the wakeup.

Move nbd_config_put() to the end of recv_work, so that the reference is
held for the whole lifetime of the worker thread. This makes sure the
config cannot be freed while recv_work is still running, even if clear
+ reconfigure interleave.

In addition, we don't need to worry about recv_work dropping the last
nbd_put (which causes deadlock):

path A (netlink with NBD_CFLAG_DESTROY_ON_DISCONNECT):
  connect  // nbd_refs=1 (trigger recv_work)
  open nbd // nbd_refs=2
  NBD_CLEAR_SOCK
  close nbd
    nbd_release
      nbd_disconnect_and_put
        flush_workqueue // recv_work done
      nbd_config_put
        nbd_put // nbd_refs=1
      nbd_put // nbd_refs=0
        queue_work

path B (netlink without NBD_CFLAG_DESTROY_ON_DISCONNECT):
  connect  // nbd_refs=2 (trigger recv_work)
  open nbd // nbd_refs=3
  NBD_CLEAR_SOCK // conf_refs=2
  close nbd
    nbd_release
      nbd_config_put // conf_refs=1
      nbd_put // nbd_refs=2
  recv_work done // conf_refs=0, nbd_refs=1
  rmmod // nbd_refs=0

Reported-by: syzbot+56fbf4c7ddf65e95c7cc@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/all/6907edce.a70a0220.37351b.0014.GAE@google.com/T/
Fixes: 87aac3a80a ("nbd: make the config put is called before the notifying the waiter")
Depends-on: e2daec488c ("nbd: Fix hungtask when nbd_config_put")
Signed-off-by: Zheng Qixing <zhengqixing@huawei.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
2025-11-08 06:37:54 -07:00
..
aoe Summary of significant series in this pull request: 2025-10-02 18:18:33 -07:00
drbd drbd: replace kmap() with kmap_local_page() in receiver path 2025-11-03 08:15:54 -07:00
mtip32xx block: switch ->getgeo() to struct gendisk 2025-08-13 02:59:29 -04:00
null_blk null_blk: allow byte aligned memory offsets 2025-11-06 16:28:55 -07:00
rnbd drivers/block: WQ_PERCPU added to alloc_workqueue users 2025-09-09 09:11:31 -06:00
rnull rust: block: update ARef and AlwaysRefCounted imports from sync::aref 2025-11-05 18:24:10 -07:00
xen-blkback xen/blkback: convert timeouts to secs_to_jiffies() 2025-01-12 20:21:03 -08:00
zram Summary of significant series in this pull request: 2025-10-02 18:18:33 -07:00
amiflop.c block: switch ->getgeo() to struct gendisk 2025-08-13 02:59:29 -04:00
ataflop.c treewide: Switch/rename to timer_delete[_sync]() 2025-04-05 10:30:12 +02:00
brd.c brd: use page reference to protect page lifetime 2025-09-01 08:37:29 -06:00
floppy.c block: floppy: Replace kmalloc() + copy_from_user() with memdup_user() 2025-09-09 09:10:58 -06:00
Kconfig rnull: move driver to separate directory 2025-09-02 05:23:56 -06:00
loop.c loop: remove redundant __GFP_NOWARN flag 2025-10-08 06:27:53 -06:00
Makefile rnull: move driver to separate directory 2025-09-02 05:23:56 -06:00
n64cart.c block: move the nonrot flag to queue_limits 2024-06-19 07:58:28 -06:00
nbd.c nbd: defer config put in recv_work 2025-11-08 06:37:54 -07:00
ps3disk.c ps3disk: Do not use dev->bounce_size before it is set 2025-01-03 11:44:25 -07:00
ps3vram.c block: pass a queue_limits argument to blk_alloc_disk 2024-02-19 16:58:23 -07:00
rbd_types.h
rbd.c drivers/block: WQ_PERCPU added to alloc_workqueue users 2025-09-09 09:11:31 -06:00
sunvdc.c drivers/block: WQ_PERCPU added to alloc_workqueue users 2025-09-09 09:11:31 -06:00
swim_asm.S
swim.c block: switch ->getgeo() to struct gendisk 2025-08-13 02:59:29 -04:00
swim3.c treewide, timers: Rename from_timer() to timer_container_of() 2025-06-08 09:07:37 +02:00
ublk_drv.c ublk: use rq_for_each_segment() for user copy 2025-11-06 16:26:04 -07:00
virtio_blk.c virtio_blk: NULL out vqs to avoid double free on failed resume 2025-11-06 16:32:58 -07:00
xen-blkfront.c block: switch ->getgeo() to struct gendisk 2025-08-13 02:59:29 -04:00
z2ram.c block: remove BLK_MQ_F_SHOULD_MERGE 2024-12-23 08:17:23 -07:00
zloop.c block: introduce disk_report_zone() 2025-11-05 08:07:21 -07:00