Re: [PATCH 1/3] s390/vfio_ccw: Add __must_hold() attribute to vfio_ccw_sch_quiesce()

Heiko Carstens <[email protected]>
Newsgroups org.kernel.vger.linux-s390
Message-ID <[email protected]>
Eric, Matthew,

not sure if you are aware of the below (nothing I would address). Please handle :)

On Thu, Aug 06, 2026 at 03:52:23PM +0000, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
> 
> Pre-existing issues:
> - [High] Premature loop termination in `vfio_ccw_sch_quiesce` due to re-used completion without re-initialization.
> - [High] Unconditional NULL pointer dereference in asynchronous subchannel callbacks.
> - [High] Leaked workqueue items (`io_work` and `crw_work`) during device teardown leading to Use-After-Free.
> - [High] Lockless state access in `vfio_ccw_sch_io_todo` causes a double-free of channel program (`cp_free`).
> --
> 
> --- Patch [1]: [PATCH 1/3] s390/vfio_ccw: Add __must_hold() attribute to vfio_ccw_sch_quiesce() ---
> commit c7b4889a75b6cf11527a57bff8438c3c08dac9c4
> Author: Heiko Carstens <[email protected]>
> Subject: s390/vfio_ccw: Add __must_hold() attribute to vfio_ccw_sch_quiesce()
> 
> This patch adds the __must_hold attribute to vfio_ccw_sch_quiesce() to
> inform context analysis that sch->lock must be held on function entry.
> This resolves a thread-safety-analysis warning during compilation.
> 
> > diff --git a/drivers/s390/cio/vfio_ccw_drv.c b/drivers/s390/cio/vfio_ccw_drv.c
> > index 1a095085bc728..ed9ca77c5b23f 100644
> > --- a/drivers/s390/cio/vfio_ccw_drv.c
> > +++ b/drivers/s390/cio/vfio_ccw_drv.c
> > @@ -35,6 +35,7 @@ debug_info_t *vfio_ccw_debug_trace_id;
> >   * Helpers
> >   */
> >  int vfio_ccw_sch_quiesce(struct subchannel *sch)
> > +	__must_hold(&sch->lock);
> >  {
> >  	struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
> >  	struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev);
> 
> [Severity: High]
> This is a pre-existing issue, but does the completion handling in this loop
> allow premature termination?
> 
> If wait_for_completion_timeout() times out, but a late interrupt arrives
> and calls complete(), the completion's done counter becomes greater than 0.
> 
> On the next iteration of the do/while (ret == -EBUSY) loop in 
> vfio_ccw_sch_quiesce():
> 
> do {
>         ret = cio_cancel_halt_clear(sch, &iretry);
>         ...
>         if (ret == -EBUSY)
>                 wait_for_completion_timeout(&completion, 3*HZ);
>         ...
> } while (ret == -EBUSY);
> 
> Could wait_for_completion_timeout() see done > 0 and return immediately
> without waiting, causing a tight spin loop calling cio_disable_subchannel()
> since there is no reinit_completion() inside the loop?
> 
> [Severity: High]
> This isn't a bug introduced by this patch, but is there a risk of a NULL
> pointer dereference when fetching the parent pointer?
> 
> During device removal, vfio_ccw_sch_remove() sets the drvdata to NULL:
> dev_set_drvdata(&sch->dev, NULL);
> 
> If an asynchronous event like an interrupt triggers concurrently before the
> CSS core fully detaches the driver, could vfio_ccw_sch_irq() or
> vfio_ccw_sch_event() fetch a NULL parent and immediately dereference it
> in this sequence?
> 
> struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
> struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev);
> 
> [ ... ]
> > diff --git a/drivers/s390/cio/vfio_ccw_private.h b/drivers/s390/cio/vfio_ccw_private.h
> > index 0501d4bbcdbd6..8db29519dbfde 100644
> > --- a/drivers/s390/cio/vfio_ccw_private.h
> > +++ b/drivers/s390/cio/vfio_ccw_private.h
> > @@ -127,7 +127,8 @@ struct vfio_ccw_private {
> >  	struct work_struct	crw_work;
> >  } __aligned(8);
> 
> [Severity: High]
> This is a pre-existing issue, but does the device teardown path leak these
> pending work items?
> 
> When a mediated device is removed, vfio_ccw_mdev_release_dev() frees the
> private structure without calling cancel_work_sync() on io_work or crw_work:
> 
> static void vfio_ccw_mdev_release_dev(struct vfio_device *vdev)
> {
>         ...
>         kfree(private);
> }
> 
> Additionally, fsm_close() can skip the global flush_workqueue() if
> cio_disable_subchannel() immediately returns 0:
> 
> ret = cio_disable_subchannel(sch);
> if (ret == -EBUSY)
>         ret = vfio_ccw_sch_quiesce(sch);
> 
> If a work item is left pending on the workqueue, could it execute after
> the private structure has been freed, resulting in a use-after-free?
> 
> > -int vfio_ccw_sch_quiesce(struct subchannel *sch);
> > +int vfio_ccw_sch_quiesce(struct subchannel *sch)
> > +	__must_hold(&sch->lock);
> >  void vfio_ccw_sch_io_todo(struct work_struct *work);
> >  void vfio_ccw_crw_todo(struct work_struct *work);
> 
> [Severity: High]
> This isn't a bug introduced by this patch, but does the lockless state check
> in vfio_ccw_sch_io_todo() allow a double-free of the channel program?
> 
> The workqueue function vfio_ccw_sch_io_todo() reads private->state
> locklessly:
> 
> if (is_final && private->state == VFIO_CCW_STATE_CP_PENDING) {
>         cp_free(&private->cp);
>         cp_is_finished = true;
> }
> 
> Concurrently, fsm_close() holds sch->lock, changes the state to STANDBY,
> unlocks, and also calls cp_free():
> 
> spin_unlock_irq(&sch->lock);
> cp_free(&private->cp);
> 
> Since vfio_ccw_sch_io_todo() operates outside sch->lock, could both threads
> evaluate their conditions simultaneously and concurrently execute cp_free()
> on the same channel program?
> 
> -- 
> Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.