* [PULL 17/23] tests/9p: increase P9_MAX_SIZE for test client
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 09/23] hw/9pfs: add xattr FID limit to prevent memory exhaustion Christian Schoenebeck
` (24 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
Increase the maximum 9P message size ('msize') of 9p test client from
4k to 32k to support larger messages.
This is needed for the xattr tests being added with the subsequent
patches which are going to transmit xattrs of size 8k. It would have
also been possible to send them in multiple chunks, however let's not
overcomplicate things.
This new msize is still reasonable small compared to common msize
values on production systems.
Link: https://lore.kernel.org/qemu-devel/2dcb1243c80ea97d085af5171785850cf012be36.1781361555.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
tests/qtest/libqos/virtio-9p-client.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tests/qtest/libqos/virtio-9p-client.h b/tests/qtest/libqos/virtio-9p-client.h
index c432b0daee..4b04324503 100644
--- a/tests/qtest/libqos/virtio-9p-client.h
+++ b/tests/qtest/libqos/virtio-9p-client.h
@@ -21,7 +21,8 @@
#include "qgraph.h"
#include "tests/qtest/libqtest-single.h"
-#define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */
+/* Max size of a T-message or R-message */
+#define P9_MAX_SIZE (32 * 1024)
typedef struct {
QTestState *qts;
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 09/23] hw/9pfs: add xattr FID limit to prevent memory exhaustion
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
2026-06-29 13:28 ` [PULL 17/23] tests/9p: increase P9_MAX_SIZE for test client Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 23/23] hw/9pfs/local: harden local_fid_fd() on FID types Christian Schoenebeck
` (23 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
Add a limit on the number of simultaneously open xattr FIDs to prevent
host memory exhaustion attacks. Each xattr FID contains a buffer for the
xattr value, and without a limit, a malicious priviliged guest with
direct communication access to 9p server could create a huge number of
xattr FIDs until host memory is eventually exhausted.
Fix this by:
- add xattr_fid_limit to struct FsContext for the max. amount
- add xattr_fid_count to struct FsContext for the current amount
- init xattr_fid_limit with 1024
- init xattr_fid_count with 0
- add function xattr_fid_count_inc() to increment the count
- add function xattr_fid_count_decr() to decrement the count
- call xattr_fid_count_inc() in Txattrcreate handler
- call xattr_fid_count_inc() in Txattrwalk handler
- call xattr_fid_count_decr() when a xattr FID is freed
Additionally:
- reset the xattr FID counter in virtfs_reset()
When the limit is reached then xattr_fid_count_inc() returns -ENOSPC and
the request handler is aborted on its error path without turning the
FID into an xattr type and without allocating memory for the xattr.
The default value of 1024 was chosen, as (sane usage of) xattr requests
in the 9p protocol are usually very short-lived, and even machines with
128 cores with very high xattr activity should have plenty of head room
without ever hitting this limit.
Fixes: 10b468bdc5 ("virtio-9p: Implement TXATTRCREATE")
Fixes: CVE-2026-8348
Reported-by: Feifan Qian <bea1e@proton.me>
Link: https://lore.kernel.org/qemu-devel/eb3787869745d47234fb662600187bf773e1ef8a.1781361555.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
fsdev/file-op-9p.h | 9 +++++++
hw/9pfs/9p.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 71 insertions(+)
diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h
index e8d0661c4b..454761d81d 100644
--- a/fsdev/file-op-9p.h
+++ b/fsdev/file-op-9p.h
@@ -81,6 +81,11 @@ typedef struct ExtendedOps {
#define V9FS_SEC_MASK 0x0000003C
+/*
+ * Limits the maximum amount of simultaneously open xattr FIDs to prevent
+ * host memory exhaustion (as each xattr FID contains a xattr value buffer).
+ */
+#define V9FS_MAX_XATTR_DEFAULT 1024
typedef struct FileOperations FileOperations;
typedef struct XattrOperations XattrOperations;
@@ -109,6 +114,10 @@ struct FsContext {
void *private;
mode_t fmode;
mode_t dmode;
+ /* max. amount of simultaneously open xattr FIDs */
+ uint32_t xattr_fid_limit;
+ /* current amount of open xattr FIDs */
+ uint32_t xattr_fid_count;
};
struct V9fsPath {
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index 09454a5404..e737629581 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -265,6 +265,31 @@ static size_t v9fs_string_size(V9fsString *str)
return str->size;
}
+static int xattr_fid_count_inc(V9fsPDU *pdu)
+{
+ V9fsState *s = pdu->s;
+
+ if (s->ctx.xattr_fid_limit > 0 &&
+ s->ctx.xattr_fid_count >= s->ctx.xattr_fid_limit) {
+ error_report_once("9pfs: xattr_fid_count limit exceeded "
+ "(configurable by option 'max_xattr').");
+ return -ENOSPC;
+ }
+ s->ctx.xattr_fid_count++;
+ return 0;
+}
+
+static void xattr_fid_count_decr(V9fsPDU *pdu)
+{
+ V9fsState *s = pdu->s;
+
+ if (s->ctx.xattr_fid_count > 0) {
+ s->ctx.xattr_fid_count--;
+ } else {
+ error_report_once("9pfs: xattr_fid_count underflow detected");
+ }
+}
+
/*
* returns 0 if fid got re-opened, 1 if not, < 0 on error
*/
@@ -397,6 +422,7 @@ static int coroutine_fn free_fid(V9fsPDU *pdu, V9fsFidState *fidp)
}
} else if (fidp->fid_type == P9_FID_XATTR) {
retval = v9fs_xattr_fid_clunk(pdu, fidp);
+ xattr_fid_count_decr(pdu);
}
v9fs_path_free(&fidp->path);
g_free(fidp);
@@ -634,6 +660,14 @@ static void coroutine_fn virtfs_reset(V9fsPDU *pdu)
fidp->clunked = true;
put_fid(pdu, fidp);
}
+
+ /*
+ * Explicitly reset the xattr FID counter.
+ *
+ * free_fid() already decrements the counter for each P9_FID_XATTR, so the
+ * counter should already be zero, hence this is just a defensive measure.
+ */
+ s->ctx.xattr_fid_count = 0;
}
#define P9_QID_TYPE_DIR 0x80
@@ -4023,6 +4057,14 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque)
clunk_fid(s, xattr_fidp->fid);
goto out;
}
+
+ /* Check xattr FID limit */
+ err = xattr_fid_count_inc(pdu);
+ if (err < 0) {
+ clunk_fid(s, xattr_fidp->fid);
+ goto out;
+ }
+
/*
* Read the xattr value
*/
@@ -4030,6 +4072,7 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque)
xattr_fidp->fid_type = P9_FID_XATTR;
xattr_fidp->fs.xattr.xattrwalk_fid = true;
xattr_fidp->fs.xattr.value = g_malloc0(size);
+
if (size) {
err = v9fs_co_llistxattr(pdu, &xattr_fidp->path,
xattr_fidp->fs.xattr.value,
@@ -4056,6 +4099,14 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque)
clunk_fid(s, xattr_fidp->fid);
goto out;
}
+
+ /* Check xattr FID limit */
+ err = xattr_fid_count_inc(pdu);
+ if (err < 0) {
+ clunk_fid(s, xattr_fidp->fid);
+ goto out;
+ }
+
/*
* Read the xattr value
*/
@@ -4063,6 +4114,7 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque)
xattr_fidp->fid_type = P9_FID_XATTR;
xattr_fidp->fs.xattr.xattrwalk_fid = true;
xattr_fidp->fs.xattr.value = g_malloc0(size);
+
if (size) {
err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path,
&name, xattr_fidp->fs.xattr.value,
@@ -4164,6 +4216,12 @@ static void coroutine_fn v9fs_xattrcreate(void *opaque)
goto out_put_fid;
}
+ /* Check xattr FID limit */
+ err = xattr_fid_count_inc(pdu);
+ if (err < 0) {
+ goto out_put_fid;
+ }
+
/* Make the file fid point to xattr */
xattr_fidp = file_fidp;
xattr_fidp->fid_type = P9_FID_XATTR;
@@ -4425,6 +4483,10 @@ int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t,
s->reclaiming = false;
+ /* init xattr FID limit */
+ s->ctx.xattr_fid_limit = V9FS_MAX_XATTR_DEFAULT;
+ s->ctx.xattr_fid_count = 0;
+
rc = 0;
out:
if (rc) {
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 23/23] hw/9pfs/local: harden local_fid_fd() on FID types
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
2026-06-29 13:28 ` [PULL 17/23] tests/9p: increase P9_MAX_SIZE for test client Christian Schoenebeck
2026-06-29 13:28 ` [PULL 09/23] hw/9pfs: add xattr FID limit to prevent memory exhaustion Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 03/23] 9pfs/xen: implement msize_limit callback Christian Schoenebeck
` (22 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
local_fid_fd() returns fs->fd for any FID type that is not P9_FID_DIR.
Since P9_FID_XATTR and P9_FID_NONE share union V9fsFidOpenState, calling
local_fid_fd() on these types misinterprets xattr state as a file
descriptor, potentially leading to undefined behaviour or information
disclosure.
Even though we are catching these FID type mismatches on protocol level
in 9p.c already, previous patches proofed this to be error prone.
So let's add another safety layer in local_fid_fd() that would return -1
if the FID type would not possess a valid file descriptor, to prevent
wrong file descriptors from reaching fs backend calls.
Link: https://lore.kernel.org/qemu-devel/531f6b81bc1bf1a48c3d4afaa60a65db10511041.1781621428.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
hw/9pfs/9p-local.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
index 4708e170a4..ee592b62f8 100644
--- a/hw/9pfs/9p-local.c
+++ b/hw/9pfs/9p-local.c
@@ -775,8 +775,11 @@ static int local_fid_fd(int fid_type, V9fsFidOpenState *fs)
{
if (fid_type == P9_FID_DIR) {
return dirfd(fs->dir.stream);
- } else {
+ } else if (fid_type == P9_FID_FILE) {
return fs->fd;
+ } else {
+ errno = EBADF;
+ return -1;
}
}
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 03/23] 9pfs/xen: implement msize_limit callback
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (2 preceding siblings ...)
2026-06-29 13:28 ` [PULL 23/23] hw/9pfs/local: harden local_fid_fd() on FID types Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 10/23] hw/9pfs: add max_xattr option Christian Schoenebeck
` (21 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian,
Stefano Stabellini
Add and implement the msize_limit callback for the Xen transport.
The limit is calculated using XEN_FLEX_RING_SIZE() based on the
negotiated ring_order. For the theoretical maximum ring_order of 9,
this results in a maximum 'msize' of 1048576 bytes (1 MiB).
The minimum limit of all rings is picked, because multiple rings
could theoretically have different ring_orders.
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
Link: https://lore.kernel.org/qemu-devel/9471786bc47b93e822f6c6233a83f2b9f61e6c82.1781287774.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
hw/9pfs/xen-9p-backend.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c
index ca0fff5fa9..e31124bcf5 100644
--- a/hw/9pfs/xen-9p-backend.c
+++ b/hw/9pfs/xen-9p-backend.c
@@ -250,12 +250,31 @@ static void xen_9pfs_push_and_notify(V9fsPDU *pdu)
qemu_bh_schedule(ring->bh);
}
+static size_t xen_9p_msize_limit(V9fsState *s)
+{
+ Xen9pfsDev *xen_9pfs = container_of(s, Xen9pfsDev, state);
+ size_t limit;
+ int i;
+
+ if (!xen_9pfs->num_rings) {
+ return 0;
+ }
+
+ limit = XEN_FLEX_RING_SIZE(xen_9pfs->rings[0].ring_order);
+ for (i = 1; i < xen_9pfs->num_rings; i++) {
+ limit = MIN(limit, XEN_FLEX_RING_SIZE(xen_9pfs->rings[i].ring_order));
+ }
+
+ return limit;
+}
+
static const V9fsTransport xen_9p_transport = {
.pdu_vmarshal = xen_9pfs_pdu_vmarshal,
.pdu_vunmarshal = xen_9pfs_pdu_vunmarshal,
.init_in_iov_from_pdu = xen_9pfs_init_in_iov_from_pdu,
.init_out_iov_from_pdu = xen_9pfs_init_out_iov_from_pdu,
.push_and_notify = xen_9pfs_push_and_notify,
+ .msize_limit = xen_9p_msize_limit,
};
static int xen_9pfs_init(struct XenLegacyDevice *xendev)
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 10/23] hw/9pfs: add max_xattr option
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (3 preceding siblings ...)
2026-06-29 13:28 ` [PULL 03/23] 9pfs/xen: implement msize_limit callback Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 05/23] hw/9pfs: add response_buffer_size transport callback Christian Schoenebeck
` (20 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
Previous patch introduced a limit of max. 1024 simultaneous xattr FIDs.
This patch introduces an option "max_attr" that allows to override this
limit, just for the case that some user might run into this limit for
some reason, even if unlikely; or for reducing the limit further down
(e.g. that default limit of 1024 would cap at max. 64 MiB host memory,
at least on Linux hosts where the limit per xattr is 64k).
This new "max_xattr" option can be specified with both -fsdev and
-virtfs command line options, with the "local" and the "synth" fs
drivers.
The previous limit of 1024 is preserved as the default value.
Link: https://lore.kernel.org/qemu-devel/b7631ac0d8dde0629bc7c4f2c4185d9f57b962b4.1781361555.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
fsdev/file-op-9p.h | 2 ++
fsdev/qemu-fsdev-opts.c | 6 ++++++
fsdev/qemu-fsdev.c | 2 +-
hw/9pfs/9p-local.c | 9 +++++++++
hw/9pfs/9p-synth.c | 17 +++++++++++++++++
hw/9pfs/9p.c | 4 ++--
system/vl.c | 7 ++++++-
7 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h
index 454761d81d..1d931144f4 100644
--- a/fsdev/file-op-9p.h
+++ b/fsdev/file-op-9p.h
@@ -101,6 +101,8 @@ typedef struct FsDriverEntry {
FsThrottle fst;
mode_t fmode;
mode_t dmode;
+ /* temporary storage for parse_opts only */
+ uint32_t max_xattr;
} FsDriverEntry;
struct FsContext {
diff --git a/fsdev/qemu-fsdev-opts.c b/fsdev/qemu-fsdev-opts.c
index 07a18c6e48..c2c1e83611 100644
--- a/fsdev/qemu-fsdev-opts.c
+++ b/fsdev/qemu-fsdev-opts.c
@@ -46,6 +46,9 @@ static QemuOptsList qemu_fsdev_opts = {
}, {
.name = "dmode",
.type = QEMU_OPT_NUMBER,
+ }, {
+ .name = "max_xattr",
+ .type = QEMU_OPT_NUMBER,
},
THROTTLE_OPTS,
@@ -92,6 +95,9 @@ static QemuOptsList qemu_virtfs_opts = {
}, {
.name = "dmode",
.type = QEMU_OPT_NUMBER,
+ }, {
+ .name = "max_xattr",
+ .type = QEMU_OPT_NUMBER,
},
{ /*End of list */ }
diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c
index 57877dad0a..f97103cf44 100644
--- a/fsdev/qemu-fsdev.c
+++ b/fsdev/qemu-fsdev.c
@@ -45,7 +45,7 @@ typedef struct FsDriverListEntry {
static QTAILQ_HEAD(, FsDriverListEntry) fsdriver_entries =
QTAILQ_HEAD_INITIALIZER(fsdriver_entries);
-#define COMMON_FS_DRIVER_OPTIONS "id", "fsdriver", "readonly"
+#define COMMON_FS_DRIVER_OPTIONS "id", "fsdriver", "readonly", "max_xattr"
static FsDriverTable FsDrivers[] = {
{
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
index aa48306b0e..4708e170a4 100644
--- a/hw/9pfs/9p-local.c
+++ b/hw/9pfs/9p-local.c
@@ -1527,6 +1527,15 @@ static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp)
const char *path = qemu_opt_get(opts, "path");
const char *multidevs = qemu_opt_get(opts, "multidevs");
+ uint64_t val = qemu_opt_get_number(opts, "max_xattr",
+ V9FS_MAX_XATTR_DEFAULT);
+ if (val > UINT32_MAX) {
+ error_setg(errp, "max_xattr value '%s' too large",
+ qemu_opt_get(opts, "max_xattr"));
+ return -1;
+ }
+ fse->max_xattr = val;
+
if (!sec_model) {
error_setg(errp, "security_model property not set");
error_append_security_model_hint(errp);
diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c
index b3743f6169..322dc3bb69 100644
--- a/hw/9pfs/9p-synth.c
+++ b/hw/9pfs/9p-synth.c
@@ -25,6 +25,8 @@
#include "qemu/rcu_queue.h"
#include "qemu/cutils.h"
#include "system/qtest.h"
+#include "qapi/error.h"
+#include "qemu/option.h"
/* Root node for synth file system */
static V9fsSynthNode synth_root = {
@@ -629,12 +631,27 @@ static int synth_init(FsContext *ctx, Error **errp)
return 0;
}
+static int synth_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp)
+{
+ uint64_t val = qemu_opt_get_number(opts, "max_xattr",
+ V9FS_MAX_XATTR_DEFAULT);
+ if (val > UINT32_MAX) {
+ error_setg(errp, "max_xattr value '%s' too large",
+ qemu_opt_get(opts, "max_xattr"));
+ return -1;
+ }
+ fse->max_xattr = val;
+
+ return 0;
+}
+
static bool synth_has_valid_file_handle(int fid_type, V9fsFidOpenState *fs)
{
return false;
}
FileOperations synth_ops = {
+ .parse_opts = synth_parse_opts,
.init = synth_init,
.lstat = synth_lstat,
.readlink = synth_readlink,
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index e737629581..d1ec3c0c14 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -4483,8 +4483,8 @@ int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t,
s->reclaiming = false;
- /* init xattr FID limit */
- s->ctx.xattr_fid_limit = V9FS_MAX_XATTR_DEFAULT;
+ /* init xattr FID limit from fsdev config */
+ s->ctx.xattr_fid_limit = fse->max_xattr;
s->ctx.xattr_fid_count = 0;
rc = 0;
diff --git a/system/vl.c b/system/vl.c
index 1c0da7df29..1d14e2e207 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -3260,7 +3260,7 @@ void qemu_init(int argc, char **argv)
QemuOpts *fsdev;
QemuOpts *device;
const char *writeout, *sock_fd, *socket, *path, *security_model,
- *multidevs;
+ *multidevs, *max_xattr_str;
olist = qemu_find_opts("virtfs");
if (!olist) {
@@ -3324,6 +3324,11 @@ void qemu_init(int argc, char **argv)
if (multidevs) {
qemu_opt_set(fsdev, "multidevs", multidevs, &error_abort);
}
+ max_xattr_str = qemu_opt_get(opts, "max_xattr");
+ if (max_xattr_str) {
+ qemu_opt_set(fsdev, "max_xattr", max_xattr_str,
+ &error_abort);
+ }
device = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
&error_abort);
qemu_opt_set(device, "driver", "virtio-9p-pci", &error_abort);
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 05/23] hw/9pfs: add response_buffer_size transport callback
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (4 preceding siblings ...)
2026-06-29 13:28 ` [PULL 10/23] hw/9pfs: add max_xattr option Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 07/23] 9pfs/xen: implement response_buffer_size callback Christian Schoenebeck
` (19 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
Add a new callback to the V9fsTransport interface that allows each transport
to provide the real size of its current response buffer.
This is needed for subsequent safety guards that will limit generated
responses appropriately before trying to allocate, generate, and send a
response to guest.
This is especially required for request handlers that need to allocate
dynamic and potentially large host memory for generating a response. These
safety guards are mandatory to counter bad clients that try to trick server
by supplying response buffers being smaller than the previously negotiated
msize value.
Link: https://lore.kernel.org/qemu-devel/703ed8ce4401c4550ef2cd99f30ab808665d6e85.1781287774.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
hw/9pfs/9p.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h
index d8f364fafd..1a309664f6 100644
--- a/hw/9pfs/9p.h
+++ b/hw/9pfs/9p.h
@@ -482,6 +482,7 @@ struct V9fsTransport {
unsigned int *pniov, size_t size);
void (*push_and_notify)(V9fsPDU *pdu);
size_t (*msize_limit)(V9fsState *s);
+ size_t (*response_buffer_size)(V9fsPDU *pdu);
};
#endif
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 07/23] 9pfs/xen: implement response_buffer_size callback
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (5 preceding siblings ...)
2026-06-29 13:28 ` [PULL 05/23] hw/9pfs: add response_buffer_size transport callback Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 12/23] tests/9p: add Tread / Rread test client functions Christian Schoenebeck
` (18 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian,
Stefano Stabellini
Add and implement the response_buffer_size callback for the Xen
transport.
Returns the size of the response buffer from the rings in_sg, as limit
for 9p server while generating a response for supplied PDU.
We use a local iovec array variable in_sg[2] instead of ring->sg, as
ring->sg is only allocated by init_in_iov_from_pdu() and
init_out_iov_from_pdu() during request / response processing.
response_buffer_size() however may be called before those allocators,
which would dereference ring->sg as NULL pointer. The local array
avoids this.
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
Link: https://lore.kernel.org/qemu-devel/3b139769eb1d3f9d91ee5281228e6467f9a08b99.1781287774.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
hw/9pfs/xen-9p-backend.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c
index e31124bcf5..24c90d97ec 100644
--- a/hw/9pfs/xen-9p-backend.c
+++ b/hw/9pfs/xen-9p-backend.c
@@ -268,6 +268,17 @@ static size_t xen_9p_msize_limit(V9fsState *s)
return limit;
}
+static size_t xen_9pfs_response_buffer_size(V9fsPDU *pdu)
+{
+ Xen9pfsDev *priv = container_of(pdu->s, Xen9pfsDev, state);
+ Xen9pfsRing *ring = &priv->rings[pdu->tag % priv->num_rings];
+ struct iovec in_sg[2];
+ int num;
+
+ xen_9pfs_in_sg(ring, in_sg, &num, pdu->idx, 0);
+ return iov_size(in_sg, num);
+}
+
static const V9fsTransport xen_9p_transport = {
.pdu_vmarshal = xen_9pfs_pdu_vmarshal,
.pdu_vunmarshal = xen_9pfs_pdu_vunmarshal,
@@ -275,6 +286,7 @@ static const V9fsTransport xen_9p_transport = {
.init_out_iov_from_pdu = xen_9pfs_init_out_iov_from_pdu,
.push_and_notify = xen_9pfs_push_and_notify,
.msize_limit = xen_9p_msize_limit,
+ .response_buffer_size = xen_9pfs_response_buffer_size,
};
static int xen_9pfs_init(struct XenLegacyDevice *xendev)
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 12/23] tests/9p: add Tread / Rread test client functions
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (6 preceding siblings ...)
2026-06-29 13:28 ` [PULL 07/23] 9pfs/xen: implement response_buffer_size callback Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 18/23] tests/9p: add virtio_9p_add_synth_driver_args() test client function Christian Schoenebeck
` (17 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
Add v9fs_tread() and v9fs_rread() functions to the 9P test client
for reading files from 9pfs server in test cases.
Link: https://lore.kernel.org/qemu-devel/049bdd1e66416f5200fb3d59d2da5e8ec149926d.1781361555.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
tests/qtest/libqos/virtio-9p-client.c | 45 +++++++++++++++++++++++++++
tests/qtest/libqos/virtio-9p-client.h | 33 ++++++++++++++++++++
tests/qtest/virtio-9p-test.c | 1 +
3 files changed, 79 insertions(+)
diff --git a/tests/qtest/libqos/virtio-9p-client.c b/tests/qtest/libqos/virtio-9p-client.c
index af01d4c345..b9785ef8cb 100644
--- a/tests/qtest/libqos/virtio-9p-client.c
+++ b/tests/qtest/libqos/virtio-9p-client.c
@@ -241,6 +241,7 @@ static const char *rmessage_name(uint8_t id)
id == P9_RUNLINKAT ? "RUNLINKAT" :
id == P9_RFLUSH ? "RFLUSH" :
id == P9_RREADDIR ? "RREADDIR" :
+ id == P9_RREAD ? "RREAD" :
"<unknown>";
}
@@ -1103,3 +1104,47 @@ void v9fs_runlinkat(P9Req *req)
v9fs_req_recv(req, P9_RUNLINKAT);
v9fs_req_free(req);
}
+
+/* size[4] Tread tag[2] fid[4] offset[8] count[4] */
+TReadRes v9fs_tread(TReadOpt opt)
+{
+ P9Req *req;
+ uint32_t err;
+
+ g_assert(opt.client);
+
+ uint32_t body_size = 4 + 8 + 4;
+
+ req = v9fs_req_init(opt.client, body_size, P9_TREAD, opt.tag);
+ v9fs_uint32_write(req, opt.fid);
+ v9fs_uint64_write(req, opt.offset);
+ v9fs_uint32_write(req, opt.count);
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rread(req, opt.rread.count, opt.rread.data);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TReadRes) {
+ .req = req,
+ .count = opt.rread.count ? *opt.rread.count : 0
+ };
+}
+
+/* size[4] Rread tag[2] count[4] data[count] */
+void v9fs_rread(P9Req *req, uint32_t *count, void *data)
+{
+ v9fs_req_recv(req, P9_RREAD);
+ v9fs_uint32_read(req, count);
+ if (data && *count > 0) {
+ v9fs_memread(req, data, *count);
+ }
+ v9fs_req_free(req);
+}
diff --git a/tests/qtest/libqos/virtio-9p-client.h b/tests/qtest/libqos/virtio-9p-client.h
index e3221a3104..37f2517cff 100644
--- a/tests/qtest/libqos/virtio-9p-client.h
+++ b/tests/qtest/libqos/virtio-9p-client.h
@@ -473,6 +473,37 @@ typedef struct TunlinkatRes {
P9Req *req;
} TunlinkatRes;
+/* options for 'Tread' 9p request */
+typedef struct TReadOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* file ID of file to read from (required) */
+ uint32_t fid;
+ /* start position of read from beginning of file (optional) */
+ uint64_t offset;
+ /* how many bytes to read (required) */
+ uint32_t count;
+ /* data being received from 9p server as 'Rread' response (optional) */
+ struct {
+ uint32_t *count;
+ void *data;
+ } rread;
+ /* only send Tread request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TReadOpt;
+
+/* result of 'Tread' 9p request */
+typedef struct TReadRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+ /* amount of bytes read */
+ uint32_t count;
+} TReadRes;
+
void v9fs_set_allocator(QGuestAllocator *t_alloc);
void v9fs_memwrite(P9Req *req, const void *addr, size_t len);
void v9fs_memskip(P9Req *req, size_t len);
@@ -524,5 +555,7 @@ TlinkRes v9fs_tlink(TlinkOpt);
void v9fs_rlink(P9Req *req);
TunlinkatRes v9fs_tunlinkat(TunlinkatOpt);
void v9fs_runlinkat(P9Req *req);
+TReadRes v9fs_tread(TReadOpt opt);
+void v9fs_rread(P9Req *req, uint32_t *count, void *data);
#endif
diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c
index 1c69d41e33..8ccec77e70 100644
--- a/tests/qtest/virtio-9p-test.c
+++ b/tests/qtest/virtio-9p-test.c
@@ -31,6 +31,7 @@
#define tsymlink(...) v9fs_tsymlink((TsymlinkOpt) __VA_ARGS__)
#define tlink(...) v9fs_tlink((TlinkOpt) __VA_ARGS__)
#define tunlinkat(...) v9fs_tunlinkat((TunlinkatOpt) __VA_ARGS__)
+#define tread(...) v9fs_tread((TReadOpt) __VA_ARGS__)
static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
{
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 18/23] tests/9p: add virtio_9p_add_synth_driver_args() test client function
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (7 preceding siblings ...)
2026-06-29 13:28 ` [PULL 12/23] tests/9p: add Tread / Rread test client functions Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 14/23] tests/9p: add Txattrcreate / Rxattrcreate test client functions Christian Schoenebeck
` (16 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
Add virtio_9p_add_synth_driver_args() to allow appending custom
QEMU options for individual 9p synth tests.
Link: https://lore.kernel.org/qemu-devel/7fe3eca5d17292464676b68d0513052564cd432a.1781361555.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
tests/qtest/libqos/virtio-9p.c | 6 ++++++
tests/qtest/libqos/virtio-9p.h | 6 ++++++
2 files changed, 12 insertions(+)
diff --git a/tests/qtest/libqos/virtio-9p.c b/tests/qtest/libqos/virtio-9p.c
index 186fcc1141..823756de8c 100644
--- a/tests/qtest/libqos/virtio-9p.c
+++ b/tests/qtest/libqos/virtio-9p.c
@@ -228,6 +228,12 @@ static void regex_replace(GString *haystack, const char *pattern,
g_string_assign(haystack, s);
}
+void virtio_9p_add_synth_driver_args(GString *cmd_line, const char *args)
+{
+ /* append passed args to '-fsdev ...' group */
+ regex_replace(cmd_line, "(-fsdev \\w[^ ]*)", "\\1,%s", args);
+}
+
void virtio_9p_assign_local_driver(GString *cmd_line, const char *args)
{
g_assert_nonnull(local_test_path);
diff --git a/tests/qtest/libqos/virtio-9p.h b/tests/qtest/libqos/virtio-9p.h
index 480727120e..e7efeef7a1 100644
--- a/tests/qtest/libqos/virtio-9p.h
+++ b/tests/qtest/libqos/virtio-9p.h
@@ -44,6 +44,12 @@ struct QVirtio9PDevice {
QVirtio9P v9p;
};
+/**
+ * Add required test specific args to the QEMU command line for the 9pfs
+ * 'synth' fs driver.
+ */
+void virtio_9p_add_synth_driver_args(GString *cmd_line, const char *args);
+
/**
* Creates the directory for the 9pfs 'local' filesystem driver to access.
*/
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 14/23] tests/9p: add Txattrcreate / Rxattrcreate test client functions
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (8 preceding siblings ...)
2026-06-29 13:28 ` [PULL 18/23] tests/9p: add virtio_9p_add_synth_driver_args() test client function Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 21/23] hw/9pfs: fix invalid union access by v9fs_co_fsync() Christian Schoenebeck
` (15 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
Add v9fs_txattrcreate() and v9fs_rxattrcreate() functions to the
9P test client for testing creation of xattrs with 9pfs server.
Link: https://lore.kernel.org/qemu-devel/5dbc5061dab1f7829fffc40bf89d7ff443e4bcab.1781361555.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
tests/qtest/libqos/virtio-9p-client.c | 45 +++++++++++++++++++++++++++
tests/qtest/libqos/virtio-9p-client.h | 30 ++++++++++++++++++
tests/qtest/virtio-9p-test.c | 1 +
3 files changed, 76 insertions(+)
diff --git a/tests/qtest/libqos/virtio-9p-client.c b/tests/qtest/libqos/virtio-9p-client.c
index 83af6dab5d..305b0dac63 100644
--- a/tests/qtest/libqos/virtio-9p-client.c
+++ b/tests/qtest/libqos/virtio-9p-client.c
@@ -243,6 +243,7 @@ static const char *rmessage_name(uint8_t id)
id == P9_RREADDIR ? "RREADDIR" :
id == P9_RREAD ? "RREAD" :
id == P9_RCLUNK ? "RCLUNK" :
+ id == P9_RXATTRCREATE ? "RXATTRCREATE" :
"<unknown>";
}
@@ -1182,3 +1183,47 @@ void v9fs_rclunk(P9Req *req)
v9fs_req_recv(req, P9_RCLUNK);
v9fs_req_free(req);
}
+
+/* size[4] Txattrcreate tag[2] fid[4] name[s] attr_size[8] flags[4] */
+TXattrCreateRes v9fs_txattrcreate(TXattrCreateOpt opt)
+{
+ P9Req *req;
+ uint32_t err;
+
+ g_assert(opt.client);
+ g_assert(opt.name);
+
+ uint32_t body_size = 4 + 8 + 4;
+ uint16_t string_size = v9fs_string_size(opt.name);
+
+ g_assert_cmpint(body_size, <=, UINT32_MAX - string_size);
+ body_size += string_size;
+
+ req = v9fs_req_init(opt.client, body_size, P9_TXATTRCREATE, opt.tag);
+ v9fs_uint32_write(req, opt.fid);
+ v9fs_string_write(req, opt.name);
+ v9fs_uint64_write(req, opt.size);
+ v9fs_uint32_write(req, opt.flags);
+ v9fs_req_send(req);
+
+ err = 0;
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rxattrcreate(req);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TXattrCreateRes) { .req = req, .err = err };
+}
+
+/* size[4] Rxattrcreate tag[2] */
+void v9fs_rxattrcreate(P9Req *req)
+{
+ v9fs_req_recv(req, P9_RXATTRCREATE);
+ v9fs_req_free(req);
+}
diff --git a/tests/qtest/libqos/virtio-9p-client.h b/tests/qtest/libqos/virtio-9p-client.h
index f7ef7f0067..c432b0daee 100644
--- a/tests/qtest/libqos/virtio-9p-client.h
+++ b/tests/qtest/libqos/virtio-9p-client.h
@@ -524,6 +524,34 @@ typedef struct TClunkRes {
P9Req *req;
} TClunkRes;
+/* options for 'Txattrcreate' 9p request */
+typedef struct TXattrCreateOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* file ID to convert to xattr fid (required) */
+ uint32_t fid;
+ /* name of the xattr (required) */
+ const char *name;
+ /* size of the xattr value (required) */
+ uint64_t size;
+ /* flags: P9_XATTR_CREATE or P9_XATTR_REPLACE (optional) */
+ uint32_t flags;
+ /* only send Txattrcreate request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TXattrCreateOpt;
+
+/* result of 'Txattrcreate' 9p request */
+typedef struct TXattrCreateRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+ /* error code if Rlerror received */
+ uint32_t err;
+} TXattrCreateRes;
+
void v9fs_set_allocator(QGuestAllocator *t_alloc);
void v9fs_memwrite(P9Req *req, const void *addr, size_t len);
void v9fs_memskip(P9Req *req, size_t len);
@@ -579,5 +607,7 @@ TReadRes v9fs_tread(TReadOpt opt);
void v9fs_rread(P9Req *req, uint32_t *count, void *data);
TClunkRes v9fs_tclunk(TClunkOpt opt);
void v9fs_rclunk(P9Req *req);
+TXattrCreateRes v9fs_txattrcreate(TXattrCreateOpt opt);
+void v9fs_rxattrcreate(P9Req *req);
#endif
diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c
index 099c5e0ca0..99a897b158 100644
--- a/tests/qtest/virtio-9p-test.c
+++ b/tests/qtest/virtio-9p-test.c
@@ -33,6 +33,7 @@
#define tunlinkat(...) v9fs_tunlinkat((TunlinkatOpt) __VA_ARGS__)
#define tread(...) v9fs_tread((TReadOpt) __VA_ARGS__)
#define tclunk(...) v9fs_tclunk((TClunkOpt) __VA_ARGS__)
+#define txattrcreate(...) v9fs_txattrcreate((TXattrCreateOpt) __VA_ARGS__)
static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
{
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 21/23] hw/9pfs: fix invalid union access by v9fs_co_fsync()
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (9 preceding siblings ...)
2026-06-29 13:28 ` [PULL 14/23] tests/9p: add Txattrcreate / Rxattrcreate test client functions Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 01/23] hw/9pfs: add msize_limit transport callback Christian Schoenebeck
` (14 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
The individual FID types (P9_FID_NONE, P9_FID_FILE, P9_FID_DIR, P9_FID_XATTR)
share union V9fsFidOpenState with FID-type specific fields. Accessing any of
the union fields must comply with the FID-type to avoid undefined behaviour
or information disclosure.
Fix this in v9fs_fsync() and v9fs_wstat() by checking if FID has a valid file
descriptor before calling v9fs_co_fsync().
Fixes: 10b468bdc533 ("virtio-9p: Implement TXATTRCREATE")
Reported-by: Feifan Qian <bea1e@proton.me>
Link: https://lore.kernel.org/qemu-devel/b583e29d5a0776e41263732c93ac9f0da0a6016d.1781621428.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
hw/9pfs/9p.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index d1ec3c0c14..a2b7335515 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -2305,10 +2305,15 @@ static void coroutine_fn v9fs_fsync(void *opaque)
err = -ENOENT;
goto out_nofid;
}
+ if (!fid_has_valid_file_handle(pdu->s, fidp)) {
+ err = -EBADF;
+ goto out;
+ }
err = v9fs_co_fsync(pdu, fidp, datasync);
if (!err) {
err = offset;
}
+out:
put_fid(pdu, fidp);
out_nofid:
pdu_complete(pdu, err);
@@ -3640,6 +3645,10 @@ static void coroutine_fn v9fs_wstat(void *opaque)
}
/* do we need to sync the file? */
if (donttouch_stat(&v9stat)) {
+ if (!fid_has_valid_file_handle(s, fidp)) {
+ err = -EBADF;
+ goto out;
+ }
err = v9fs_co_fsync(pdu, fidp, 0);
goto out;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 01/23] hw/9pfs: add msize_limit transport callback
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (10 preceding siblings ...)
2026-06-29 13:28 ` [PULL 21/23] hw/9pfs: fix invalid union access by v9fs_co_fsync() Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 22/23] hw/9pfs: fix invalid union access by v9fs_co_fstat() Christian Schoenebeck
` (13 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
Add a new callback 'msize_limit' to the V9fsTransport structure.
This allows each transport implementation to provide its theoretical
maximum 'msize' value, which will be used to cap the negotiated
msize during Tversion handshake.
Link: https://lore.kernel.org/qemu-devel/7c4e53eb73c0580d7a321dbf3823ba5647652298.1781287774.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
hw/9pfs/9p.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h
index b2df659b0e..d8f364fafd 100644
--- a/hw/9pfs/9p.h
+++ b/hw/9pfs/9p.h
@@ -481,6 +481,7 @@ struct V9fsTransport {
void (*init_out_iov_from_pdu)(V9fsPDU *pdu, struct iovec **piov,
unsigned int *pniov, size_t size);
void (*push_and_notify)(V9fsPDU *pdu);
+ size_t (*msize_limit)(V9fsState *s);
};
#endif
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 22/23] hw/9pfs: fix invalid union access by v9fs_co_fstat()
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (11 preceding siblings ...)
2026-06-29 13:28 ` [PULL 01/23] hw/9pfs: add msize_limit transport callback Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 11/23] qemu-options: document 9pfs max_xattr option Christian Schoenebeck
` (12 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
The individual FID types (P9_FID_NONE, P9_FID_FILE, P9_FID_DIR, P9_FID_XATTR)
share union V9fsFidOpenState with FID-type specific fields. Accessing any of
the union fields must comply with the FID-type to avoid undefined behaviour
or information disclosure.
Fix this in v9fs_lock() and v9fs_getlock() by checking if FID has a valid
file descriptor before calling v9fs_co_fstat().
Fixes: 10b468bdc533 ("virtio-9p: Implement TXATTRCREATE")
Link: https://lore.kernel.org/qemu-devel/4b33cd1aaa2551efda220a6f651e3660d27f4746.1781621428.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
hw/9pfs/9p.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index a2b7335515..3119f01117 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -3908,6 +3908,10 @@ static void coroutine_fn v9fs_lock(void *opaque)
err = -ENOENT;
goto out_nofid;
}
+ if (!fid_has_valid_file_handle(pdu->s, fidp)) {
+ err = -EBADF;
+ goto out;
+ }
err = v9fs_co_fstat(pdu, fidp, &stbuf);
if (err < 0) {
goto out;
@@ -3953,6 +3957,10 @@ static void coroutine_fn v9fs_getlock(void *opaque)
err = -ENOENT;
goto out_nofid;
}
+ if (!fid_has_valid_file_handle(pdu->s, fidp)) {
+ err = -EBADF;
+ goto out;
+ }
err = v9fs_co_fstat(pdu, fidp, &stbuf);
if (err < 0) {
goto out;
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 11/23] qemu-options: document 9pfs max_xattr option
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (12 preceding siblings ...)
2026-06-29 13:28 ` [PULL 22/23] hw/9pfs: fix invalid union access by v9fs_co_fstat() Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 08/23] hw/9pfs: cap Treaddir allocation (CVE-2026-9238) Christian Schoenebeck
` (11 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
Add documentation for the new "max_xattr" command line option
of 9pfs server, introduced by the previous commit.
Link: https://lore.kernel.org/qemu-devel/b5a1a6ba299a49183d0032d9e4cd5e009d4aae47.1781361555.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
qemu-options.hx | 28 ++++++++++++++++++++--------
1 file changed, 20 insertions(+), 8 deletions(-)
diff --git a/qemu-options.hx b/qemu-options.hx
index f1874a0591..e44b47de68 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1940,19 +1940,19 @@ ERST
DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev,
"-fsdev local,id=id,path=path,security_model=mapped-xattr|mapped-file|passthrough|none\n"
- " [,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode]\n"
+ " [,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode][,max_xattr=max]\n"
" [[,throttling.bps-total=b]|[[,throttling.bps-read=r][,throttling.bps-write=w]]]\n"
" [[,throttling.iops-total=i]|[[,throttling.iops-read=r][,throttling.iops-write=w]]]\n"
" [[,throttling.bps-total-max=bm]|[[,throttling.bps-read-max=rm][,throttling.bps-write-max=wm]]]\n"
" [[,throttling.iops-total-max=im]|[[,throttling.iops-read-max=irm][,throttling.iops-write-max=iwm]]]\n"
" [[,throttling.iops-size=is]]\n"
- "-fsdev synth,id=id\n",
+ "-fsdev synth,id=id[,max_xattr=max]\n",
QEMU_ARCH_ALL)
SRST
-``-fsdev local,id=id,path=path,security_model=security_model [,writeout=writeout][,readonly=on][,fmode=fmode][,dmode=dmode] [,throttling.option=value[,throttling.option=value[,...]]]``
+``-fsdev local,id=id,path=path,security_model=security_model [,writeout=writeout][,readonly=on][,fmode=fmode][,dmode=dmode][,max_xattr=max] [,throttling.option=value[,throttling.option=value[,...]]]``
\
-``-fsdev synth,id=id[,readonly=on]``
+``-fsdev synth,id=id[,readonly=on][,max_xattr=max]``
Define a new file system device. Valid options are:
``local``
@@ -2026,6 +2026,12 @@ SRST
Let every is bytes of a request count as a new request for iops
throttling purposes.
+ ``max_xattr=max``
+ Specifies the maximum number of concurrent xattr FIDs allowed for
+ this export. The default is 1024. Set to 0 for allowing an infinite
+ number of xattr FIDs. This limit prevents host memory exhaustion
+ attacks by capping the number of simultaneous xattr FIDs.
+
-fsdev option is used along with -device driver "virtio-9p-...".
``-device virtio-9p-type,fsdev=id,mount_tag=mount_tag``
@@ -2045,14 +2051,14 @@ ERST
DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs,
"-virtfs local,path=path,mount_tag=tag,security_model=mapped-xattr|mapped-file|passthrough|none\n"
- " [,id=id][,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode][,multidevs=remap|forbid|warn]\n"
- "-virtfs synth,mount_tag=tag[,id=id][,readonly=on]\n",
+ " [,id=id][,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode][,multidevs=remap|forbid|warn][,max_xattr=max]\n"
+ "-virtfs synth,mount_tag=tag[,id=id][,readonly=on][,max_xattr=max]\n",
QEMU_ARCH_ALL)
SRST
-``-virtfs local,path=path,mount_tag=mount_tag ,security_model=security_model[,writeout=writeout][,readonly=on] [,fmode=fmode][,dmode=dmode][,multidevs=multidevs]``
+``-virtfs local,path=path,mount_tag=mount_tag ,security_model=security_model[,writeout=writeout][,readonly=on] [,fmode=fmode][,dmode=dmode][,multidevs=multidevs][,max_xattr=max]``
\
-``-virtfs synth,mount_tag=mount_tag``
+``-virtfs synth,mount_tag=mount_tag[,max_xattr=max]``
Define a new virtual filesystem device and expose it to the guest using
a virtio-9p-device (a.k.a. 9pfs), which essentially means that a certain
directory on host is made directly accessible by guest as a pass-through
@@ -2118,6 +2124,12 @@ SRST
Specifies the tag name to be used by the guest to mount this
export point.
+ ``max_xattr=max``
+ Specifies the maximum number of concurrent xattr FIDs allowed for
+ this export. The default is 1024. Set to 0 for allowing an infinite
+ number of xattr FIDs. This limit prevents host memory exhaustion
+ attacks by capping the number of simultaneous xattr FIDs.
+
``multidevs=remap|forbid|warn``
Specifies how to deal with multiple devices being shared with
the same 9p export in order to avoid file ID collisions on guest.
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 08/23] hw/9pfs: cap Treaddir allocation (CVE-2026-9238)
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (13 preceding siblings ...)
2026-06-29 13:28 ` [PULL 11/23] qemu-options: document 9pfs max_xattr option Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 20/23] tests/9p: add 3 xattr FID limit test cases (local fs driver) Christian Schoenebeck
` (10 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian,
Stefano Stabellini
Constrain max_count in v9fs_readdir() to transport's current, real
response buffer size before calling v9fs_do_readdir() to prevent
excessive host memory allocation for specific, crafted, huge
directories (large amount of entries) by bad clients.
Client may send a Treaddir request with a large 'count' parameter, and
while the negotiated 'msize' provides some limit, it accounts for guest
being somewhat faithful on the negotiated 'msize' value throughout the
session.
A bad guest client could have negotiated a large 'msize' but provide a
small reply buffer for Treaddir request, causing QEMU to allocate host
memory proportional to 'msize' before discovering the reply cannot fit.
Possible consequence was a potential DoS by a priviliged guest, causing
a disconnection of guest communication due to transport device being
marked as "broken", however QEMU process would have continued to run with
potentially giant host memory allocation, which might have negative
impact on other services running on host.
Fixes: CVE-2026-9238
Fixes: 2149675b195f ("9pfs: add new function v9fs_co_readdir_many()")
Reported-by: Feifan Qian <bea1e@proton.me>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
Link: https://lore.kernel.org/qemu-devel/f81a387a2de4f2172fd5830c5654f49d78102254.1781287774.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
hw/9pfs/9p.c | 24 ++++++++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index 81d9bb8c6d..09454a5404 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -2679,6 +2679,7 @@ static void coroutine_fn v9fs_readdir(void *opaque)
uint32_t max_count;
V9fsPDU *pdu = opaque;
V9fsState *s = pdu->s;
+ size_t max_resp_sz;
retval = pdu_unmarshal(pdu, offset, "dqd", &fid,
&initial_offset, &max_count);
@@ -2687,9 +2688,28 @@ static void coroutine_fn v9fs_readdir(void *opaque)
}
trace_v9fs_readdir(pdu->tag, pdu->id, fid, initial_offset, max_count);
+ max_resp_sz = s->msize;
+
+ /*
+ * Constrain max_count to transport's current, actual response buffer size.
+ * A bad client might provide a response buffer < msize.
+ */
+ if (s->transport->response_buffer_size) {
+ size_t buf_size = s->transport->response_buffer_size(pdu);
+ if (max_resp_sz > buf_size) {
+ max_resp_sz = buf_size;
+ }
+ }
+
/* Enough space for a R_readdir header: size[4] Rreaddir tag[2] count[4] */
- if (max_count > s->msize - 11) {
- max_count = s->msize - 11;
+ if (max_resp_sz > 11) {
+ max_resp_sz -= 11;
+ } else {
+ max_resp_sz = 0;
+ }
+
+ if (max_count > max_resp_sz) {
+ max_count = max_resp_sz;
warn_report_once(
"9p: bad client: T_readdir with count > msize - 11"
);
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 20/23] tests/9p: add 3 xattr FID limit test cases (local fs driver)
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (14 preceding siblings ...)
2026-06-29 13:28 ` [PULL 08/23] hw/9pfs: cap Treaddir allocation (CVE-2026-9238) Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 16/23] hw/9pfs: add xattr count query interface to fs synth driver Christian Schoenebeck
` (9 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
Analogue to the previously added 3 synth tests, add (similar) 3 test
cases using the "local" fs driver to verify correct xattr FID limit
enforcement of 9pfs server with a real filesystem.
These 3 new local tests use the shared test code of the previously
added 3 synth tests. The only difference is that the local fs driver
does not expose the current internal xattr FID counter, so we can't
verify this with the local tests.
This is a slow test (may take several seconds) and therefore
registered as "slow" test and not running by default.
Use -m slow to run this test.
Link: https://lore.kernel.org/qemu-devel/d23fa874df4f474ee7cbe738a35c1483426057f0.1781361555.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
tests/qtest/virtio-9p-test.c | 70 ++++++++++++++++++++++++++++++++++--
1 file changed, 67 insertions(+), 3 deletions(-)
diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c
index 2e88429dfa..cfd3c02da4 100644
--- a/tests/qtest/virtio-9p-test.c
+++ b/tests/qtest/virtio-9p-test.c
@@ -395,6 +395,19 @@ static void do_xattr_limit(QVirtio9P *v9p, int max_xattr, bool check_counter)
}
}
+static void do_local_xattr_limit(QVirtio9P *v9p, int max_xattr)
+{
+ g_autofree char *test_file = virtio_9p_test_path("WRITE");
+
+ /*
+ * this file must be created for the test to work with the 'local' fs driver
+ */
+ g_file_set_contents(test_file, "", 0, NULL);
+
+ /* the actual test code shared with the 'synth' fs driver tests */
+ do_xattr_limit(v9p, max_xattr, false);
+}
+
static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
@@ -990,6 +1003,27 @@ static void fs_deep_absolute_path(void *obj, void *data,
g_string_free(path, TRUE);
}
+static void fs_local_xattr_limit_default(void *obj, void *data,
+ QGuestAllocator *t_alloc)
+{
+ v9fs_set_allocator(t_alloc);
+ do_local_xattr_limit(obj, V9FS_MAX_XATTR_DEFAULT);
+}
+
+static void fs_local_xattr_limit_custom(void *obj, void *data,
+ QGuestAllocator *t_alloc)
+{
+ v9fs_set_allocator(t_alloc);
+ do_local_xattr_limit(obj, 100);
+}
+
+static void fs_local_xattr_limit_unlimited(void *obj, void *data,
+ QGuestAllocator *t_alloc)
+{
+ v9fs_set_allocator(t_alloc);
+ do_local_xattr_limit(obj, -1);
+}
+
static void *synth_max_xattr_custom_opt(GString *cmd_line, void *arg)
{
virtio_9p_add_synth_driver_args(cmd_line, "max_xattr=100");
@@ -1008,20 +1042,42 @@ static void cleanup_9p_local_driver(void *data)
virtio_9p_remove_local_test_dir();
}
-static void *assign_9p_local_driver(GString *cmd_line, void *arg)
+static void assign_9p_local_driver_with_args(GString *cmd_line,
+ const char *extra_opts)
{
/* make sure test dir for the 'local' tests exists */
virtio_9p_create_local_test_dir();
- virtio_9p_assign_local_driver(cmd_line, "security_model=mapped-xattr");
+ g_autofree char *opts =
+ (extra_opts) ?
+ g_strdup_printf("security_model=mapped-xattr,%s", extra_opts) :
+ g_strdup("security_model=mapped-xattr");
+
+ virtio_9p_assign_local_driver(cmd_line, opts);
g_test_queue_destroy(cleanup_9p_local_driver, NULL);
+}
+
+static void *assign_9p_local_driver(GString *cmd_line, void *arg)
+{
+ assign_9p_local_driver_with_args(cmd_line, NULL);
+ return arg;
+}
+
+static void *local_max_xattr_custom_opt(GString *cmd_line, void *arg)
+{
+ assign_9p_local_driver_with_args(cmd_line, "max_xattr=100");
+ return arg;
+}
+
+static void *local_max_xattr_unlimited_opt(GString *cmd_line, void *arg)
+{
+ assign_9p_local_driver_with_args(cmd_line, "max_xattr=0");
return arg;
}
static void register_virtio_9p_test(void)
{
-
QOSGraphTestOptions opts = {
};
@@ -1078,6 +1134,14 @@ static void register_virtio_9p_test(void)
&opts);
qos_add_test("local/deep_absolute_path", "virtio-9p",
fs_deep_absolute_path, &opts);
+ qos_add_test("local/xattr_limit/default", "virtio-9p",
+ fs_local_xattr_limit_default, &opts);
+ opts.before = local_max_xattr_custom_opt;
+ qos_add_test("local/xattr_limit/custom", "virtio-9p",
+ fs_local_xattr_limit_custom, &opts);
+ opts.before = local_max_xattr_unlimited_opt;
+ qos_add_test("local/xattr_limit/unlimited", "virtio-9p",
+ fs_local_xattr_limit_unlimited, &opts);
}
libqos_init(register_virtio_9p_test);
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 16/23] hw/9pfs: add xattr count query interface to fs synth driver
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (15 preceding siblings ...)
2026-06-29 13:28 ` [PULL 20/23] tests/9p: add 3 xattr FID limit test cases (local fs driver) Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 19/23] tests/9p: add 3 xattr FID limit test cases (synth fs driver) Christian Schoenebeck
` (8 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
Add a synthetic "/stat/xattr_count" file path that, if being read
by 9p client, returns the 9p server internal xattr FID counter to
client.
This allows to test and verify that the xattr FID limit is being
enforced correctly.
Link: https://lore.kernel.org/qemu-devel/357c20fc244c04dcbd36b13aa20a5c9b2034e9f0.1781361555.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
hw/9pfs/9p-synth.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c
index 4b0732e093..3b3654b282 100644
--- a/hw/9pfs/9p-synth.c
+++ b/hw/9pfs/9p-synth.c
@@ -565,6 +565,19 @@ static ssize_t v9fs_synth_qtest_flush_write(void *buf, int len, off_t offset,
return 1;
}
+/* transmits internal xattr counter to client */
+static ssize_t v9fs_synth_read_xattr_count(void *buf, int len, off_t offset,
+ void *arg)
+{
+ FsContext *ctx = arg;
+ size_t local_count = ctx->xattr_fid_count;
+ if (len < (int)sizeof(size_t)) {
+ return -ENOSPC;
+ }
+ memcpy(buf, &local_count, sizeof(size_t));
+ return sizeof(size_t);
+}
+
static int synth_init(FsContext *ctx, Error **errp)
{
QLIST_INIT(&synth_root.child);
@@ -626,6 +639,19 @@ static int synth_init(FsContext *ctx, Error **errp)
g_free(name);
}
}
+
+ /* Directory for internal statistic queries */
+ {
+ V9fsSynthNode *stat_dir = NULL;
+ ret = qemu_v9fs_synth_mkdir(NULL, 0755, "stat", &stat_dir);
+ assert(!ret);
+
+ /* File for internal xattr count query */
+ ret = qemu_v9fs_synth_add_file(stat_dir, 0444, "xattr_count",
+ v9fs_synth_read_xattr_count,
+ NULL, ctx);
+ assert(!ret);
+ }
}
return 0;
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 19/23] tests/9p: add 3 xattr FID limit test cases (synth fs driver)
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (16 preceding siblings ...)
2026-06-29 13:28 ` [PULL 16/23] hw/9pfs: add xattr count query interface to fs synth driver Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 02/23] 9pfs/virtio: implement msize_limit callback Christian Schoenebeck
` (7 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
Add 3 test cases to verify correct xattr FID limit enforcement of
9pfs server.
- 1. test with default max_xattr=1024
- 2. test with custom max_xattr=100
- 3. test with unlimited max_xattr=0
These are tests using the synth driver. Advantage: by using the
synth driver the tests cannot only check when the xattr FID limit
kicks in (server would return an Rlerror response with ENOSPC),
but can also validate the current 9p server internal xattr FID
counter at any moment.
This is a slow test (may take several seconds) and therefore
registered as "slow" test and not running by default.
Use -m slow to run this test.
Link: https://lore.kernel.org/qemu-devel/540c51faa074d9dd736bbf2170084a12288e23ef.1781361555.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
tests/qtest/virtio-9p-test.c | 189 ++++++++++++++++++++++++++++++++++-
1 file changed, 188 insertions(+), 1 deletion(-)
diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c
index 99a897b158..2e88429dfa 100644
--- a/tests/qtest/virtio-9p-test.c
+++ b/tests/qtest/virtio-9p-test.c
@@ -35,6 +35,15 @@
#define tclunk(...) v9fs_tclunk((TClunkOpt) __VA_ARGS__)
#define txattrcreate(...) v9fs_txattrcreate((TXattrCreateOpt) __VA_ARGS__)
+/*
+ * xattr size to be used for xattr tests
+ *
+ * 64k is the max. xattr size supported by the Linux kernel, However btrfs
+ * for instance supports only 16219 bytes. So let's be conservative and
+ * just use 8k for the xattr tests.
+ */
+#define TEST_XATTR_SIZE (8 * 1024)
+
static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
@@ -107,6 +116,42 @@ static bool fs_dirents_contain_name(struct V9fsDirent *e, const char* name)
return false;
}
+/*
+ * Returns the current internal xattr FID count (works with synth driver only).
+ */
+static size_t get_xattr_count(QVirtio9P *v9p)
+{
+ uint16_t nwqid;
+ v9fs_qid *wqid;
+ const char *xattr_count_path[] = { "stat", "xattr_count" };
+ size_t xattr_count;
+ uint32_t bytes_read;
+
+ /* walk to /stat/xattr_count file */
+ uint32_t fid = twalk({
+ .client = v9p, .fid = 0,
+ .nwname = 2, .wnames = (char **)xattr_count_path,
+ .rwalk = { .nwqid = &nwqid, .wqid = &wqid }
+ }).newfid;
+
+ /* open for read */
+ tlopen({
+ .client = v9p, .fid = fid, .flags = O_RDONLY,
+ .rlopen = { .qid = NULL, .iounit = NULL }
+ });
+
+ /* read the internal xattr FID count */
+ tread({
+ .client = v9p, .fid = fid, .offset = 0, .count = sizeof(xattr_count),
+ .rread = { .count = &bytes_read, .data = &xattr_count }
+ });
+
+ /* cleanup */
+ tclunk({ .client = v9p, .fid = fid });
+
+ return xattr_count;
+}
+
/* basic readdir test where reply fits into a single response message */
static void fs_readdir(void *obj, void *data, QGuestAllocator *t_alloc)
{
@@ -248,6 +293,108 @@ static void do_readdir_split(QVirtio9P *v9p, uint32_t count)
g_free(wnames[0]);
}
+/*
+ * Test 9p server's xattr FID count limit enforcement.
+ *
+ * Shared test code for both 'synth' and 'local' driver to verify correct
+ * behaviour of 9p server enforcing preconfigured xattr FID count limit
+ * correctly.
+ *
+ * @v9p: 9pfs client
+ *
+ * @max_xattr: max. allowed xattr FIDs, or -1 for infinite
+ *
+ * @check_counter: whether to verify 9p server internal xattr FID counter
+ * (only works with 'synth' fs driver)
+ */
+static void do_xattr_limit(QVirtio9P *v9p, int max_xattr, bool check_counter)
+{
+ size_t count;
+ int i;
+ int limit = (max_xattr != -1) ? max_xattr : V9FS_MAX_XATTR_DEFAULT + 100;
+ g_autofree uint32_t *fids = g_new0(uint32_t, limit);
+ uint32_t err_fid = 0;
+ const char *file_path[] = { QTEST_V9FS_SYNTH_WRITE_FILE };
+ g_autofree uint8_t *xattr_data = g_malloc(TEST_XATTR_SIZE);
+
+ if (!g_test_slow()) {
+ g_test_skip("This is a slow test, run with -m slow");
+ return;
+ }
+
+ /* prepare xattr data with 'X' characters */
+ memset(xattr_data, 'X', TEST_XATTR_SIZE);
+
+ tattach({ .client = v9p });
+
+ /* create max. amount of permitted xattrs */
+ for (i = 0; i < limit; i++) {
+ /* walk to create a new fid */
+ fids[i] = twalk({
+ .client = v9p, .fid = 0,
+ .nwname = 1, .wnames = (char **) file_path
+ }).newfid;
+
+ /* create new xattr fid */
+ txattrcreate({
+ .client = v9p, .fid = fids[i], .name = "user.test",
+ .size = TEST_XATTR_SIZE, .flags = 0
+ });
+
+ /* transfer the xattr data */
+ twrite({
+ .client = v9p, .fid = fids[i], .offset = 0,
+ .count = TEST_XATTR_SIZE, .data = xattr_data
+ });
+
+ /* verify server internal xattr counter */
+ if (check_counter) {
+ count = get_xattr_count(v9p);
+ g_assert_cmpuint(count, ==, (i + 1));
+ }
+
+ /* avoid virtio descriptor exhaustion */
+ qvirtqueue_reset_pool(v9p->vq);
+ }
+
+ /* if xattrs are limited, the next xattr should fail */
+ if (max_xattr != -1) {
+ /* walk to create another fid */
+ err_fid = twalk({
+ .client = v9p, .fid = 0,
+ .nwname = 1, .wnames = (char **) file_path
+ }).newfid;
+
+ /* try to create one more xattr fid - should fail */
+ txattrcreate({
+ .client = v9p, .fid = err_fid, .name = "user.test_exceed",
+ .size = TEST_XATTR_SIZE, .flags = 0,
+ .expectErr = ENOSPC
+ });
+
+ /* verify internal xattr counter hasn't changed */
+ if (check_counter) {
+ count = get_xattr_count(v9p);
+ g_assert_cmpuint(count, ==, limit);
+ }
+ }
+
+ /* clunk all fids (should decrement xattr counter) */
+ for (i = 0; i < limit; i++) {
+ tclunk({ .client = v9p, .fid = fids[i] });
+ qvirtqueue_reset_pool(v9p->vq);
+ }
+ if (err_fid) {
+ tclunk({ .client = v9p, .fid = err_fid });
+ }
+
+ /* verify internal xattr counter is zero */
+ if (check_counter) {
+ count = get_xattr_count(v9p);
+ g_assert_cmpuint(count, ==, 0);
+ }
+}
+
static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc)
{
QVirtio9P *v9p = obj;
@@ -508,6 +655,27 @@ static void fs_readdir_split_512(void *obj, void *data,
do_readdir_split(obj, 512);
}
+static void fs_synth_xattr_limit_default(void *obj, void *data,
+ QGuestAllocator *t_alloc)
+{
+ v9fs_set_allocator(t_alloc);
+ do_xattr_limit(obj, V9FS_MAX_XATTR_DEFAULT, true);
+}
+
+static void fs_synth_xattr_limit_custom(void *obj, void *data,
+ QGuestAllocator *t_alloc)
+{
+ v9fs_set_allocator(t_alloc);
+ do_xattr_limit(obj, 100, true);
+}
+
+static void fs_synth_xattr_limit_unlimited(void *obj, void *data,
+ QGuestAllocator *t_alloc)
+{
+ v9fs_set_allocator(t_alloc);
+ do_xattr_limit(obj, -1, true);
+}
+
/* tests using the 9pfs 'local' fs driver */
@@ -822,6 +990,18 @@ static void fs_deep_absolute_path(void *obj, void *data,
g_string_free(path, TRUE);
}
+static void *synth_max_xattr_custom_opt(GString *cmd_line, void *arg)
+{
+ virtio_9p_add_synth_driver_args(cmd_line, "max_xattr=100");
+ return arg;
+}
+
+static void *synth_max_xattr_unlimited_opt(GString *cmd_line, void *arg)
+{
+ virtio_9p_add_synth_driver_args(cmd_line, "max_xattr=0");
+ return arg;
+}
+
static void cleanup_9p_local_driver(void *data)
{
/* remove previously created test dir when test is completed */
@@ -872,7 +1052,14 @@ static void register_virtio_9p_test(void)
fs_readdir_split_256, &opts);
qos_add_test("synth/readdir/split_128", "virtio-9p",
fs_readdir_split_128, &opts);
-
+ qos_add_test("synth/xattr_limit/default", "virtio-9p",
+ fs_synth_xattr_limit_default, &opts);
+ opts.before = synth_max_xattr_custom_opt;
+ qos_add_test("synth/xattr_limit/custom", "virtio-9p",
+ fs_synth_xattr_limit_custom, &opts);
+ opts.before = synth_max_xattr_unlimited_opt;
+ qos_add_test("synth/xattr_limit/unlimited", "virtio-9p",
+ fs_synth_xattr_limit_unlimited, &opts);
/* 9pfs test cases using the 'local' filesystem driver */
opts.before = assign_9p_local_driver;
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 02/23] 9pfs/virtio: implement msize_limit callback
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (17 preceding siblings ...)
2026-06-29 13:28 ` [PULL 19/23] tests/9p: add 3 xattr FID limit test cases (synth fs driver) Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 04/23] hw/9pfs: cap negotiated msize to transport limit Christian Schoenebeck
` (6 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
Add and implement the msize_limit callback for the virtio transport.
This new callback function provides the theoretical maximum 'msize'
value supported by this virtio transport.
The limit is calculated as (VIRTQUEUE_MAX_SIZE - 2) * 4096 bytes,
where 2 virtio descriptors are lost exactly for:
- 1 descriptor for the original request (typically being small)
- 1 descriptor as indirect table pointer (when used), which just
contains a pointer to the separate sglist containing the
response's actual payload data
And 4096 bytes are assumed as standard page size used by Linux 9p
client. This results in a maximum 'msize' of 4186112 bytes.
Theoretically Linux client could support a much larger size, e.g. by
using multiple consecutive pages per sg entry / descriptor. However
that's currently not the case and unlikely to change any time soon.
And due to recent security issues, let's handle this limit
conservatively until really necessary to be raised.
Link: https://lore.kernel.org/qemu-devel/4c34426bc906e19423e7e2389c419c2e972d9b9b.1781287774.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
hw/9pfs/virtio-9p-device.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c
index 9f70e2338c..8c5d86cb66 100644
--- a/hw/9pfs/virtio-9p-device.c
+++ b/hw/9pfs/virtio-9p-device.c
@@ -192,12 +192,19 @@ static void virtio_init_out_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov,
*pniov = elem->out_num;
}
+static size_t virtio_9p_msize_limit(V9fsState *s)
+{
+ const size_t guestPageSize = 4096;
+ return (VIRTQUEUE_MAX_SIZE - 2) * guestPageSize;
+}
+
static const V9fsTransport virtio_9p_transport = {
.pdu_vmarshal = virtio_pdu_vmarshal,
.pdu_vunmarshal = virtio_pdu_vunmarshal,
.init_in_iov_from_pdu = virtio_init_in_iov_from_pdu,
.init_out_iov_from_pdu = virtio_init_out_iov_from_pdu,
.push_and_notify = virtio_9p_push_and_notify,
+ .msize_limit = virtio_9p_msize_limit,
};
static void virtio_9p_device_realize(DeviceState *dev, Error **errp)
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 04/23] hw/9pfs: cap negotiated msize to transport limit
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (18 preceding siblings ...)
2026-06-29 13:28 ` [PULL 02/23] 9pfs/virtio: implement msize_limit callback Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 15/23] hw/9pfs: enable xattr (mockup) support for synth fs driver Christian Schoenebeck
` (5 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
The 'msize' parameter negotiated during Tversion handshake can be
arbitrarily large as requested by the guest. So far 9p server accepted
any msize value suggested by guest, i.e. server did not cap it at all,
no matter how large, as in practice the upper limit of msize is a client
capability. But as subsequent's security patch shows, capping msize on
server side makes sense as additional safety-net.
Let's cap msize to transport's theoretical limit for msize, mainly to
prevent a bad client from triggering excessive host memory allocations
throughout the session.
We intentionally don't cap msize to transport's current, real response
buffer size, as the response buffer size may vary between individual
requests.
Link: https://lore.kernel.org/qemu-devel/2105e9a3578c6f751bb64af55c16dd953f393f20.1781287774.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
hw/9pfs/9p.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index b486cce48a..81d9bb8c6d 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -1469,6 +1469,16 @@ static void coroutine_fn v9fs_version(void *opaque)
goto out;
}
+ /* cap msize to transport's theoretical limit */
+ if (s->transport->msize_limit) {
+ size_t limit = s->transport->msize_limit(s);
+ if (s->msize > limit) {
+ s->msize = limit;
+ warn_report_once("9p: client msize capped to %zu (transport limit)",
+ limit);
+ }
+ }
+
/* 8192 is the default msize of Linux clients */
if (s->msize <= 8192 && !(s->ctx.export_flags & V9FS_NO_PERF_WARN)) {
warn_report_once(
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 15/23] hw/9pfs: enable xattr (mockup) support for synth fs driver
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (19 preceding siblings ...)
2026-06-29 13:28 ` [PULL 04/23] hw/9pfs: cap negotiated msize to transport limit Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 06/23] 9pfs/virtio: implement response_buffer_size callback Christian Schoenebeck
` (4 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
The synth backend is used for testing only. Enable xattr operations
by making lsetxattr and lremovexattr callbacks to return success
result.
They are still actually not doing anything, they just pretend to be
working to prevent 9pfs server from erroring out on xattr requests.
This allows the subsequent test case patches to verify xattr FID
limit enforcement.
Link: https://lore.kernel.org/qemu-devel/9c1c7128135f3bd9c2f62a3f64bb730b6a94ec7a.1781361555.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
hw/9pfs/9p-synth.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c
index 322dc3bb69..4b0732e093 100644
--- a/hw/9pfs/9p-synth.c
+++ b/hw/9pfs/9p-synth.c
@@ -477,15 +477,15 @@ static int synth_lsetxattr(FsContext *ctx, V9fsPath *path,
const char *name, void *value,
size_t size, int flags)
{
- errno = ENOTSUP;
- return -1;
+ /* pretend it worked */
+ return 0;
}
static int synth_lremovexattr(FsContext *ctx,
V9fsPath *path, const char *name)
{
- errno = ENOTSUP;
- return -1;
+ /* pretend it worked */
+ return 0;
}
static int synth_name_to_path(FsContext *ctx, V9fsPath *dir_path,
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 06/23] 9pfs/virtio: implement response_buffer_size callback
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (20 preceding siblings ...)
2026-06-29 13:28 ` [PULL 15/23] hw/9pfs: enable xattr (mockup) support for synth fs driver Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 13:28 ` [PULL 13/23] tests/9p: add Tclunk / Rclunk test client functions Christian Schoenebeck
` (3 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
Add and implement the response_buffer_size callback for the virtio
transport.
Returns the actual current virtio response buffer size for the supplied
PDU, which will be used as safety guard for limiting the response size
when generating a 9p response.
Link: https://lore.kernel.org/qemu-devel/5bbed2768f7a0da8fa2be183e75928c5d1ef691d.1781287774.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
hw/9pfs/virtio-9p-device.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c
index 8c5d86cb66..50dc93091d 100644
--- a/hw/9pfs/virtio-9p-device.c
+++ b/hw/9pfs/virtio-9p-device.c
@@ -198,6 +198,15 @@ static size_t virtio_9p_msize_limit(V9fsState *s)
return (VIRTQUEUE_MAX_SIZE - 2) * guestPageSize;
}
+static size_t virtio_9p_response_buffer_size(V9fsPDU *pdu)
+{
+ V9fsState *s = pdu->s;
+ V9fsVirtioState *v = container_of(s, V9fsVirtioState, state);
+ VirtQueueElement *elem = v->elems[pdu->idx];
+
+ return iov_size(elem->in_sg, elem->in_num);
+}
+
static const V9fsTransport virtio_9p_transport = {
.pdu_vmarshal = virtio_pdu_vmarshal,
.pdu_vunmarshal = virtio_pdu_vunmarshal,
@@ -205,6 +214,7 @@ static const V9fsTransport virtio_9p_transport = {
.init_out_iov_from_pdu = virtio_init_out_iov_from_pdu,
.push_and_notify = virtio_9p_push_and_notify,
.msize_limit = virtio_9p_msize_limit,
+ .response_buffer_size = virtio_9p_response_buffer_size,
};
static void virtio_9p_device_realize(DeviceState *dev, Error **errp)
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PULL 13/23] tests/9p: add Tclunk / Rclunk test client functions
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (21 preceding siblings ...)
2026-06-29 13:28 ` [PULL 06/23] 9pfs/virtio: implement response_buffer_size callback Christian Schoenebeck
@ 2026-06-29 13:28 ` Christian Schoenebeck
2026-06-29 18:28 ` [PULL 00/23] 9p queue 2026-06-29 Stefan Hajnoczi
` (2 subsequent siblings)
25 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-06-29 13:28 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian
Add v9fs_tclunk() and v9fs_rclunk() functions to the 9P test client
for closing file handles (or "FIDs") in test cases.
Link: https://lore.kernel.org/qemu-devel/d53f0337eb7f8525a14e394599d809ecf6805e5e.1781361555.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
---
tests/qtest/libqos/virtio-9p-client.c | 34 +++++++++++++++++++++++++++
tests/qtest/libqos/virtio-9p-client.h | 22 +++++++++++++++++
tests/qtest/virtio-9p-test.c | 1 +
3 files changed, 57 insertions(+)
diff --git a/tests/qtest/libqos/virtio-9p-client.c b/tests/qtest/libqos/virtio-9p-client.c
index b9785ef8cb..83af6dab5d 100644
--- a/tests/qtest/libqos/virtio-9p-client.c
+++ b/tests/qtest/libqos/virtio-9p-client.c
@@ -242,6 +242,7 @@ static const char *rmessage_name(uint8_t id)
id == P9_RFLUSH ? "RFLUSH" :
id == P9_RREADDIR ? "RREADDIR" :
id == P9_RREAD ? "RREAD" :
+ id == P9_RCLUNK ? "RCLUNK" :
"<unknown>";
}
@@ -1148,3 +1149,36 @@ void v9fs_rread(P9Req *req, uint32_t *count, void *data)
}
v9fs_req_free(req);
}
+
+/* size[4] Tclunk tag[2] fid[4] */
+TClunkRes v9fs_tclunk(TClunkOpt opt)
+{
+ P9Req *req;
+ uint32_t err;
+
+ g_assert(opt.client);
+
+ req = v9fs_req_init(opt.client, 4, P9_TCLUNK, opt.tag);
+ v9fs_uint32_write(req, opt.fid);
+ v9fs_req_send(req);
+
+ if (!opt.requestOnly) {
+ v9fs_req_wait_for_reply(req, NULL);
+ if (opt.expectErr) {
+ v9fs_rlerror(req, &err);
+ g_assert_cmpint(err, ==, opt.expectErr);
+ } else {
+ v9fs_rclunk(req);
+ }
+ req = NULL; /* request was freed */
+ }
+
+ return (TClunkRes) { .req = req };
+}
+
+/* size[4] Rclunk tag[2] */
+void v9fs_rclunk(P9Req *req)
+{
+ v9fs_req_recv(req, P9_RCLUNK);
+ v9fs_req_free(req);
+}
diff --git a/tests/qtest/libqos/virtio-9p-client.h b/tests/qtest/libqos/virtio-9p-client.h
index 37f2517cff..f7ef7f0067 100644
--- a/tests/qtest/libqos/virtio-9p-client.h
+++ b/tests/qtest/libqos/virtio-9p-client.h
@@ -504,6 +504,26 @@ typedef struct TReadRes {
uint32_t count;
} TReadRes;
+/* options for 'Tclunk' 9p request */
+typedef struct TClunkOpt {
+ /* 9P client being used (mandatory) */
+ QVirtio9P *client;
+ /* user supplied tag number being returned with response (optional) */
+ uint16_t tag;
+ /* file ID to clunk (required) */
+ uint32_t fid;
+ /* only send Tclunk request but not wait for a reply? (optional) */
+ bool requestOnly;
+ /* do we expect an Rlerror response, if yes which error code? (optional) */
+ uint32_t expectErr;
+} TClunkOpt;
+
+/* result of 'Tclunk' 9p request */
+typedef struct TClunkRes {
+ /* if requestOnly was set: request object for further processing */
+ P9Req *req;
+} TClunkRes;
+
void v9fs_set_allocator(QGuestAllocator *t_alloc);
void v9fs_memwrite(P9Req *req, const void *addr, size_t len);
void v9fs_memskip(P9Req *req, size_t len);
@@ -557,5 +577,7 @@ TunlinkatRes v9fs_tunlinkat(TunlinkatOpt);
void v9fs_runlinkat(P9Req *req);
TReadRes v9fs_tread(TReadOpt opt);
void v9fs_rread(P9Req *req, uint32_t *count, void *data);
+TClunkRes v9fs_tclunk(TClunkOpt opt);
+void v9fs_rclunk(P9Req *req);
#endif
diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c
index 8ccec77e70..099c5e0ca0 100644
--- a/tests/qtest/virtio-9p-test.c
+++ b/tests/qtest/virtio-9p-test.c
@@ -32,6 +32,7 @@
#define tlink(...) v9fs_tlink((TlinkOpt) __VA_ARGS__)
#define tunlinkat(...) v9fs_tunlinkat((TunlinkatOpt) __VA_ARGS__)
#define tread(...) v9fs_tread((TReadOpt) __VA_ARGS__)
+#define tclunk(...) v9fs_tclunk((TClunkOpt) __VA_ARGS__)
static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
{
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: [PULL 00/23] 9p queue 2026-06-29
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (22 preceding siblings ...)
2026-06-29 13:28 ` [PULL 13/23] tests/9p: add Tclunk / Rclunk test client functions Christian Schoenebeck
@ 2026-06-29 18:28 ` Stefan Hajnoczi
2026-06-30 7:28 ` Pierrick Bouvier
2026-07-01 10:15 ` Michael Tokarev
25 siblings, 0 replies; 32+ messages in thread
From: Stefan Hajnoczi @ 2026-06-29 18:28 UTC (permalink / raw)
To: Christian Schoenebeck
Cc: qemu-devel, qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian,
Stefano Stabellini
[-- Attachment #1: Type: text/plain, Size: 116 bytes --]
Applied, thanks.
Please update the changelog at https://wiki.qemu.org/ChangeLog/11.1 for any user-visible changes.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PULL 00/23] 9p queue 2026-06-29
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (23 preceding siblings ...)
2026-06-29 18:28 ` [PULL 00/23] 9p queue 2026-06-29 Stefan Hajnoczi
@ 2026-06-30 7:28 ` Pierrick Bouvier
2026-07-01 16:20 ` Christian Schoenebeck
2026-07-01 10:15 ` Michael Tokarev
25 siblings, 1 reply; 32+ messages in thread
From: Pierrick Bouvier @ 2026-06-30 7:28 UTC (permalink / raw)
To: Christian Schoenebeck, qemu-devel
Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian,
Stefano Stabellini
Hi Christian,
On 6/29/2026 6:28 AM, Christian Schoenebeck wrote:
> The following changes since commit 20553466cc47af6a8c95f665b601fce3c852e503:
>
> Merge tag 'pbouvier/pr/docs-20260626' of https://gitlab.com/p-b-o/qemu into staging (2026-06-27 23:28:35 -0400)
>
> are available in the Git repository at:
>
> https://github.com/cschoenebeck/qemu.git tags/pull-9p-20260629
>
> for you to fetch changes up to 75893c058b21d87d1ec66bbd4e8bf84e1fd616d1:
>
> hw/9pfs/local: harden local_fid_fd() on FID types (2026-06-29 15:10:32 +0200)
>
> ----------------------------------------------------------------
> 9pfs changes:
>
> - Fix DoS via Treaddir (CVE-2026-9238).
>
> - Add xattr FID limit (CVE-2026-8348).
>
> - Fix union V9fsFidOpenState type confusion.
>
> ----------------------------------------------------------------
> Christian Schoenebeck (23):
> hw/9pfs: add msize_limit transport callback
> 9pfs/virtio: implement msize_limit callback
> 9pfs/xen: implement msize_limit callback
> hw/9pfs: cap negotiated msize to transport limit
> hw/9pfs: add response_buffer_size transport callback
> 9pfs/virtio: implement response_buffer_size callback
> 9pfs/xen: implement response_buffer_size callback
> hw/9pfs: cap Treaddir allocation (CVE-2026-9238)
> hw/9pfs: add xattr FID limit to prevent memory exhaustion
> hw/9pfs: add max_xattr option
> qemu-options: document 9pfs max_xattr option
> tests/9p: add Tread / Rread test client functions
> tests/9p: add Tclunk / Rclunk test client functions
> tests/9p: add Txattrcreate / Rxattrcreate test client functions
> hw/9pfs: enable xattr (mockup) support for synth fs driver
> hw/9pfs: add xattr count query interface to fs synth driver
> tests/9p: increase P9_MAX_SIZE for test client
> tests/9p: add virtio_9p_add_synth_driver_args() test client function
> tests/9p: add 3 xattr FID limit test cases (synth fs driver)
> tests/9p: add 3 xattr FID limit test cases (local fs driver)
> hw/9pfs: fix invalid union access by v9fs_co_fsync()
> hw/9pfs: fix invalid union access by v9fs_co_fstat()
> hw/9pfs/local: harden local_fid_fd() on FID types
>
> fsdev/file-op-9p.h | 11 ++
> fsdev/qemu-fsdev-opts.c | 6 +
> fsdev/qemu-fsdev.c | 2 +-
> hw/9pfs/9p-local.c | 14 +-
> hw/9pfs/9p-synth.c | 51 ++++++-
> hw/9pfs/9p.c | 113 ++++++++++++++-
> hw/9pfs/9p.h | 2 +
> hw/9pfs/virtio-9p-device.c | 17 +++
> hw/9pfs/xen-9p-backend.c | 31 ++++
> qemu-options.hx | 28 ++--
> system/vl.c | 7 +-
> tests/qtest/libqos/virtio-9p-client.c | 124 ++++++++++++++++
> tests/qtest/libqos/virtio-9p-client.h | 88 +++++++++++-
> tests/qtest/libqos/virtio-9p.c | 6 +
> tests/qtest/libqos/virtio-9p.h | 6 +
> tests/qtest/virtio-9p-test.c | 262 +++++++++++++++++++++++++++++++++-
> 16 files changed, 746 insertions(+), 22 deletions(-)
>
This series brought a test regression:
$ ./build/pyvenv/bin/meson test -C build \
--setup thorough --print-errorlogs \
qemu:qtest-aarch64/qos-test
...
ERROR:../tests/qtest/libqos/virtio-9p-client.c:280:v9fs_req_recv:
assertion failed (hdr.id == id): (7 == 121)
It seems to come test added in patch 19
"tests/9p: add 3 xattr FID limit test cases (synth fs driver)"
This test runs only with slow setup, so I suspect it never worked and
was never ran.
Could you take a look to fix or revert the concerned change?
You can add me in copy, I'll be happy to review and test it.
Thanks,
Pierrick
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PULL 00/23] 9p queue 2026-06-29
2026-06-30 7:28 ` Pierrick Bouvier
@ 2026-07-01 16:20 ` Christian Schoenebeck
2026-07-02 18:21 ` Pierrick Bouvier
0 siblings, 1 reply; 32+ messages in thread
From: Christian Schoenebeck @ 2026-07-01 16:20 UTC (permalink / raw)
To: qemu-devel, Pierrick Bouvier
Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian,
Stefano Stabellini
On Tuesday, 30 June 2026 09:28:48 CEST Pierrick Bouvier wrote:
> Hi Christian,
Hi Pierrick,
> This series brought a test regression:
> $ ./build/pyvenv/bin/meson test -C build \
> --setup thorough --print-errorlogs \
> qemu:qtest-aarch64/qos-test
> ...
> ERROR:../tests/qtest/libqos/virtio-9p-client.c:280:v9fs_req_recv:
> assertion failed (hdr.id == id): (7 == 121)
>
> It seems to come test added in patch 19
> "tests/9p: add 3 xattr FID limit test cases (synth fs driver)"
> This test runs only with slow setup, so I suspect it never worked and
> was never ran.
Of course I ran these slow tests.
I intentionally registered these tests as "slow" tests as they take a long
time to complete and QEMU tests being notorious on exceeding the overall CI
timeout limit. So these tests are exempted from running in the official CI
pipeline, but not from mine.
I just reran them with latest master head. However I am unable to reproduce
your reported test error so far.
You cropped the output too aggressively. I need to know:
- Which test exactly failed? (especially whether it's really a synth backend
or a local backend test that failed)
- What is the errno being printed *before* the "assertion failed" message?
("(hdr.id == id): (7 == 121)" just means 9p server responded with an error
response instead of the expected one)
- Were you running the tests in a Docker environment or something or just
plain locally?
- Does the underlying host file system support xattrs? (might matter if it's a
local backend test that's failing)
/Christian
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PULL 00/23] 9p queue 2026-06-29
2026-07-01 16:20 ` Christian Schoenebeck
@ 2026-07-02 18:21 ` Pierrick Bouvier
2026-07-03 14:36 ` Christian Schoenebeck
0 siblings, 1 reply; 32+ messages in thread
From: Pierrick Bouvier @ 2026-07-02 18:21 UTC (permalink / raw)
To: Christian Schoenebeck, qemu-devel
Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian,
Stefano Stabellini
Hi Christian,
On 7/1/2026 9:20 AM, Christian Schoenebeck wrote:
> On Tuesday, 30 June 2026 09:28:48 CEST Pierrick Bouvier wrote:
>> Hi Christian,
>
> Hi Pierrick,
>
>> This series brought a test regression:
>> $ ./build/pyvenv/bin/meson test -C build \
>> --setup thorough --print-errorlogs \
>> qemu:qtest-aarch64/qos-test
>> ...
>> ERROR:../tests/qtest/libqos/virtio-9p-client.c:280:v9fs_req_recv:
>> assertion failed (hdr.id == id): (7 == 121)
>>
>> It seems to come test added in patch 19
>> "tests/9p: add 3 xattr FID limit test cases (synth fs driver)"
>> This test runs only with slow setup, so I suspect it never worked and
>> was never ran.
>
> Of course I ran these slow tests.
>
I should have added "in our CI" sorry. I'm sure you developed, tested
and ran those yourself.
> I intentionally registered these tests as "slow" tests as they take a long
> time to complete and QEMU tests being notorious on exceeding the overall CI
> timeout limit. So these tests are exempted from running in the official CI
> pipeline, but not from mine.
>
> I just reran them with latest master head. However I am unable to reproduce
> your reported test error so far.
>
> You cropped the output too aggressively. I need to know:
>
> - Which test exactly failed? (especially whether it's really a synth backend
> or a local backend test that failed)
>
That's the complete log from the command given above.
Note: disk is not full, but the error "no space left on device" is very
weird.
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 0 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 1 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: -chardev
socket,id=chr-reconnect,path=/tmp/vhost-test-VZ60R3/reconnect.sock,server=on:
info: QEMU waiting for connection on:
disconnected:unix:/tmp/vhost-test-VZ60R3/reconnect.sock,server=on
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 0 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 1 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: -chardev
socket,id=chr-connect-fail,path=/tmp/vhost-test-GTPQR3/connect-fail.sock,server=on:
info: QEMU waiting for connection on:
disconnected:unix:/tmp/vhost-test-GTPQR3/connect-fail.sock,server=on
qemu-system-aarch64: -netdev
vhost-user,id=hs0,chardev=chr-connect-fail,vhostforce=on: Failed to read
msg header. Read 0 instead of 12. Original request 1.
qemu-system-aarch64: -netdev
vhost-user,id=hs0,chardev=chr-connect-fail,vhostforce=on:
vhost_backend_init failed: Protocol error
qemu-system-aarch64: -netdev
vhost-user,id=hs0,chardev=chr-connect-fail,vhostforce=on: failed to init
vhost_net for queue 0
qemu-system-aarch64: -netdev
vhost-user,id=hs0,chardev=chr-connect-fail,vhostforce=on: info: QEMU
waiting for connection on:
disconnected:unix:/tmp/vhost-test-GTPQR3/connect-fail.sock,server=on
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 0 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 1 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: -chardev
socket,id=chr-flags-mismatch,path=/tmp/vhost-test-6J0ZR3/flags-mismatch.sock,server=on:
info: QEMU waiting for connection on:
disconnected:unix:/tmp/vhost-test-6J0ZR3/flags-mismatch.sock,server=on
qemu-system-aarch64: Failed to write msg. Wrote -1 instead of 52.
qemu-system-aarch64: vhost_set_vring_addr failed: Invalid argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 0 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: unable to start vhost net: 22: falling back on
userspace virtio
vhost lacks feature mask 0x00000000000000000000000140000000 for backend
qemu-system-aarch64: failed to init vhost_net for queue 0
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 0 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 1 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 0 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 1 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 2 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 3 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 0 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 1 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 0 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 1 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 0 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 1 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Virtqueue size exceeded
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 0 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 1 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 0 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost VQ 1 ring restore failed: -22: Invalid
argument (22)
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost_set_vring_call failed 22
qemu-system-aarch64: Failed to set msg fds.
qemu-system-aarch64: vhost_set_vring_call failed 22
set virtio-scmi status to 15 failed, old status: 15
qemu-system-aarch64: 9pfs: xattr_fid_count limit exceeded (configurable
by option 'max_xattr').
qemu-system-aarch64: 9pfs: xattr_fid_count limit exceeded (configurable
by option 'max_xattr').
qemu-system-aarch64: 9pfs: xattr_fid_count limit exceeded (configurable
by option 'max_xattr').
Received response 7 (RLERROR) instead of 121 (RCLUNK)
Rlerror has errno 28 (No space left on device)
**
ERROR:../tests/qtest/libqos/virtio-9p-client.c:280:v9fs_req_recv:
assertion failed (hdr.id == id): (7 == 121)
> - What is the errno being printed *before* the "assertion failed" message?
> ("(hdr.id == id): (7 == 121)" just means 9p server responded with an error
> response instead of the expected one)
>
> - Were you running the tests in a Docker environment or something or just
> plain locally?
>
I saw the error on four different setup:
- On a GitHub runner, using qemu debian container
(tests/docker/dockerfiles/debian.docker)
- On WSL2 on windows-arm64 (which is a full Linux VM).
- On my personal x64 machine, which has nothing funky (ext4 filesystem,
and no containers).
- On a GitHub runner, without any container (see end of this mail to
reproduce).
> - Does the underlying host file system support xattrs? (might matter if it's a
> local backend test that's failing)
>
In WSL2 (3 fs):
cat /proc/fs/ext4/*/options | grep xattr
user_xattr
user_xattr
user_xattr
On my machine (2 fs):
cat /proc/fs/ext4/*/options | grep xattr
user_xattr
user_xattr
So it looks ok.
---
Finally, and for sake of science, I reproduced the issue on a clean
GitHub runner. Please follow the README here:
https://github.com/second-reality/github-runners
Then, connect to an x64-ubuntu24.04 runner and:
```
cd qemu && \
./configure --enable-debug --target-list=aarch64-softmmu && \
ninja -C build && \
./build/pyvenv/bin/meson test -C build \
--setup thorough --print-errorlogs qemu:qtest-aarch64/qos-test
```
Any chance you have another machine than the one when you ran those
tests to confirm it works on it?
> /Christian
>
>
Regards,
Pierrick
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PULL 00/23] 9p queue 2026-06-29
2026-07-02 18:21 ` Pierrick Bouvier
@ 2026-07-03 14:36 ` Christian Schoenebeck
2026-07-03 17:20 ` Pierrick Bouvier
0 siblings, 1 reply; 32+ messages in thread
From: Christian Schoenebeck @ 2026-07-03 14:36 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian,
Stefano Stabellini, Pierrick Bouvier
On Thursday, 2 July 2026 20:21:54 CEST Pierrick Bouvier wrote:
> Hi Christian,
>
> On 7/1/2026 9:20 AM, Christian Schoenebeck wrote:
> > On Tuesday, 30 June 2026 09:28:48 CEST Pierrick Bouvier wrote:
> >> Hi Christian,
> >
> > Hi Pierrick,
> >
> >> This series brought a test regression:
> >> $ ./build/pyvenv/bin/meson test -C build \
> >> --setup thorough --print-errorlogs \
> >> qemu:qtest-aarch64/qos-test
> >> ...
> >> ERROR:../tests/qtest/libqos/virtio-9p-client.c:280:v9fs_req_recv:
> >> assertion failed (hdr.id == id): (7 == 121)
> >>
> >> It seems to come test added in patch 19
> >> "tests/9p: add 3 xattr FID limit test cases (synth fs driver)"
> >> This test runs only with slow setup, so I suspect it never worked and
> >> was never ran.
> >
> > Of course I ran these slow tests.
>
> I should have added "in our CI" sorry. I'm sure you developed, tested
> and ran those yourself.
>
> > I intentionally registered these tests as "slow" tests as they take a long
> > time to complete and QEMU tests being notorious on exceeding the overall
> > CI
> > timeout limit. So these tests are exempted from running in the official CI
> > pipeline, but not from mine.
> >
> > I just reran them with latest master head. However I am unable to
> > reproduce
> > your reported test error so far.
> >
> > You cropped the output too aggressively. I need to know:
> >
> > - Which test exactly failed? (especially whether it's really a synth
> > backend>
> > or a local backend test that failed)
>
> That's the complete log from the command given above.
> Note: disk is not full, but the error "no space left on device" is very
> weird.
OK, I was able to reproduce the test error. Root cause is that ext4 without
ea_inode enabled is limited to ~4KB per xattr block. I'll prepare a patch.
For the next time: please note that the most important information is always
the name of the test case that failed! There are several ways to run the
tests. In the way you ran them, the name of the failed test is printed
*before* the stderr block:
...
# slow test /aarch64/virt/generic-pcihost/pci-bus-generic/pci-bus/virtio-9p-
pci/virtio-9p/virtio-9p-tests/local/deep_absolute_path executed in 1.63 secs
# Start of xattr_limit tests
# starting QEMU: exec ./qemu-system-aarch64 -qtest unix:/tmp/qtest-22413.sock
-qtest-log /dev/null -chardev socket,path=/tmp/qtest-22413.qmp,id=char0 -mo
n chardev=char0,mode=control -display none -audio none -run-with exit-with-
parent=on -M virt, -cpu max -fsdev local,id=fsdev0,path='/src/bee/qemu/build/q
test-9p-local-KKOMR3',security_model=mapped-xattr -device virtio-9p-
pci,fsdev=fsdev0,addr=04.0,mount_tag=qtest -accel qtest
not ok /aarch64/virt/generic-pcihost/pci-bus-generic/pci-bus/virtio-9p-pci/
virtio-9p/virtio-9p-tests/local/xattr_limit/default - ERROR:../tests/qtest/lib
qos/virtio-9p-client.c:280:v9fs_req_recv: assertion failed (hdr.id == id): (7
== 121)
Bail out!
----------------------------------- stderr -----------------------------------
...
So here the failing test name was /aarch64/virt/generic-pcihost/pci-bus-
generic/pci-bus/virtio-9p-pci/virtio-9p/virtio-9p-tests/local/xattr_limit/
default
Independent of this particular issue here, I wonder whether there is some way
to register a test such that it won't break other people's test pipeline.
Because these "local" backend xattr tests are sensible on underlying host
capabilities. They are useful for me, but that should not mean to stop the
world just because an unrelated test environment is missing a capability.
/Christian
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PULL 00/23] 9p queue 2026-06-29
2026-07-03 14:36 ` Christian Schoenebeck
@ 2026-07-03 17:20 ` Pierrick Bouvier
0 siblings, 0 replies; 32+ messages in thread
From: Pierrick Bouvier @ 2026-07-03 17:20 UTC (permalink / raw)
To: Christian Schoenebeck, qemu-devel
Cc: qemu-stable, Greg Kurz, Peter Maydell, Feifan Qian,
Stefano Stabellini
Hi Christian,
On 7/3/2026 7:36 AM, Christian Schoenebeck wrote:
> On Thursday, 2 July 2026 20:21:54 CEST Pierrick Bouvier wrote:
>> Hi Christian,
>>
>> On 7/1/2026 9:20 AM, Christian Schoenebeck wrote:
>>> On Tuesday, 30 June 2026 09:28:48 CEST Pierrick Bouvier wrote:
>>>> Hi Christian,
>>>
>>> Hi Pierrick,
>>>
>>>> This series brought a test regression:
>>>> $ ./build/pyvenv/bin/meson test -C build \
>>>> --setup thorough --print-errorlogs \
>>>> qemu:qtest-aarch64/qos-test
>>>> ...
>>>> ERROR:../tests/qtest/libqos/virtio-9p-client.c:280:v9fs_req_recv:
>>>> assertion failed (hdr.id == id): (7 == 121)
>>>>
>>>> It seems to come test added in patch 19
>>>> "tests/9p: add 3 xattr FID limit test cases (synth fs driver)"
>>>> This test runs only with slow setup, so I suspect it never worked and
>>>> was never ran.
>>>
>>> Of course I ran these slow tests.
>>
>> I should have added "in our CI" sorry. I'm sure you developed, tested
>> and ran those yourself.
>>
>>> I intentionally registered these tests as "slow" tests as they take a long
>>> time to complete and QEMU tests being notorious on exceeding the overall
>>> CI
>>> timeout limit. So these tests are exempted from running in the official CI
>>> pipeline, but not from mine.
>>>
>>> I just reran them with latest master head. However I am unable to
>>> reproduce
>>> your reported test error so far.
>>>
>>> You cropped the output too aggressively. I need to know:
>>>
>>> - Which test exactly failed? (especially whether it's really a synth
>>> backend>
>>> or a local backend test that failed)
>>
>> That's the complete log from the command given above.
>> Note: disk is not full, but the error "no space left on device" is very
>> weird.
>
> OK, I was able to reproduce the test error. Root cause is that ext4 without
> ea_inode enabled is limited to ~4KB per xattr block. I'll prepare a patch.
>
Just saw it, I'll test and review it. Thanks!
> For the next time: please note that the most important information is always
> the name of the test case that failed! There are several ways to run the
> tests. In the way you ran them, the name of the failed test is printed
> *before* the stderr block:
>
> ...
> # slow test /aarch64/virt/generic-pcihost/pci-bus-generic/pci-bus/virtio-9p-
> pci/virtio-9p/virtio-9p-tests/local/deep_absolute_path executed in 1.63 secs
> # Start of xattr_limit tests
> # starting QEMU: exec ./qemu-system-aarch64 -qtest unix:/tmp/qtest-22413.sock
> -qtest-log /dev/null -chardev socket,path=/tmp/qtest-22413.qmp,id=char0 -mo
> n chardev=char0,mode=control -display none -audio none -run-with exit-with-
> parent=on -M virt, -cpu max -fsdev local,id=fsdev0,path='/src/bee/qemu/build/q
> test-9p-local-KKOMR3',security_model=mapped-xattr -device virtio-9p-
> pci,fsdev=fsdev0,addr=04.0,mount_tag=qtest -accel qtest
> not ok /aarch64/virt/generic-pcihost/pci-bus-generic/pci-bus/virtio-9p-pci/
> virtio-9p/virtio-9p-tests/local/xattr_limit/default - ERROR:../tests/qtest/lib
> qos/virtio-9p-client.c:280:v9fs_req_recv: assertion failed (hdr.id == id): (7
> == 121)
> Bail out!
> ----------------------------------- stderr -----------------------------------
> ...
>
> So here the failing test name was /aarch64/virt/generic-pcihost/pci-bus-
> generic/pci-bus/virtio-9p-pci/virtio-9p/virtio-9p-tests/local/xattr_limit/
> default
>
> Independent of this particular issue here, I wonder whether there is some way
> to register a test such that it won't break other people's test pipeline.
> Because these "local" backend xattr tests are sensible on underlying host
> capabilities. They are useful for me, but that should not mean to stop the
> world just because an unrelated test environment is missing a capability.
>
There is no mechanism to attach a criteria to run a test.
In this case, the only solution would be to check this attribute at
runtime (in the test itself), and return 0 to indicate success. IMHO,
that's a perfectly valid strategy. Also, it has the benefit to keep all
the information in the test itself, instead of spreading it across
multiple files.
Functional tests are a bit more evolved and offer some skip
possibilities, and I'm not sure it's worth extending this to other tests.
> /Christian
>
>
>
>
Thanks,
Pierrick.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PULL 00/23] 9p queue 2026-06-29
2026-06-29 13:28 [PULL 00/23] 9p queue 2026-06-29 Christian Schoenebeck
` (24 preceding siblings ...)
2026-06-30 7:28 ` Pierrick Bouvier
@ 2026-07-01 10:15 ` Michael Tokarev
2026-07-01 17:21 ` Christian Schoenebeck
25 siblings, 1 reply; 32+ messages in thread
From: Michael Tokarev @ 2026-07-01 10:15 UTC (permalink / raw)
To: Christian Schoenebeck, qemu-devel
Cc: qemu-stable, Greg Kurz, Feifan Qian, Stefano Stabellini
On 29.06.2026 16:28, Christian Schoenebeck wrote:
> 9pfs changes:
>
> - Fix DoS via Treaddir (CVE-2026-9238).
>
> - Add xattr FID limit (CVE-2026-8348).
>
> - Fix union V9fsFidOpenState type confusion.
>
> ----------------------------------------------------------------
> Christian Schoenebeck (23):
> hw/9pfs: add msize_limit transport callback
> 9pfs/virtio: implement msize_limit callback
> 9pfs/xen: implement msize_limit callback
> hw/9pfs: cap negotiated msize to transport limit
> hw/9pfs: add response_buffer_size transport callback
> 9pfs/virtio: implement response_buffer_size callback
> 9pfs/xen: implement response_buffer_size callback
> hw/9pfs: cap Treaddir allocation (CVE-2026-9238)
> hw/9pfs: add xattr FID limit to prevent memory exhaustion
> hw/9pfs: add max_xattr option
> qemu-options: document 9pfs max_xattr option
> tests/9p: add Tread / Rread test client functions
> tests/9p: add Tclunk / Rclunk test client functions
> tests/9p: add Txattrcreate / Rxattrcreate test client functions
> hw/9pfs: enable xattr (mockup) support for synth fs driver
> hw/9pfs: add xattr count query interface to fs synth driver
> tests/9p: increase P9_MAX_SIZE for test client
> tests/9p: add virtio_9p_add_synth_driver_args() test client function
> tests/9p: add 3 xattr FID limit test cases (synth fs driver)
> tests/9p: add 3 xattr FID limit test cases (local fs driver)
> hw/9pfs: fix invalid union access by v9fs_co_fsync()
> hw/9pfs: fix invalid union access by v9fs_co_fstat()
> hw/9pfs/local: harden local_fid_fd() on FID types
Thank you for this work!
FWIW, I've applied this to 11.0.x and 10.0.x. For the latter, I also
picked up the following two commits which are simple refactoring:
commit c84ebaa6b2cf9a27fc051b4692a42350adf6358e
Author: Greg Kurz <groug@kaod.org>
Date: Wed Mar 12 16:29:28 2025 +0100
9pfs: Don't use file descriptors in core code
commit 29070a13e7c131448ff35c90c70ff42e2989d420
Author: Greg Kurz <groug@kaod.org>
Date: Wed Mar 12 16:29:27 2025 +0100
9pfs: local : Introduce local_fid_fd() helper
What do you think, for older 7.2.x series, which was an LTS but now
security-only support, which of the above should be picked up?
I mean, which are the most important ones here?
Thanks,
/mjt
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PULL 00/23] 9p queue 2026-06-29
2026-07-01 10:15 ` Michael Tokarev
@ 2026-07-01 17:21 ` Christian Schoenebeck
0 siblings, 0 replies; 32+ messages in thread
From: Christian Schoenebeck @ 2026-07-01 17:21 UTC (permalink / raw)
To: qemu-devel, Michael Tokarev
Cc: qemu-stable, Greg Kurz, Feifan Qian, Stefano Stabellini
On Wednesday, 1 July 2026 12:15:23 CEST Michael Tokarev wrote:
[...]
> Thank you for this work!
Hi Michael,
> FWIW, I've applied this to 11.0.x and 10.0.x. For the latter, I also
> picked up the following two commits which are simple refactoring:
>
> commit c84ebaa6b2cf9a27fc051b4692a42350adf6358e
> Author: Greg Kurz <groug@kaod.org>
> Date: Wed Mar 12 16:29:28 2025 +0100
>
> 9pfs: Don't use file descriptors in core code
That's actually f2bb367d2b265c6c0ead1e0d4a8f7c43310b3107
> commit 29070a13e7c131448ff35c90c70ff42e2989d420
> Author: Greg Kurz <groug@kaod.org>
> Date: Wed Mar 12 16:29:27 2025 +0100
>
> 9pfs: local : Introduce local_fid_fd() helper
And that's 4f82ce8cd94f2601fb2b2e4cfe0cf5b44131817e
Both are safe to be picked and required for the patches in this PR.
> What do you think, for older 7.2.x series, which was an LTS but now
> security-only support, which of the above should be picked up?
> I mean, which are the most important ones here?
Good question. It depends on what the exact policy on the reduced support
level means (for 7.2.x). I mean these are essentially all security related
patches, but with different impact:
- All issues in this PR require full root control over guest. They are not
possible exploits by regular guest users (so low to medium severity).
- Patch 8 prevents a potential DoS combined with unlimited host memory
allocation / exhaustion.
- Patch 9 also prevents a potential unlimited host memory allocation /
exhaustion, however that's a bit tricky to pick alone, as it introduces a
hard coded limit on the max. amount of open xattrs to client, a limit which
did not exist before at all. The limit is chosen high enough to
theoretically not cause issues, but you'll never know what happens in
guest's user space, therefore patch 10 introduced a configurable option for
this limit, which OTOH is odd to be back-ported to such old stable branches.
- Patches 21 .. 23 merely fix a rather theoretical issue. I have not
identified a way for a exploit on this issue. So probably can be omitted.
- Most of the other patches are test cases for guarding these fixed issues. No
idea if you usually pick tests up or not. They are useful though to verify
whether they fix the issues as intended. Note though that they are
registered as "slow" tests, which do not run by default, e.g.:
cd build
tests/qtest/qos-test -m slow
Also note that Pierrick reported that some of the tests introduced by this PR
failed at their end. I couldn't reproduce so far, it might be a false
positive, minor issue with the test environment, in worst case I still have to
fix something on the core patches. So keep an eye on that, please:
https://lore.kernel.org/qemu-devel/53159abf-ae84-465a-ac70-8ad71fa6c045@oss.qualcomm.com/
/Christian
^ permalink raw reply [flat|nested] 32+ messages in thread