On 9/14/2026 10:02 PM, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) 
> to consider:
> 
> New issues:
> - [High] DMA cacheline sharing causes memory corruption on non-coherent 
> architectures.
> - [High] Erroneous teardown ordering causes valid in-flight requests to fail 
> with I/O errors during suspend.
> - [High] Unbounded wait in `virtblk_ctrl_vq_request` deadlocks device 
> teardown if the host is unresponsive.
> 
> Pre-existing issues:
> - [High] Use-after-free and double-free of `vblk` in the `virtblk_probe` 
> error path.
> --
> 
> commit 36271d203fcaee50a88fd7b53aa73dbbba25cf7c
> Author: linlzhan <[email protected]>
> 
> virtio_blk: Add control virtqueue support
> 
> This commit adds support for the optional virtio-blk control virtqueue.
> It allows the driver to manage control-queue requests independently from
> the data path, preparing for subsequent inline encryption implementation.
> 
>> diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
>> --- a/drivers/block/virtio_blk.c
>> +++ b/drivers/block/virtio_blk.c
> [ ... ]
>> @@ -110,6 +123,12 @@ struct virtblk_req {
>>      struct scatterlist sg[];
>>  };
>>  
>> +struct virtblk_ctrl_request {
>> +    __virtio32 type;
>> +    u8 status;
>> +    struct completion compl;
>> +};
> 
> [Severity: High]
> Is it safe to place DMA targets (type, status) and CPU synchronization
> primitives (struct completion compl) in the same struct without cacheline
> alignment?
> 
> On non-coherent architectures, could the DMA API unmap operation invalidate
> the cacheline and destroy the CPU's unwritten modifications to compl.wait,
> thereby corrupting the waitqueue? Buffers that are targets of DMA typically
> need to be cacheline-aligned or physically separated from CPU-modified
> lock/sync primitives.
> 
> [ ... ]

ACK

Update  struct completion compl to a pointer of struct completion compl.

>> +static int virtblk_ctrl_vq_request(struct virtio_blk *vblk,
>> +                                struct virtblk_ctrl_request *creq,
>> +                                struct scatterlist *sgs[],
>> +                                unsigned int out_sgs, unsigned int in_sgs)
>> +{
> [ ... ]
>> +    wait_for_completion(&creq->compl);
>> +    mutex_unlock(&vblk->ctrl_vq.mutex);
>> +    return 0;
>> +}
> 
> [Severity: High]
> Could this unbounded wait deadlock device teardown if the host is
> unresponsive?
> 
> When called from a block I/O context, this thread holds a reference to
> q_usage_counter. If the host hangs, del_gendisk() in virtblk_remove() will
> freeze the block queue and wait indefinitely for q_usage_counter to reach
> zero.
> 
> Since virtblk_ctrl_vq_drain() is called after del_gendisk(), wouldn't the
> system deadlock before the drain can recover the stuck requests?
> 
> [ ... ]

ACK

Add abandon flag in struct virtblk_ctrl_request. Set a 10 seconds timeout when
sending a request via the control queue. Set the control request as abandoned
once it doesn't return in 10 seconds to force finishing the caller in the 
control
queue, so that q_usage_counter in the data I/O thread can decrease to 0, unblock
del_gendisk() finally.

>> @@ -1553,6 +1713,7 @@ static int virtblk_probe(struct virtio_device *vdev)
>>  out_free_vq:
>>      vdev->config->del_vqs(vdev);
>>      kfree(vblk->vqs);
>> +    vblk->ctrl_vq.vq = NULL;
>>  out_free_vblk:
>>      kfree(vblk);
>>  out_free_index:
> 
> [Severity: High]
> This is a pre-existing issue, but does the error path in virtblk_probe()
> trigger a use-after-free and double-free?
> 
> If device_add_disk() fails, the path jumps to out_cleanup_disk which calls
> put_disk(vblk->disk). Because vblk->disk->fops points to virtblk_fops,
> dropping the last reference invokes virtblk_free_disk(), which frees vblk
> and vblk->index.
> 
> By the time execution reaches out_free_vq, aren't accesses to vblk->vqs and
> this new assignment to vblk->ctrl_vq.vq writing to freed memory, followed
> by a double free of vblk in out_free_vblk?
> 
> [ ... ]

It doesn't lead to use-after-free or double-free.

Because
  1. put_disk() calls disk_release();
  2. disk_release()calls disk->fops->free_disk() only if GD_ADDED is set.
  3. GD_ADDED is only set in add_disk_final() after the successful call of
     device_add_disk().

GD_ADDED won't be set if device_add_disk() returns the failure, then
virtblk_free_disk() won't be called. vblk->ctrl_vq.vq is set to NULL before
free vblk.

>> @@ -1593,6 +1759,8 @@ static int virtblk_freeze_priv(struct virtio_device 
>> *vdev)
>>      struct request_queue *q = vblk->disk->queue;
>>      unsigned int memflags;
>>  
>> +    virtblk_ctrl_vq_quiesce(vblk);
>> +
>>      /* Ensure no requests in virtqueues before deleting vqs. */
>>      memflags = blk_mq_freeze_queue(q);
> 
> [Severity: High]
> By calling virtblk_ctrl_vq_quiesce() before blk_mq_freeze_queue(), could
> valid in-flight requests fail with I/O errors?
> 
> virtblk_ctrl_vq_quiesce() sets ctrl_vq.dead = true. Since the block queue
> is not yet frozen, in-flight block I/O requests that haven't yet submitted
> their control requests (but have already incremented q_usage_counter) will
> encounter dead == true when calling virtblk_ctrl_vq_request(), resulting in
> -ENODEV.
> 
> Should the data plane be frozen before the control queue is marked dead?
> 

ACK

Move virtblk_ctrl_vq_quiesce(vblk) after blk_mq_freeze_queue(q), before
virtio_reset_device().



Reply via email to