On Sat, 24 Jan 2026, Dominique Martinet wrote:
> Stefano Stabellini wrote on Fri, Jan 23, 2026 at 10:40:09AM -0800:
> > The xenwatch thread can race with other back-end change notifications
> > and call xen_9pfs_front_free() twice, hitting the observed general
> > protection fault due to a double-free. Guard the teardown path so only
> > one caller can release the front-end state at a time, preventing the
> > crash.
> 
> Thank you, just a couple of nitpicks below
> 
> > diff --git a/net/9p/trans_xen.c b/net/9p/trans_xen.c
> > index 280f686f60fbb..8809f3c06ec95 100644
> > --- a/net/9p/trans_xen.c
> > +++ b/net/9p/trans_xen.c
> > @@ -274,11 +274,7 @@ static void xen_9pfs_front_free(struct 
> > xen_9pfs_front_priv *priv)
> >  {
> >     int i, j;
> >  
> > -   write_lock(&xen_9pfs_lock);
> > -   list_del(&priv->list);
> > -   write_unlock(&xen_9pfs_lock);
> > -
> > -   for (i = 0; i < XEN_9PFS_NUM_RINGS; i++) {
> > +   for (i = 0; priv->rings != NULL && i < XEN_9PFS_NUM_RINGS; i++) {
> 
> 
> I don't understand this priv->rings != NULL check here;
> if it's guarding for front_free() called before front_init() then it
> doesn't need to be checked at every iteration, and if it can change in
> another thread I don't see why it would be safe.

xen_9pfs_front_free() can be reached from the error paths before any
rings are allocated, so we need to handle a NULL priv->rings but it
doesn't have to be checked at every iteration. I can move it before the
for loop as you suggested.


> 
> >             struct xen_9pfs_dataring *ring = &priv->rings[i];
> >  
> >             cancel_work_sync(&ring->work);
> > @@ -310,9 +306,18 @@ static void xen_9pfs_front_free(struct 
> > xen_9pfs_front_priv *priv)
> >  
> >  static void xen_9pfs_front_remove(struct xenbus_device *dev)
> >  {
> > -   struct xen_9pfs_front_priv *priv = dev_get_drvdata(&dev->dev);
> > +   struct xen_9pfs_front_priv *priv;
> >  
> > +   write_lock(&xen_9pfs_lock);
> > +   priv = dev_get_drvdata(&dev->dev);
> > +   if (priv == NULL) {
> > +           write_unlock(&xen_9pfs_lock);
> > +           return;
> > +   }
> >     dev_set_drvdata(&dev->dev, NULL);
> > +   list_del_init(&priv->list);
> 
> There's nothing wrong with using the del_init() variant here, but it
> would imply someone else could still access it after the unlock here,
> which means to me they could still access it after priv is freed in
> xen_9pfs_front_free().
> >From your commit message I think the priv == NULL check and
> dev_set_drvdata() under lock above is enough, can you confirm?

Yes you are right. I can replace list_del_init with list_del if you
think it is clearer.

 
> > +   write_unlock(&xen_9pfs_lock);
> > +
> >     xen_9pfs_front_free(priv);
> >  }
> >  
> > @@ -387,7 +392,7 @@ static int xen_9pfs_front_init(struct xenbus_device 
> > *dev)
> >  {
> >     int ret, i;
> >     struct xenbus_transaction xbt;
> > -   struct xen_9pfs_front_priv *priv = dev_get_drvdata(&dev->dev);
> > +   struct xen_9pfs_front_priv *priv;
> >     char *versions, *v;
> >     unsigned int max_rings, max_ring_order, len = 0;
> >  
> > @@ -415,6 +420,10 @@ static int xen_9pfs_front_init(struct xenbus_device 
> > *dev)
> >     if (p9_xen_trans.maxsize > XEN_FLEX_RING_SIZE(max_ring_order))
> >             p9_xen_trans.maxsize = XEN_FLEX_RING_SIZE(max_ring_order) / 2;
> >  
> > +   priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> > +   if (!priv)
> > +           return -ENOMEM;
> > +   priv->dev = dev;
> >     priv->rings = kcalloc(XEN_9PFS_NUM_RINGS, sizeof(*priv->rings),
> >                           GFP_KERNEL);
> >     if (!priv->rings) {
> > @@ -473,6 +482,11 @@ static int xen_9pfs_front_init(struct xenbus_device 
> > *dev)
> >             goto error;
> >     }
> >  
> > +   write_lock(&xen_9pfs_lock);
> > +   dev_set_drvdata(&dev->dev, priv);
> 
> Honest question: could xen_9pfs_front_init() also be called multiple
> times as well?
> (if so this should check for the previous drvdata and free the priv
> that was just built if it was non-null)
> 
> Please ignore this if you think that can't happen, I really don't
> know.

That should not be possible before freeing priv first:
xen_9pfs_front_init is only called when the frontend is in the
XenbusStateInitialising state (see xen_9pfs_front_changed). Once we
store priv we immediately switch the state to XenbusStateInitialised,
and there will be no more calls to xen_9pfs_front_init. If the backend
forces a re-probe, xenbus invokes xen_9pfs_front_remove first, which
frees priv.

Reply via email to