[PATCH 0/1] block/throttle-groups: fix a NULL token crash on group creation

Denis V. Lunev posted 1 patch 2 weeks, 4 days ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260908145554.3215221-1-den@openvz.org
Maintainers: Kevin Wolf <kwolf@redhat.com>, Hanna Reitz <hreitz@redhat.com>, Alberto Garcia <berto@igalia.com>
block/block-backend.c   | 12 ++++++------
block/throttle-groups.c |  4 +++-
2 files changed, 9 insertions(+), 7 deletions(-)
[PATCH 0/1] block/throttle-groups: fix a NULL token crash on group creation
Posted by Denis V. Lunev 2 weeks, 4 days ago
Applying I/O limits to a disk that is already serving requests from an
iothread can kill QEMU:

  #0  throttle_group_next_tgm (tgm=0x0) at block/throttle-groups.c:185
  #1  next_throttle_token (tgm=..., direction=THROTTLE_READ)
  #2  throttle_group_co_io_limits_intercept (bytes=8192, ...)
  #3  blk_co_do_preadv_part (blk=..., bytes=8192, ...)
  #4  blk_aio_read_entry (opaque=...)
  #5  coroutine_trampoline (i0=..., i1=...)

throttle_group_register_tgm() publishes tgm->throttle_state before it
takes tg->lock, and the group it points at is only filled in afterwards.
A request that samples the pointer in between and wins tg->lock finds
tg->tokens[] still NULL. The ordering is as old as the throttle group
code, 76f4afb40f ("throttle: Add throttle group support").

This was found from a production core. The group in it was brand new -
name matching the disk, refcount 1, empty member list, both tokens NULL,
a zeroed config because throttle_group_config() had not run yet, and
tg->lock owned by the crashing iothread while the main thread sat
blocked on the same lock inside throttle_group_register_tgm(). The
trigger was libvirt applying blkdeviotune right after attaching a
volume, while the guest was probing the new disk.

The window is a couple of instructions wide. To see the bug on demand,
widen it by hand:

    --- a/block/throttle-groups.c
    +++ b/block/throttle-groups.c
    @@ -584,6 +584,7 @@ void throttle_group_register_tgm(...)
         tgm->throttle_state = ts;
         tgm->aio_context = ctx;
         qatomic_set(&tgm->restart_pending, 0);

    +    g_usleep(1000);
         QEMU_LOCK_GUARD(&tg->lock);

With that in place, any I/O against the disk during a
block_set_io_throttle that creates the group crashes at once, and the
patch below makes it stop.

Cc: Kevin Wolf <kwolf@redhat.com>
Cc: Hanna Reitz <hreitz@redhat.com>
Cc: Alberto Garcia <berto@igalia.com>
Signed-off-by: Denis V. Lunev <den@openvz.org>

Denis V. Lunev (1):
  block/throttle-groups: fix crash when enabling I/O limits on a busy
    disk

 block/block-backend.c   | 12 ++++++------
 block/throttle-groups.c |  4 +++-
 2 files changed, 9 insertions(+), 7 deletions(-)

-- 
2.53.0
Re: [PATCH 0/1] block/throttle-groups: fix a NULL token crash on group creation
Posted by Kevin Wolf 4 days, 4 hours ago
Am 08.09.2026 um 16:55 hat Denis V. Lunev geschrieben:
> Applying I/O limits to a disk that is already serving requests from an
> iothread can kill QEMU:
> 
>   #0  throttle_group_next_tgm (tgm=0x0) at block/throttle-groups.c:185
>   #1  next_throttle_token (tgm=..., direction=THROTTLE_READ)
>   #2  throttle_group_co_io_limits_intercept (bytes=8192, ...)
>   #3  blk_co_do_preadv_part (blk=..., bytes=8192, ...)
>   #4  blk_aio_read_entry (opaque=...)
>   #5  coroutine_trampoline (i0=..., i1=...)
> 
> throttle_group_register_tgm() publishes tgm->throttle_state before it
> takes tg->lock, and the group it points at is only filled in afterwards.
> A request that samples the pointer in between and wins tg->lock finds
> tg->tokens[] still NULL. The ordering is as old as the throttle group
> code, 76f4afb40f ("throttle: Add throttle group support").
> 
> This was found from a production core. The group in it was brand new -
> name matching the disk, refcount 1, empty member list, both tokens NULL,
> a zeroed config because throttle_group_config() had not run yet, and
> tg->lock owned by the crashing iothread while the main thread sat
> blocked on the same lock inside throttle_group_register_tgm(). The
> trigger was libvirt applying blkdeviotune right after attaching a
> volume, while the guest was probing the new disk.
> 
> The window is a couple of instructions wide. To see the bug on demand,
> widen it by hand:
> 
>     --- a/block/throttle-groups.c
>     +++ b/block/throttle-groups.c
>     @@ -584,6 +584,7 @@ void throttle_group_register_tgm(...)
>          tgm->throttle_state = ts;
>          tgm->aio_context = ctx;
>          qatomic_set(&tgm->restart_pending, 0);
> 
>     +    g_usleep(1000);
>          QEMU_LOCK_GUARD(&tg->lock);
> 
> With that in place, any I/O against the disk during a
> block_set_io_throttle that creates the group crashes at once, and the
> patch below makes it stop.
> 
> Cc: Kevin Wolf <kwolf@redhat.com>
> Cc: Hanna Reitz <hreitz@redhat.com>
> Cc: Alberto Garcia <berto@igalia.com>
> Signed-off-by: Denis V. Lunev <den@openvz.org>

Thanks, applied to the block branch.

Kevin
Re: [PATCH 0/1] block/throttle-groups: fix a NULL token crash on group creation
Posted by Denis V. Lunev 4 days, 23 hours ago
On 9/8/26 16:55, Denis V. Lunev wrote:
> This email originated from an IP that might not be authorized by the domain it was sent from.
> Do not click links or open attachments unless it is an email you expected to receive.
> Applying I/O limits to a disk that is already serving requests from an
> iothread can kill QEMU:
>
>   #0  throttle_group_next_tgm (tgm=0x0) at block/throttle-groups.c:185
>   #1  next_throttle_token (tgm=..., direction=THROTTLE_READ)
>   #2  throttle_group_co_io_limits_intercept (bytes=8192, ...)
>   #3  blk_co_do_preadv_part (blk=..., bytes=8192, ...)
>   #4  blk_aio_read_entry (opaque=...)
>   #5  coroutine_trampoline (i0=..., i1=...)
>
> throttle_group_register_tgm() publishes tgm->throttle_state before it
> takes tg->lock, and the group it points at is only filled in afterwards.
> A request that samples the pointer in between and wins tg->lock finds
> tg->tokens[] still NULL. The ordering is as old as the throttle group
> code, 76f4afb40f ("throttle: Add throttle group support").
>
> This was found from a production core. The group in it was brand new -
> name matching the disk, refcount 1, empty member list, both tokens NULL,
> a zeroed config because throttle_group_config() had not run yet, and
> tg->lock owned by the crashing iothread while the main thread sat
> blocked on the same lock inside throttle_group_register_tgm(). The
> trigger was libvirt applying blkdeviotune right after attaching a
> volume, while the guest was probing the new disk.
>
> The window is a couple of instructions wide. To see the bug on demand,
> widen it by hand:
>
>     --- a/block/throttle-groups.c
>     +++ b/block/throttle-groups.c
>     @@ -584,6 +584,7 @@ void throttle_group_register_tgm(...)
>          tgm->throttle_state = ts;
>          tgm->aio_context = ctx;
>          qatomic_set(&tgm->restart_pending, 0);
>
>     +    g_usleep(1000);
>          QEMU_LOCK_GUARD(&tg->lock);
>
> With that in place, any I/O against the disk during a
> block_set_io_throttle that creates the group crashes at once, and the
> patch below makes it stop.
>
> Cc: Kevin Wolf <kwolf@redhat.com>
> Cc: Hanna Reitz <hreitz@redhat.com>
> Cc: Alberto Garcia <berto@igalia.com>
> Signed-off-by: Denis V. Lunev <den@openvz.org>
>
> Denis V. Lunev (1):
>   block/throttle-groups: fix crash when enabling I/O limits on a busy
>     disk
>
>  block/block-backend.c   | 12 ++++++------
>  block/throttle-groups.c |  4 +++-
>  2 files changed, 9 insertions(+), 7 deletions(-)
>
ping