* [PATCH v3 0/2] 9p: fix guest-triggered Treaddir/ACPI eject UAF
@ 2026-07-23 12:43 Christian Schoenebeck
2026-07-23 12:43 ` [PATCH v3 2/2] hw/9pfs/xen: drain in-flight PDUs before xen-9p disconnect Christian Schoenebeck
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Christian Schoenebeck @ 2026-07-23 12:43 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Greg Kurz, Stefano Stabellini, Anthony PERARD,
Edgar E. Iglesias, Jia Jia
A guest can trigger unplugging 9pfs server's virtio-pci device via ACPI
eject. As a consequence the device is unrealized, server's internal state
is freed while pending coroutines would still have access to them, causing
a potential heap-use-after-free.
Overview Patches:
- Patch 1: this is the core fix, that drains all PDUs (i.e. coroutines
that handle individual pending requests in parallel) before freeing
server state.
- Patch 2: fixes a similar identified issue with the Xen transport, even
though not triggered via ACPI, it is also prone to UAF, plus a resource
leak.
v3:
- Patch 2: set s->transport = NULL in v9fs_device_unrealize_common()
and make the idempotent check just guard the v9fs_reset(s) and
v9fs_device_unrealize_common(s) calls in xen_9pfs_disconnect() to
prevent a NULL pointer dereference.
v2: [ https://lore.kernel.org/qemu-devel/cover.1784392605.git.qemu_oss@crudebyte.com/ ]
- Patch 1: Make Jia the official author of this patch.
- Drop prev. patch 2 ("hw/9pfs/virtio: disable hotpluggable property...")
- Patch 2: defer explict xen_9pfs_disconnect() call from error paths of
xen_9pfs_pdu_vmarshal() and xen_9pfs_pdu_vunmarshal().
Christian Schoenebeck (1):
hw/9pfs/xen: drain in-flight PDUs before xen-9p disconnect
Jia Jia (1):
hw/9pfs/virtio: drain in-flight PDUs before virtio-9p unrealize
hw/9pfs/9p.c | 1 +
hw/9pfs/virtio-9p-device.c | 1 +
hw/9pfs/xen-9p-backend.c | 17 +++++++++++++++--
3 files changed, 17 insertions(+), 2 deletions(-)
--
2.47.3
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v3 2/2] hw/9pfs/xen: drain in-flight PDUs before xen-9p disconnect 2026-07-23 12:43 [PATCH v3 0/2] 9p: fix guest-triggered Treaddir/ACPI eject UAF Christian Schoenebeck @ 2026-07-23 12:43 ` Christian Schoenebeck 2026-07-24 7:49 ` Philippe Mathieu-Daudé 2026-07-23 12:43 ` [PATCH v3 1/2] hw/9pfs/virtio: drain in-flight PDUs before virtio-9p unrealize Christian Schoenebeck 2026-07-24 7:35 ` [PATCH v3 0/2] 9p: fix guest-triggered Treaddir/ACPI eject UAF Christian Schoenebeck 2 siblings, 1 reply; 8+ messages in thread From: Christian Schoenebeck @ 2026-07-23 12:43 UTC (permalink / raw) To: qemu-devel Cc: qemu-stable, Greg Kurz, Stefano Stabellini, Anthony PERARD, Edgar E. Iglesias The xen-9p disconnect path has two issues: 1. It frees the Xen9pfsRing structures while in-flight PDUs may still reference them via pdu->tag to index rings[]. This causes a UAF in xen_9pfs_push_and_notify() when worker threads resume after completing filesystem operations. 2. It never calls v9fs_device_unrealize_common(), which means server state (struct LocalData, mountfd, FIDs) is never cleaned up on disconnect, causing a resource leak on every guest-initiated disconnect. Fix both by draining in-flight PDUs via v9fs_reset() before tearing down rings, and calling v9fs_device_unrealize_common() to clean up server state. Additionally, explicit calls of xen_9pfs_disconnect() in the error paths of xen_9pfs_pdu_vmarshal() and xen_9pfs_pdu_vunmarshal() must be deferred (via aio_bh_schedule_oneshot()), because xen_9pfs_pdu_v(un)marshal() are running within a coroutine context which makes them unsafe [1] for calling v9fs_reset() directly, as the latter e.g. has a loop like: while (!QLIST_EMPTY(&s->active_list)) { aio_poll(qemu_get_aio_context(), true); } which would a) never terminate (as the coroutine is on the active_list) and b) aio_poll() is marked as no_coroutine_fn. [1] https://lore.kernel.org/qemu-devel/3351181.5fSG56mABF@weasel/ And finally, add an idempotent guard to xen_9pfs_disconnect() for the v9fs_reset(s) and v9fs_device_unrealize_common(s) calls specifically [2], just to be sure. [2] https://lore.kernel.org/qemu-devel/alpine.DEB.2.22.394.2607221815520.5295@ubuntu-linux-20-04-desktop/ Fixes: b37eeb0201 ("xen/9pfs: introduce Xen 9pfs backend") Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> --- hw/9pfs/9p.c | 1 + hw/9pfs/xen-9p-backend.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 3119f01117..fc01791830 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -4530,6 +4530,7 @@ void v9fs_device_unrealize_common(V9fsState *s) qp_table_destroy(&s->qpp_table); qp_table_destroy(&s->qpf_table); g_free(s->ctx.fs_root); + s->transport = NULL; } typedef struct VirtfsCoResetData { diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index 24c90d97ec..a2f118d2ca 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -68,6 +68,11 @@ typedef struct Xen9pfsDev { static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev); +static void xen_9pfs_disconnect_bh(void *opaque) +{ + xen_9pfs_disconnect(opaque); +} + static void xen_9pfs_in_sg(Xen9pfsRing *ring, struct iovec *in_sg, int *num, @@ -150,7 +155,8 @@ static ssize_t xen_9pfs_pdu_vmarshal(V9fsPDU *pdu, "Failed to encode VirtFS reply type %d\n", pdu->id + 1); xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); - xen_9pfs_disconnect(&xen_9pfs->xendev); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + xen_9pfs_disconnect_bh, &xen_9pfs->xendev); } return ret; } @@ -173,7 +179,8 @@ static ssize_t xen_9pfs_pdu_vunmarshal(V9fsPDU *pdu, xen_pv_printf(&xen_9pfs->xendev, 0, "Failed to decode VirtFS request type %d\n", pdu->id); xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); - xen_9pfs_disconnect(&xen_9pfs->xendev); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + xen_9pfs_disconnect_bh, &xen_9pfs->xendev); } return ret; } @@ -368,10 +375,16 @@ static void xen_9pfs_evtchn_event(void *opaque) static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev) { Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev); + V9fsState *s = &xen_9pdev->state; int i; trace_xen_9pfs_disconnect(xendev->name); + if (s->transport) { + v9fs_reset(s); + v9fs_device_unrealize_common(s); + } + for (i = 0; i < xen_9pdev->num_rings; i++) { if (xen_9pdev->rings[i].evtchndev != NULL) { qemu_set_fd_handler(qemu_xen_evtchn_fd(xen_9pdev->rings[i].evtchndev), -- 2.47.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3 2/2] hw/9pfs/xen: drain in-flight PDUs before xen-9p disconnect 2026-07-23 12:43 ` [PATCH v3 2/2] hw/9pfs/xen: drain in-flight PDUs before xen-9p disconnect Christian Schoenebeck @ 2026-07-24 7:49 ` Philippe Mathieu-Daudé 2026-07-24 9:09 ` Christian Schoenebeck 0 siblings, 1 reply; 8+ messages in thread From: Philippe Mathieu-Daudé @ 2026-07-24 7:49 UTC (permalink / raw) To: Christian Schoenebeck, qemu-devel Cc: qemu-stable, Greg Kurz, Stefano Stabellini, Anthony PERARD, Edgar E. Iglesias On 23/7/26 14:43, Christian Schoenebeck wrote: > The xen-9p disconnect path has two issues: > > 1. It frees the Xen9pfsRing structures while in-flight PDUs may still > reference them via pdu->tag to index rings[]. This causes a UAF > in xen_9pfs_push_and_notify() when worker threads resume after > completing filesystem operations. > > 2. It never calls v9fs_device_unrealize_common(), which means server > state (struct LocalData, mountfd, FIDs) is never cleaned up on > disconnect, causing a resource leak on every guest-initiated > disconnect. > > Fix both by draining in-flight PDUs via v9fs_reset() before tearing > down rings, and calling v9fs_device_unrealize_common() to clean up > server state. > > Additionally, explicit calls of xen_9pfs_disconnect() in the error > paths of xen_9pfs_pdu_vmarshal() and xen_9pfs_pdu_vunmarshal() must > be deferred (via aio_bh_schedule_oneshot()), because > xen_9pfs_pdu_v(un)marshal() are running within a coroutine context > which makes them unsafe [1] for calling v9fs_reset() directly, as > the latter e.g. has a loop like: > > while (!QLIST_EMPTY(&s->active_list)) { > aio_poll(qemu_get_aio_context(), true); > } > > which would a) never terminate (as the coroutine is on the > active_list) and b) aio_poll() is marked as no_coroutine_fn. > > [1] https://lore.kernel.org/qemu-devel/3351181.5fSG56mABF@weasel/ > > And finally, add an idempotent guard to xen_9pfs_disconnect() > for the v9fs_reset(s) and v9fs_device_unrealize_common(s) calls > specifically [2], just to be sure. > > [2] https://lore.kernel.org/qemu-devel/alpine.DEB.2.22.394.2607221815520.5295@ubuntu-linux-20-04-desktop/ > > Fixes: b37eeb0201 ("xen/9pfs: introduce Xen 9pfs backend") > Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com> > Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> > --- > hw/9pfs/9p.c | 1 + > hw/9pfs/xen-9p-backend.c | 17 +++++++++++++++-- > 2 files changed, 16 insertions(+), 2 deletions(-) > > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c > index 3119f01117..fc01791830 100644 > --- a/hw/9pfs/9p.c > +++ b/hw/9pfs/9p.c > @@ -4530,6 +4530,7 @@ void v9fs_device_unrealize_common(V9fsState *s) > qp_table_destroy(&s->qpp_table); > qp_table_destroy(&s->qpf_table); > g_free(s->ctx.fs_root); > + s->transport = NULL; > } > > typedef struct VirtfsCoResetData { > diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c > index 24c90d97ec..a2f118d2ca 100644 > --- a/hw/9pfs/xen-9p-backend.c > +++ b/hw/9pfs/xen-9p-backend.c > @@ -68,6 +68,11 @@ typedef struct Xen9pfsDev { > > static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev); > > +static void xen_9pfs_disconnect_bh(void *opaque) > +{ > + xen_9pfs_disconnect(opaque); > +} > + > static void xen_9pfs_in_sg(Xen9pfsRing *ring, > struct iovec *in_sg, > int *num, > @@ -150,7 +155,8 @@ static ssize_t xen_9pfs_pdu_vmarshal(V9fsPDU *pdu, > "Failed to encode VirtFS reply type %d\n", > pdu->id + 1); > xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); > - xen_9pfs_disconnect(&xen_9pfs->xendev); > + aio_bh_schedule_oneshot(qemu_get_aio_context(), > + xen_9pfs_disconnect_bh, &xen_9pfs->xendev); > } > return ret; > } > @@ -173,7 +179,8 @@ static ssize_t xen_9pfs_pdu_vunmarshal(V9fsPDU *pdu, > xen_pv_printf(&xen_9pfs->xendev, 0, > "Failed to decode VirtFS request type %d\n", pdu->id); > xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); > - xen_9pfs_disconnect(&xen_9pfs->xendev); > + aio_bh_schedule_oneshot(qemu_get_aio_context(), > + xen_9pfs_disconnect_bh, &xen_9pfs->xendev); > } > return ret; > } > @@ -368,10 +375,16 @@ static void xen_9pfs_evtchn_event(void *opaque) > static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev) > { > Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev); > + V9fsState *s = &xen_9pdev->state; > int i; > > trace_xen_9pfs_disconnect(xendev->name); > > + if (s->transport) { > + v9fs_reset(s); Maybe v9fs_reset() is misnamed. What about v9fs_cancel_in_flight()? > + v9fs_device_unrealize_common(s); IIUC xen_9pfs_disconnect() is mixing XenDevOps::disconnect() and XenDevOps::free()? > + } > + > for (i = 0; i < xen_9pdev->num_rings; i++) { > if (xen_9pdev->rings[i].evtchndev != NULL) { > qemu_set_fd_handler(qemu_xen_evtchn_fd(xen_9pdev->rings[i].evtchndev), ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 2/2] hw/9pfs/xen: drain in-flight PDUs before xen-9p disconnect 2026-07-24 7:49 ` Philippe Mathieu-Daudé @ 2026-07-24 9:09 ` Christian Schoenebeck 0 siblings, 0 replies; 8+ messages in thread From: Christian Schoenebeck @ 2026-07-24 9:09 UTC (permalink / raw) To: Stefano Stabellini, Philippe Mathieu-Daudé Cc: qemu-devel, qemu-stable, Greg Kurz, Anthony PERARD, Edgar E. Iglesias On Friday, 24 July 2026 09:49:57 CEST Philippe Mathieu-Daudé wrote: > On 23/7/26 14:43, Christian Schoenebeck wrote: > > The xen-9p disconnect path has two issues: > > > > 1. It frees the Xen9pfsRing structures while in-flight PDUs may still > > > > reference them via pdu->tag to index rings[]. This causes a UAF > > in xen_9pfs_push_and_notify() when worker threads resume after > > completing filesystem operations. > > > > 2. It never calls v9fs_device_unrealize_common(), which means server > > > > state (struct LocalData, mountfd, FIDs) is never cleaned up on > > disconnect, causing a resource leak on every guest-initiated > > disconnect. > > > > Fix both by draining in-flight PDUs via v9fs_reset() before tearing > > down rings, and calling v9fs_device_unrealize_common() to clean up > > server state. > > > > Additionally, explicit calls of xen_9pfs_disconnect() in the error > > paths of xen_9pfs_pdu_vmarshal() and xen_9pfs_pdu_vunmarshal() must > > be deferred (via aio_bh_schedule_oneshot()), because > > xen_9pfs_pdu_v(un)marshal() are running within a coroutine context > > which makes them unsafe [1] for calling v9fs_reset() directly, as > > > > the latter e.g. has a loop like: > > while (!QLIST_EMPTY(&s->active_list)) { > > > > aio_poll(qemu_get_aio_context(), true); > > > > } > > > > which would a) never terminate (as the coroutine is on the > > active_list) and b) aio_poll() is marked as no_coroutine_fn. > > > > [1] https://lore.kernel.org/qemu-devel/3351181.5fSG56mABF@weasel/ > > > > And finally, add an idempotent guard to xen_9pfs_disconnect() > > for the v9fs_reset(s) and v9fs_device_unrealize_common(s) calls > > specifically [2], just to be sure. > > > > [2] > > https://lore.kernel.org/qemu-devel/alpine.DEB.2.22.394.2607221815520.5295 > > @ubuntu-linux-20-04-desktop/ > > > > Fixes: b37eeb0201 ("xen/9pfs: introduce Xen 9pfs backend") > > Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com> > > Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> > > --- > > > > hw/9pfs/9p.c | 1 + > > hw/9pfs/xen-9p-backend.c | 17 +++++++++++++++-- > > 2 files changed, 16 insertions(+), 2 deletions(-) > > > > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c > > index 3119f01117..fc01791830 100644 > > --- a/hw/9pfs/9p.c > > +++ b/hw/9pfs/9p.c > > @@ -4530,6 +4530,7 @@ void v9fs_device_unrealize_common(V9fsState *s) > > > > qp_table_destroy(&s->qpp_table); > > qp_table_destroy(&s->qpf_table); > > g_free(s->ctx.fs_root); > > > > + s->transport = NULL; > > > > } > > > > typedef struct VirtfsCoResetData { > > > > diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c > > index 24c90d97ec..a2f118d2ca 100644 > > --- a/hw/9pfs/xen-9p-backend.c > > +++ b/hw/9pfs/xen-9p-backend.c > > @@ -68,6 +68,11 @@ typedef struct Xen9pfsDev { > > > > static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev); > > > > +static void xen_9pfs_disconnect_bh(void *opaque) > > +{ > > + xen_9pfs_disconnect(opaque); > > +} > > + > > > > static void xen_9pfs_in_sg(Xen9pfsRing *ring, > > > > struct iovec *in_sg, > > int *num, > > > > @@ -150,7 +155,8 @@ static ssize_t xen_9pfs_pdu_vmarshal(V9fsPDU *pdu, > > > > "Failed to encode VirtFS reply type %d\n", > > pdu->id + 1); > > > > xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); > > > > - xen_9pfs_disconnect(&xen_9pfs->xendev); > > + aio_bh_schedule_oneshot(qemu_get_aio_context(), > > + xen_9pfs_disconnect_bh, > > &xen_9pfs->xendev);> > > } > > return ret; > > > > } > > > > @@ -173,7 +179,8 @@ static ssize_t xen_9pfs_pdu_vunmarshal(V9fsPDU *pdu, > > > > xen_pv_printf(&xen_9pfs->xendev, 0, > > > > "Failed to decode VirtFS request type %d\n", > > pdu->id); > > > > xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); > > > > - xen_9pfs_disconnect(&xen_9pfs->xendev); > > + aio_bh_schedule_oneshot(qemu_get_aio_context(), > > + xen_9pfs_disconnect_bh, > > &xen_9pfs->xendev);> > > } > > return ret; > > > > } > > > > @@ -368,10 +375,16 @@ static void xen_9pfs_evtchn_event(void *opaque) > > > > static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev) > > { > > > > Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev); > > > > + V9fsState *s = &xen_9pdev->state; > > > > int i; > > > > trace_xen_9pfs_disconnect(xendev->name); > > > > + if (s->transport) { > > + v9fs_reset(s); > > Maybe v9fs_reset() is misnamed. > > What about v9fs_cancel_in_flight()? The name v9fs_cancel_in_flight() would be too narrow. v9fs_reset() does two things: 1. draining all in-flight PDUs 2. freeing all FIDs (and their associated hash table) Therefore IMO v9fs_reset() more appropriately describes a reset of server state. v9fs_reset() already exists for almost 10 years: commit 0e44a0fd3f28cccb8963fdfc05c53c546b3f46b6 Author: Greg Kurz <groug@kaod.org> Date: Mon Oct 17 14:13:58 2016 +0200 virtio-9p: add reset handler Virtio devices should implement the VirtIODevice->reset() function to perform necessary cleanup actions and to bring the device to a quiescent state. In the case of the virtio-9p device, this means: - emptying the list of active PDUs (i.e. draining all in-flight I/O) - freeing all fids (i.e. close open file descriptors and free memory) ... And even though the aspect of draining in-flight PDUs is focused and relevant for this particular security patch to prevent the mentioned UAF, it is still correct and expected to do a full server reset here. > > > + v9fs_device_unrealize_common(s); > > IIUC xen_9pfs_disconnect() is mixing XenDevOps::disconnect() > and XenDevOps::free()? While you are correct with your observation that it does mix the two things, I fear this is required right now. Separating them would be cleaner and possible, but it would require significant changes. And if you review my discussions with Stefano in the previous versions of this series, you might realize that this code is somewhat tricky and not trivial. And finally: 1. These patches are addressing real security issues. 2. Your concerns are pure code organizational issues, no behaviour issues. 3. These structural issues exist for many (9?) years. 4. Changing this struture might introduce new issues. 5. We are already very close to release. 6. This series (v1) was posted already 3 weeks ago. 7. I have a bunch of other important fixes in my queue that I should send a PR So we can probably agree that this should not be within the scope of these security patches? > > + } > > + > > > > for (i = 0; i < xen_9pdev->num_rings; i++) { > > > > if (xen_9pdev->rings[i].evtchndev != NULL) { > > > > qemu_set_fd_handler(qemu_xen_evtchn_fd(xen_9pdev->rings[i].e > > vtchndev), ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 1/2] hw/9pfs/virtio: drain in-flight PDUs before virtio-9p unrealize 2026-07-23 12:43 [PATCH v3 0/2] 9p: fix guest-triggered Treaddir/ACPI eject UAF Christian Schoenebeck 2026-07-23 12:43 ` [PATCH v3 2/2] hw/9pfs/xen: drain in-flight PDUs before xen-9p disconnect Christian Schoenebeck @ 2026-07-23 12:43 ` Christian Schoenebeck 2026-07-24 7:45 ` Philippe Mathieu-Daudé 2026-07-24 7:35 ` [PATCH v3 0/2] 9p: fix guest-triggered Treaddir/ACPI eject UAF Christian Schoenebeck 2 siblings, 1 reply; 8+ messages in thread From: Christian Schoenebeck @ 2026-07-23 12:43 UTC (permalink / raw) To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Jia Jia From: Jia Jia <physicalmtea@gmail.com> A guest can trigger a heap-use-after-free in the virtio transport unrealize path by submitting a Treaddir request and immediately ejecting the device via ACPI PCI hotplug. The unrealize path frees struct LocalData while a worker thread still holds a reference on it, causing a UAF in local_open_nofollow(). Fix this by draining all in-flight 9p PDUs by calling v9fs_reset() before final server cleanup. This ensures all coroutines completed, all FIDs are closed, and no worker thread still holds references on 9p server state when it is freed. Fixes: 6cecf09373 ("virtio-9p-device: add minimal unrealize handler") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3937 Signed-off-by: Jia Jia <physicalmtea@gmail.com> [ Christian Schoenebeck: add commit log message. ] Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com> --- hw/9pfs/virtio-9p-device.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 50dc93091d..1ec48fc9e0 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -243,6 +243,7 @@ static void virtio_9p_device_unrealize(DeviceState *dev) V9fsVirtioState *v = VIRTIO_9P(dev); V9fsState *s = &v->state; + v9fs_reset(s); virtio_delete_queue(v->vq); virtio_cleanup(vdev); v9fs_device_unrealize_common(s); -- 2.47.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3 1/2] hw/9pfs/virtio: drain in-flight PDUs before virtio-9p unrealize 2026-07-23 12:43 ` [PATCH v3 1/2] hw/9pfs/virtio: drain in-flight PDUs before virtio-9p unrealize Christian Schoenebeck @ 2026-07-24 7:45 ` Philippe Mathieu-Daudé 2026-07-24 9:26 ` Christian Schoenebeck 0 siblings, 1 reply; 8+ messages in thread From: Philippe Mathieu-Daudé @ 2026-07-24 7:45 UTC (permalink / raw) To: Christian Schoenebeck, qemu-devel; +Cc: qemu-stable, Greg Kurz, Jia Jia Hi Christian, On 23/7/26 14:43, Christian Schoenebeck wrote: > From: Jia Jia <physicalmtea@gmail.com> > > A guest can trigger a heap-use-after-free in the virtio transport > unrealize path by submitting a Treaddir request and immediately > ejecting the device via ACPI PCI hotplug. The unrealize path frees > struct LocalData while a worker thread still holds a reference > on it, causing a UAF in local_open_nofollow(). > > Fix this by draining all in-flight 9p PDUs by calling v9fs_reset() > before final server cleanup. This ensures all coroutines completed, > all FIDs are closed, and no worker thread still holds references > on 9p server state when it is freed. > > Fixes: 6cecf09373 ("virtio-9p-device: add minimal unrealize handler") > Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3937 > Signed-off-by: Jia Jia <physicalmtea@gmail.com> > [ Christian Schoenebeck: add commit log message. ] > Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com> > --- > hw/9pfs/virtio-9p-device.c | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c > index 50dc93091d..1ec48fc9e0 100644 > --- a/hw/9pfs/virtio-9p-device.c > +++ b/hw/9pfs/virtio-9p-device.c > @@ -243,6 +243,7 @@ static void virtio_9p_device_unrealize(DeviceState *dev) > V9fsVirtioState *v = VIRTIO_9P(dev); > V9fsState *s = &v->state; > > + v9fs_reset(s); While doing the same, virtio_9p_reset(vdev) seems a better API access. Also calling RESET on UNREALIZE deserves some comment: virtio_9p_reset(vdev); /* Drain in-flight PDUs */ Could you update that in 9p.next? > virtio_delete_queue(v->vq); > virtio_cleanup(vdev); > v9fs_device_unrealize_common(s); ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 1/2] hw/9pfs/virtio: drain in-flight PDUs before virtio-9p unrealize 2026-07-24 7:45 ` Philippe Mathieu-Daudé @ 2026-07-24 9:26 ` Christian Schoenebeck 0 siblings, 0 replies; 8+ messages in thread From: Christian Schoenebeck @ 2026-07-24 9:26 UTC (permalink / raw) To: Philippe Mathieu-Daudé; +Cc: qemu-devel, qemu-stable, Greg Kurz, Jia Jia On Friday, 24 July 2026 09:45:39 CEST Philippe Mathieu-Daudé wrote: > Hi Christian, > > On 23/7/26 14:43, Christian Schoenebeck wrote: > > From: Jia Jia <physicalmtea@gmail.com> > > > > A guest can trigger a heap-use-after-free in the virtio transport > > unrealize path by submitting a Treaddir request and immediately > > ejecting the device via ACPI PCI hotplug. The unrealize path frees > > struct LocalData while a worker thread still holds a reference > > on it, causing a UAF in local_open_nofollow(). > > > > Fix this by draining all in-flight 9p PDUs by calling v9fs_reset() > > before final server cleanup. This ensures all coroutines completed, > > all FIDs are closed, and no worker thread still holds references > > on 9p server state when it is freed. > > > > Fixes: 6cecf09373 ("virtio-9p-device: add minimal unrealize handler") > > Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3937 > > Signed-off-by: Jia Jia <physicalmtea@gmail.com> > > [ Christian Schoenebeck: add commit log message. ] > > Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com> > > --- > > > > hw/9pfs/virtio-9p-device.c | 1 + > > 1 file changed, 1 insertion(+) > > > > diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c > > index 50dc93091d..1ec48fc9e0 100644 > > --- a/hw/9pfs/virtio-9p-device.c > > +++ b/hw/9pfs/virtio-9p-device.c > > @@ -243,6 +243,7 @@ static void virtio_9p_device_unrealize(DeviceState > > *dev)> > > V9fsVirtioState *v = VIRTIO_9P(dev); > > V9fsState *s = &v->state; > > > > + v9fs_reset(s); > > While doing the same, virtio_9p_reset(vdev) seems a better API access. > Also calling RESET on UNREALIZE deserves some comment: > > virtio_9p_reset(vdev); /* Drain in-flight PDUs */ > > Could you update that in 9p.next? Well, I can add a comment, then I should probably add a more detailled comment why draining the in-flight PDUs is needed, which I thought would already be covered by the verbose commit log message. About the virtio_9p_reset() change: I don't think that's needed. virtio_9p_reset exists because it acts as callback for resetting the virtio device. But I don't see the usefulness of adding another call to the call stack in virtio_9p_device_unrealize() which does the same, with more instructions? Also note my other notes to your comments on patch 2, i.e. security fixes, narro release window, etc.? > > virtio_delete_queue(v->vq); > > virtio_cleanup(vdev); > > v9fs_device_unrealize_common(s); ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 0/2] 9p: fix guest-triggered Treaddir/ACPI eject UAF 2026-07-23 12:43 [PATCH v3 0/2] 9p: fix guest-triggered Treaddir/ACPI eject UAF Christian Schoenebeck 2026-07-23 12:43 ` [PATCH v3 2/2] hw/9pfs/xen: drain in-flight PDUs before xen-9p disconnect Christian Schoenebeck 2026-07-23 12:43 ` [PATCH v3 1/2] hw/9pfs/virtio: drain in-flight PDUs before virtio-9p unrealize Christian Schoenebeck @ 2026-07-24 7:35 ` Christian Schoenebeck 2 siblings, 0 replies; 8+ messages in thread From: Christian Schoenebeck @ 2026-07-24 7:35 UTC (permalink / raw) To: qemu-devel Cc: qemu-stable, Greg Kurz, Stefano Stabellini, Anthony PERARD, Edgar E. Iglesias, Jia Jia On Thursday, 23 July 2026 14:43:24 CEST Christian Schoenebeck wrote: > A guest can trigger unplugging 9pfs server's virtio-pci device via ACPI > eject. As a consequence the device is unrealized, server's internal state > is freed while pending coroutines would still have access to them, causing > a potential heap-use-after-free. > > Overview Patches: > > - Patch 1: this is the core fix, that drains all PDUs (i.e. coroutines > that handle individual pending requests in parallel) before freeing > server state. > > - Patch 2: fixes a similar identified issue with the Xen transport, even > though not triggered via ACPI, it is also prone to UAF, plus a resource > leak. > > v3: > - Patch 2: set s->transport = NULL in v9fs_device_unrealize_common() > and make the idempotent check just guard the v9fs_reset(s) and > v9fs_device_unrealize_common(s) calls in xen_9pfs_disconnect() to > prevent a NULL pointer dereference. > > v2: [ > https://lore.kernel.org/qemu-devel/cover.1784392605.git.qemu_oss@crudebyte. > com/ ] - Patch 1: Make Jia the official author of this patch. > - Drop prev. patch 2 ("hw/9pfs/virtio: disable hotpluggable property...") > - Patch 2: defer explict xen_9pfs_disconnect() call from error paths of > xen_9pfs_pdu_vmarshal() and xen_9pfs_pdu_vunmarshal(). > > Christian Schoenebeck (1): > hw/9pfs/xen: drain in-flight PDUs before xen-9p disconnect > > Jia Jia (1): > hw/9pfs/virtio: drain in-flight PDUs before virtio-9p unrealize > > hw/9pfs/9p.c | 1 + > hw/9pfs/virtio-9p-device.c | 1 + > hw/9pfs/xen-9p-backend.c | 17 +++++++++++++++-- > 3 files changed, 17 insertions(+), 2 deletions(-) Queued on 9p.next: https://github.com/cschoenebeck/qemu/commits/9p.next Thanks! /Christian ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-07-24 9:27 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-23 12:43 [PATCH v3 0/2] 9p: fix guest-triggered Treaddir/ACPI eject UAF Christian Schoenebeck 2026-07-23 12:43 ` [PATCH v3 2/2] hw/9pfs/xen: drain in-flight PDUs before xen-9p disconnect Christian Schoenebeck 2026-07-24 7:49 ` Philippe Mathieu-Daudé 2026-07-24 9:09 ` Christian Schoenebeck 2026-07-23 12:43 ` [PATCH v3 1/2] hw/9pfs/virtio: drain in-flight PDUs before virtio-9p unrealize Christian Schoenebeck 2026-07-24 7:45 ` Philippe Mathieu-Daudé 2026-07-24 9:26 ` Christian Schoenebeck 2026-07-24 7:35 ` [PATCH v3 0/2] 9p: fix guest-triggered Treaddir/ACPI eject UAF Christian Schoenebeck
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.