Compare commits
11 Commits
1fe98a3a7b
...
e304b17c31
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e304b17c31 | ||
|
|
9ad3d61c32 | ||
|
|
7ad7a05a7e | ||
|
|
ffa176f660 | ||
|
|
074928c386 | ||
|
|
3d643587ea | ||
|
|
9f9c5c8924 | ||
|
|
c4360a84fc | ||
|
|
fe82f9e345 | ||
|
|
d0b1ef237c | ||
|
|
baecb25326 |
26
Fix-the-missing-hmp_nbd_server_start-change-in-CVE-2.patch
Normal file
26
Fix-the-missing-hmp_nbd_server_start-change-in-CVE-2.patch
Normal file
@ -0,0 +1,26 @@
|
||||
From 8d0a13959c6dde9c73dd85add4a95a33d57b8d9a Mon Sep 17 00:00:00 2001
|
||||
From: xiao-yuliang <xiaoyuliang@kylinos.cn>
|
||||
Date: Wed, 16 Oct 2024 10:50:44 +0800
|
||||
Subject: [PATCH] Fix the missing hmp_nbd_server_start change in CVE-2024-7409
|
||||
|
||||
---
|
||||
monitor/hmp-cmds.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
|
||||
index bf468fe8eb..c1ffaba0ce 100644
|
||||
--- a/monitor/hmp-cmds.c
|
||||
+++ b/monitor/hmp-cmds.c
|
||||
@@ -2365,7 +2365,8 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
- nbd_server_start(addr, NULL, NULL, 0, &local_err);
|
||||
+ nbd_server_start(addr, NULL, NULL, NBD_DEFAULT_MAX_CONNECTIONS,
|
||||
+ &local_err);
|
||||
qapi_free_SocketAddress(addr);
|
||||
if (local_err != NULL) {
|
||||
goto exit;
|
||||
--
|
||||
2.28.0.windows.1
|
||||
|
||||
79
aio-wait.h-introduce-AIO_WAIT_WHILE_UNLOCKED.patch
Normal file
79
aio-wait.h-introduce-AIO_WAIT_WHILE_UNLOCKED.patch
Normal file
@ -0,0 +1,79 @@
|
||||
From 3257ee8757c6187901cbe287fbd3e7fecc321f37 Mon Sep 17 00:00:00 2001
|
||||
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
Date: Mon, 26 Sep 2022 05:31:57 -0400
|
||||
Subject: [PATCH 6/8] aio-wait.h: introduce AIO_WAIT_WHILE_UNLOCKED
|
||||
|
||||
Same as AIO_WAIT_WHILE macro, but if we are in the Main loop
|
||||
do not release and then acquire ctx_ 's aiocontext.
|
||||
|
||||
Once all Aiocontext locks go away, this macro will replace
|
||||
AIO_WAIT_WHILE.
|
||||
|
||||
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
|
||||
Message-Id: <20220926093214.506243-5-eesposit@redhat.com>
|
||||
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
|
||||
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||
---
|
||||
include/block/aio-wait.h | 17 +++++++++++++----
|
||||
1 file changed, 13 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h
|
||||
index 716d2639df..3c0cad35fb 100644
|
||||
--- a/include/block/aio-wait.h
|
||||
+++ b/include/block/aio-wait.h
|
||||
@@ -59,10 +59,13 @@ typedef struct {
|
||||
extern AioWait global_aio_wait;
|
||||
|
||||
/**
|
||||
- * AIO_WAIT_WHILE:
|
||||
+ * AIO_WAIT_WHILE_INTERNAL:
|
||||
* @ctx: the aio context, or NULL if multiple aio contexts (for which the
|
||||
* caller does not hold a lock) are involved in the polling condition.
|
||||
* @cond: wait while this conditional expression is true
|
||||
+ * @unlock: whether to unlock and then lock again @ctx. This apples
|
||||
+ * only when waiting for another AioContext from the main loop.
|
||||
+ * Otherwise it's ignored.
|
||||
*
|
||||
* Wait while a condition is true. Use this to implement synchronous
|
||||
* operations that require event loop activity.
|
||||
@@ -75,7 +78,7 @@ extern AioWait global_aio_wait;
|
||||
* wait on conditions between two IOThreads since that could lead to deadlock,
|
||||
* go via the main loop instead.
|
||||
*/
|
||||
-#define AIO_WAIT_WHILE(ctx, cond) ({ \
|
||||
+#define AIO_WAIT_WHILE_INTERNAL(ctx, cond, unlock) ({ \
|
||||
bool waited_ = false; \
|
||||
AioWait *wait_ = &global_aio_wait; \
|
||||
AioContext *ctx_ = (ctx); \
|
||||
@@ -90,11 +93,11 @@ extern AioWait global_aio_wait;
|
||||
assert(qemu_get_current_aio_context() == \
|
||||
qemu_get_aio_context()); \
|
||||
while ((cond)) { \
|
||||
- if (ctx_) { \
|
||||
+ if (unlock && ctx_) { \
|
||||
aio_context_release(ctx_); \
|
||||
} \
|
||||
aio_poll(qemu_get_aio_context(), true); \
|
||||
- if (ctx_) { \
|
||||
+ if (unlock && ctx_) { \
|
||||
aio_context_acquire(ctx_); \
|
||||
} \
|
||||
waited_ = true; \
|
||||
@@ -103,6 +106,12 @@ extern AioWait global_aio_wait;
|
||||
atomic_dec(&wait_->num_waiters); \
|
||||
waited_; })
|
||||
|
||||
+#define AIO_WAIT_WHILE(ctx, cond) \
|
||||
+ AIO_WAIT_WHILE_INTERNAL(ctx, cond, true)
|
||||
+
|
||||
+#define AIO_WAIT_WHILE_UNLOCKED(ctx, cond) \
|
||||
+ AIO_WAIT_WHILE_INTERNAL(ctx, cond, false)
|
||||
+
|
||||
/**
|
||||
* aio_wait_kick:
|
||||
* Wake up the main thread if it is waiting on AIO_WAIT_WHILE(). During
|
||||
--
|
||||
2.45.1.windows.1
|
||||
|
||||
223
block-Parse-filenames-only-when-explicitly-requested.patch
Normal file
223
block-Parse-filenames-only-when-explicitly-requested.patch
Normal file
@ -0,0 +1,223 @@
|
||||
From 7619ae94b2d22fab824c1ea79e83b094d9e150f8 Mon Sep 17 00:00:00 2001
|
||||
From: liuxiangdong <liuxiangdong5@huawei.com>
|
||||
Date: Wed, 19 Jun 2024 19:15:15 +0800
|
||||
Subject: [PATCH] block: Parse filenames only when explicitly requested
|
||||
(CVE-2024-4467)
|
||||
|
||||
cherry-pick from https://github.com/qemu/qemu/commit/7ead946998610657d38d1a505d5f25300d4ca613
|
||||
|
||||
When handling image filenames from legacy options such as -drive or from
|
||||
tools, these filenames are parsed for protocol prefixes, including for
|
||||
the json:{} pseudo-protocol.
|
||||
|
||||
This behaviour is intended for filenames that come directly from the
|
||||
command line and for backing files, which may come from the image file
|
||||
itself. Higher level management tools generally take care to verify that
|
||||
untrusted images don't contain a bad (or any) backing file reference;
|
||||
'qemu-img info' is a suitable tool for this.
|
||||
|
||||
However, for other files that can be referenced in images, such as
|
||||
qcow2 data files or VMDK extents, the string from the image file is
|
||||
usually not verified by management tools - and 'qemu-img info' wouldn't
|
||||
be suitable because in contrast to backing files, it already opens these
|
||||
other referenced files. So here the string should be interpreted as a
|
||||
literal local filename. More complex configurations need to be specified
|
||||
explicitly on the command line or in QMP.
|
||||
|
||||
This patch changes bdrv_open_inherit() so that it only parses filenames
|
||||
if a new parameter parse_filename is true. It is set for the top level
|
||||
in bdrv_open(), for the file child and for the backing file child. All
|
||||
other callers pass false and disable filename parsing this way.
|
||||
|
||||
Cc: qemu-stable@nongnu.org
|
||||
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||
Reviewed-by: Eric Blake <eblake@redhat.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
|
||||
Signed-off-by: liuxiangdong <liuxiangdong5@huawei.com>
|
||||
---
|
||||
block.c | 69 +++++++++++++++++++++++++++++++++++++--------------------
|
||||
1 file changed, 45 insertions(+), 24 deletions(-)
|
||||
|
||||
diff --git a/block.c b/block.c
|
||||
index 0e841dcf09..510857311e 100644
|
||||
--- a/block.c
|
||||
+++ b/block.c
|
||||
@@ -76,6 +76,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
QDict *options, int flags,
|
||||
BlockDriverState *parent,
|
||||
const BdrvChildRole *child_role,
|
||||
+ bool parse_filename,
|
||||
Error **errp);
|
||||
|
||||
/* If non-zero, use only whitelisted block drivers */
|
||||
@@ -1620,7 +1621,8 @@ static void parse_json_protocol(QDict *options, const char **pfilename,
|
||||
* block driver has been specified explicitly.
|
||||
*/
|
||||
static int bdrv_fill_options(QDict **options, const char *filename,
|
||||
- int *flags, Error **errp)
|
||||
+ int *flags, bool allow_parse_filename,
|
||||
+ Error **errp)
|
||||
{
|
||||
const char *drvname;
|
||||
bool protocol = *flags & BDRV_O_PROTOCOL;
|
||||
@@ -1660,7 +1662,7 @@ static int bdrv_fill_options(QDict **options, const char *filename,
|
||||
if (protocol && filename) {
|
||||
if (!qdict_haskey(*options, "filename")) {
|
||||
qdict_put_str(*options, "filename", filename);
|
||||
- parse_filename = true;
|
||||
+ parse_filename = allow_parse_filename;
|
||||
} else {
|
||||
error_setg(errp, "Can't specify 'file' and 'filename' options at "
|
||||
"the same time");
|
||||
@@ -2654,7 +2656,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
|
||||
}
|
||||
|
||||
backing_hd = bdrv_open_inherit(backing_filename, reference, options, 0, bs,
|
||||
- &child_backing, errp);
|
||||
+ &child_backing, true, errp);
|
||||
if (!backing_hd) {
|
||||
bs->open_flags |= BDRV_O_NO_BACKING;
|
||||
error_prepend(errp, "Could not open backing file: ");
|
||||
@@ -2689,7 +2691,7 @@ free_exit:
|
||||
static BlockDriverState *
|
||||
bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key,
|
||||
BlockDriverState *parent, const BdrvChildRole *child_role,
|
||||
- bool allow_none, Error **errp)
|
||||
+ bool allow_none, bool parse_filename, Error **errp)
|
||||
{
|
||||
BlockDriverState *bs = NULL;
|
||||
QDict *image_options;
|
||||
@@ -2720,7 +2722,7 @@ bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key,
|
||||
}
|
||||
|
||||
bs = bdrv_open_inherit(filename, reference, image_options, 0,
|
||||
- parent, child_role, errp);
|
||||
+ parent, child_role, parse_filename, errp);
|
||||
if (!bs) {
|
||||
goto done;
|
||||
}
|
||||
@@ -2730,6 +2732,24 @@ done:
|
||||
return bs;
|
||||
}
|
||||
|
||||
+static BdrvChild *bdrv_open_child_common(const char *filename,
|
||||
+ QDict *options, const char *bdref_key,
|
||||
+ BlockDriverState *parent,
|
||||
+ const BdrvChildRole *child_role,
|
||||
+ bool allow_none, bool parse_filename,
|
||||
+ Error **errp)
|
||||
+{
|
||||
+ BlockDriverState *bs;
|
||||
+
|
||||
+ bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_role,
|
||||
+ allow_none, parse_filename, errp);
|
||||
+ if (bs == NULL) {
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ return bdrv_attach_child(parent, bs, bdref_key, child_role, errp);
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Opens a disk image whose options are given as BlockdevRef in another block
|
||||
* device's options.
|
||||
@@ -2750,26 +2770,22 @@ BdrvChild *bdrv_open_child(const char *filename,
|
||||
const BdrvChildRole *child_role,
|
||||
bool allow_none, Error **errp)
|
||||
{
|
||||
- BlockDriverState *bs;
|
||||
-
|
||||
- bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_role,
|
||||
- allow_none, errp);
|
||||
- if (bs == NULL) {
|
||||
- return NULL;
|
||||
- }
|
||||
-
|
||||
- return bdrv_attach_child(parent, bs, bdref_key, child_role, errp);
|
||||
+ return bdrv_open_child_common(filename, options, bdref_key, parent,
|
||||
+ child_role, allow_none, false,
|
||||
+ errp);
|
||||
}
|
||||
|
||||
/*
|
||||
- * Wrapper on bdrv_open_child() for most popular case: open primary child of bs.
|
||||
+ * This does mostly the same as bdrv_open_child(), but for opening the primary
|
||||
+ * child of a node. A notable difference from bdrv_open_child() is that it
|
||||
+ * enables filename parsing for protocol names (including json:).
|
||||
*/
|
||||
int bdrv_open_file_child(const char *filename,
|
||||
QDict *options, const char *bdref_key,
|
||||
BlockDriverState *parent, Error **errp)
|
||||
{
|
||||
- parent->file = bdrv_open_child(filename, options, bdref_key, parent,
|
||||
- &child_file, false, errp);
|
||||
+ parent->file = bdrv_open_child_common(filename, options, bdref_key, parent,
|
||||
+ &child_file, false, true, errp);
|
||||
|
||||
return parent->file ? 0 : -EINVAL;
|
||||
}
|
||||
@@ -2812,7 +2828,8 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
|
||||
|
||||
}
|
||||
|
||||
- bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, errp);
|
||||
+ bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, false,
|
||||
+ errp);
|
||||
obj = NULL;
|
||||
|
||||
fail:
|
||||
@@ -2911,6 +2928,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
QDict *options, int flags,
|
||||
BlockDriverState *parent,
|
||||
const BdrvChildRole *child_role,
|
||||
+ bool parse_filename,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
@@ -2954,9 +2972,11 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
}
|
||||
|
||||
/* json: syntax counts as explicit options, as if in the QDict */
|
||||
- parse_json_protocol(options, &filename, &local_err);
|
||||
- if (local_err) {
|
||||
- goto fail;
|
||||
+ if (parse_filename) {
|
||||
+ parse_json_protocol(options, &filename, &local_err);
|
||||
+ if (local_err) {
|
||||
+ goto fail;
|
||||
+ }
|
||||
}
|
||||
|
||||
bs->explicit_options = qdict_clone_shallow(options);
|
||||
@@ -2967,7 +2987,8 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
parent->open_flags, parent->options);
|
||||
}
|
||||
|
||||
- ret = bdrv_fill_options(&options, filename, &flags, &local_err);
|
||||
+ ret = bdrv_fill_options(&options, filename, &flags, parse_filename,
|
||||
+ &local_err);
|
||||
if (local_err) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -3032,7 +3053,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
BlockDriverState *file_bs;
|
||||
|
||||
file_bs = bdrv_open_child_bs(filename, options, "file", bs,
|
||||
- &child_file, true, &local_err);
|
||||
+ &child_file, true, true, &local_err);
|
||||
if (local_err) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -3177,7 +3198,7 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
|
||||
QDict *options, int flags, Error **errp)
|
||||
{
|
||||
return bdrv_open_inherit(filename, reference, options, flags, NULL,
|
||||
- NULL, errp);
|
||||
+ NULL, true, errp);
|
||||
}
|
||||
|
||||
/* Return true if the NULL-terminated @list contains @str */
|
||||
--
|
||||
2.41.0.windows.1
|
||||
|
||||
459
block-introduce-bdrv_open_file_child-helper.patch
Normal file
459
block-introduce-bdrv_open_file_child-helper.patch
Normal file
@ -0,0 +1,459 @@
|
||||
From 34058a6ad98adeca582ece7ad75b5705931101c0 Mon Sep 17 00:00:00 2001
|
||||
From: liuxiangdong <liuxiangdong5@huawei.com>
|
||||
Date: Wed, 19 Jun 2024 19:09:26 +0800
|
||||
Subject: [PATCH] block: introduce bdrv_open_file_child() helper
|
||||
|
||||
cherry-pick from: https://github.com/qemu/qemu/commit/83930780325b144a5908c45b3957b9b6457b3831
|
||||
|
||||
Almost all drivers call bdrv_open_child() similarly. Let's create a
|
||||
helper for this.
|
||||
|
||||
The only not updated drivers that call bdrv_open_child() to set
|
||||
bs->file are raw-format and snapshot-access:
|
||||
raw-format sometimes want to have filtered child but
|
||||
don't set drv->is_filter to true.
|
||||
snapshot-access wants only DATA | PRIMARY
|
||||
|
||||
Possibly we should implement drv->is_filter_func() handler, to consider
|
||||
raw-format as filter when it works as filter.. But it's another story.
|
||||
|
||||
Note also, that we decrease assignments to bs->file in code: it helps
|
||||
us restrict modifying this field in further commit.
|
||||
|
||||
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
|
||||
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
|
||||
Message-Id: <20220726201134.924743-3-vsementsov@yandex-team.ru>
|
||||
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
|
||||
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||
Signed-off-by: liuxiangdong <liuxiangdong5@huawei.com>
|
||||
---
|
||||
block.c | 13 +++++++++++++
|
||||
block/blkdebug.c | 7 +++----
|
||||
block/blklogwrites.c | 6 ++----
|
||||
block/blkreplay.c | 6 ++----
|
||||
block/blkverify.c | 7 +++----
|
||||
block/bochs.c | 7 +++----
|
||||
block/cloop.c | 7 +++----
|
||||
block/copy-on-read.c | 8 ++++----
|
||||
block/crypto.c | 11 ++++++-----
|
||||
block/dmg.c | 7 +++----
|
||||
block/parallels.c | 7 +++----
|
||||
block/qcow.c | 6 ++----
|
||||
block/qcow2.c | 8 ++++----
|
||||
block/qed.c | 8 ++++----
|
||||
block/replication.c | 7 +++----
|
||||
block/throttle.c | 7 +++----
|
||||
block/vdi.c | 7 +++----
|
||||
block/vhdx.c | 7 +++----
|
||||
block/vmdk.c | 7 +++----
|
||||
block/vpc.c | 7 +++----
|
||||
include/block/block.h | 3 +++
|
||||
21 files changed, 76 insertions(+), 77 deletions(-)
|
||||
|
||||
diff --git a/block.c b/block.c
|
||||
index 29e504b86a..0e841dcf09 100644
|
||||
--- a/block.c
|
||||
+++ b/block.c
|
||||
@@ -2761,6 +2761,19 @@ BdrvChild *bdrv_open_child(const char *filename,
|
||||
return bdrv_attach_child(parent, bs, bdref_key, child_role, errp);
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Wrapper on bdrv_open_child() for most popular case: open primary child of bs.
|
||||
+ */
|
||||
+int bdrv_open_file_child(const char *filename,
|
||||
+ QDict *options, const char *bdref_key,
|
||||
+ BlockDriverState *parent, Error **errp)
|
||||
+{
|
||||
+ parent->file = bdrv_open_child(filename, options, bdref_key, parent,
|
||||
+ &child_file, false, errp);
|
||||
+
|
||||
+ return parent->file ? 0 : -EINVAL;
|
||||
+}
|
||||
+
|
||||
/* TODO Future callers may need to specify parent/child_role in order for
|
||||
* option inheritance to work. Existing callers use it for the root node. */
|
||||
BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
|
||||
diff --git a/block/blkdebug.c b/block/blkdebug.c
|
||||
index 5ae96c52b0..15210da7dd 100644
|
||||
--- a/block/blkdebug.c
|
||||
+++ b/block/blkdebug.c
|
||||
@@ -420,10 +420,9 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
s->state = 1;
|
||||
|
||||
/* Open the image file */
|
||||
- bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
|
||||
- bs, &child_file, false, &local_err);
|
||||
- if (local_err) {
|
||||
- ret = -EINVAL;
|
||||
+ ret = bdrv_open_file_child(qemu_opt_get(opts, "x-image"), options, "image",
|
||||
+ bs, &local_err);
|
||||
+ if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
}
|
||||
diff --git a/block/blklogwrites.c b/block/blklogwrites.c
|
||||
index 04d8b33607..81149d432d 100644
|
||||
--- a/block/blklogwrites.c
|
||||
+++ b/block/blklogwrites.c
|
||||
@@ -157,10 +157,8 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
/* Open the file */
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false,
|
||||
- &local_err);
|
||||
- if (local_err) {
|
||||
- ret = -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, &local_err);
|
||||
+ if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
diff --git a/block/blkreplay.c b/block/blkreplay.c
|
||||
index 2b7931b940..e07d137f31 100644
|
||||
--- a/block/blkreplay.c
|
||||
+++ b/block/blkreplay.c
|
||||
@@ -27,10 +27,8 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
int ret;
|
||||
|
||||
/* Open the image file */
|
||||
- bs->file = bdrv_open_child(NULL, options, "image",
|
||||
- bs, &child_file, false, &local_err);
|
||||
- if (local_err) {
|
||||
- ret = -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "image", bs, &local_err);
|
||||
+ if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
diff --git a/block/blkverify.c b/block/blkverify.c
|
||||
index 304b0a1368..65beeff3e5 100644
|
||||
--- a/block/blkverify.c
|
||||
+++ b/block/blkverify.c
|
||||
@@ -124,10 +124,9 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
/* Open the raw file */
|
||||
- bs->file = bdrv_open_child(qemu_opt_get(opts, "x-raw"), options, "raw",
|
||||
- bs, &child_file, false, &local_err);
|
||||
- if (local_err) {
|
||||
- ret = -EINVAL;
|
||||
+ ret = bdrv_open_file_child(qemu_opt_get(opts, "x-raw"), options, "raw",
|
||||
+ bs, &local_err);
|
||||
+ if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
diff --git a/block/bochs.c b/block/bochs.c
|
||||
index 962f18592d..d8833a1b94 100644
|
||||
--- a/block/bochs.c
|
||||
+++ b/block/bochs.c
|
||||
@@ -110,10 +110,9 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
return ret;
|
||||
}
|
||||
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
- false, errp);
|
||||
- if (!bs->file) {
|
||||
- return -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
|
||||
diff --git a/block/cloop.c b/block/cloop.c
|
||||
index 384c9735bb..94cfbe93b4 100644
|
||||
--- a/block/cloop.c
|
||||
+++ b/block/cloop.c
|
||||
@@ -71,10 +71,9 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
return ret;
|
||||
}
|
||||
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
- false, errp);
|
||||
- if (!bs->file) {
|
||||
- return -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
/* read header */
|
||||
diff --git a/block/copy-on-read.c b/block/copy-on-read.c
|
||||
index 6631f30205..bc1d83128e 100644
|
||||
--- a/block/copy-on-read.c
|
||||
+++ b/block/copy-on-read.c
|
||||
@@ -28,10 +28,10 @@
|
||||
static int cor_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false,
|
||||
- errp);
|
||||
- if (!bs->file) {
|
||||
- return -EINVAL;
|
||||
+ int ret;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||
diff --git a/block/crypto.c b/block/crypto.c
|
||||
index 8237424ae6..bb92854cba 100644
|
||||
--- a/block/crypto.c
|
||||
+++ b/block/crypto.c
|
||||
@@ -194,15 +194,14 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
|
||||
BlockCrypto *crypto = bs->opaque;
|
||||
QemuOpts *opts = NULL;
|
||||
Error *local_err = NULL;
|
||||
- int ret = -EINVAL;
|
||||
+ int ret;
|
||||
QCryptoBlockOpenOptions *open_opts = NULL;
|
||||
unsigned int cflags = 0;
|
||||
QDict *cryptoopts = NULL;
|
||||
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
- false, errp);
|
||||
- if (!bs->file) {
|
||||
- return -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_FUA &
|
||||
@@ -211,6 +210,7 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
|
||||
opts = qemu_opts_create(opts_spec, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
+ ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -220,6 +220,7 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
|
||||
|
||||
open_opts = block_crypto_open_opts_init(cryptoopts, errp);
|
||||
if (!open_opts) {
|
||||
+ ret = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
diff --git a/block/dmg.c b/block/dmg.c
|
||||
index 45f6b28f17..ad4141fbaa 100644
|
||||
--- a/block/dmg.c
|
||||
+++ b/block/dmg.c
|
||||
@@ -439,10 +439,9 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
return ret;
|
||||
}
|
||||
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
- false, errp);
|
||||
- if (!bs->file) {
|
||||
- return -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
block_module_load_one("dmg-bz2");
|
||||
diff --git a/block/parallels.c b/block/parallels.c
|
||||
index 00fae125d1..721cbcb168 100644
|
||||
--- a/block/parallels.c
|
||||
+++ b/block/parallels.c
|
||||
@@ -728,10 +728,9 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error *local_err = NULL;
|
||||
char *buf;
|
||||
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
- false, errp);
|
||||
- if (!bs->file) {
|
||||
- return -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph));
|
||||
diff --git a/block/qcow.c b/block/qcow.c
|
||||
index 5bdf72ba33..04776d102f 100644
|
||||
--- a/block/qcow.c
|
||||
+++ b/block/qcow.c
|
||||
@@ -130,10 +130,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
qdict_extract_subqdict(options, &encryptopts, "encrypt.");
|
||||
encryptfmt = qdict_get_try_str(encryptopts, "format");
|
||||
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
- false, errp);
|
||||
- if (!bs->file) {
|
||||
- ret = -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
||||
+ if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||
index c1992c571a..e3331570b2 100644
|
||||
--- a/block/qcow2.c
|
||||
+++ b/block/qcow2.c
|
||||
@@ -1775,11 +1775,11 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
.errp = errp,
|
||||
.ret = -EINPROGRESS
|
||||
};
|
||||
+ int ret;
|
||||
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
- false, errp);
|
||||
- if (!bs->file) {
|
||||
- return -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
/* Initialise locks */
|
||||
diff --git a/block/qed.c b/block/qed.c
|
||||
index 77c7cef175..6948b0c62f 100644
|
||||
--- a/block/qed.c
|
||||
+++ b/block/qed.c
|
||||
@@ -545,11 +545,11 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
.errp = errp,
|
||||
.ret = -EINPROGRESS
|
||||
};
|
||||
+ int ret;
|
||||
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
- false, errp);
|
||||
- if (!bs->file) {
|
||||
- return -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
bdrv_qed_init_state(bs);
|
||||
diff --git a/block/replication.c b/block/replication.c
|
||||
index 23b2993d74..52b40dfb22 100644
|
||||
--- a/block/replication.c
|
||||
+++ b/block/replication.c
|
||||
@@ -90,10 +90,9 @@ static int replication_open(BlockDriverState *bs, QDict *options,
|
||||
const char *mode;
|
||||
const char *top_id;
|
||||
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
- false, errp);
|
||||
- if (!bs->file) {
|
||||
- return -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
ret = -EINVAL;
|
||||
diff --git a/block/throttle.c b/block/throttle.c
|
||||
index 0349f42257..0b3dd23214 100644
|
||||
--- a/block/throttle.c
|
||||
+++ b/block/throttle.c
|
||||
@@ -81,10 +81,9 @@ static int throttle_open(BlockDriverState *bs, QDict *options,
|
||||
char *group;
|
||||
int ret;
|
||||
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs,
|
||||
- &child_file, false, errp);
|
||||
- if (!bs->file) {
|
||||
- return -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
}
|
||||
bs->supported_write_flags = bs->file->bs->supported_write_flags |
|
||||
BDRV_REQ_WRITE_UNCHANGED;
|
||||
diff --git a/block/vdi.c b/block/vdi.c
|
||||
index b9845a4cbd..fa7d2f41f7 100644
|
||||
--- a/block/vdi.c
|
||||
+++ b/block/vdi.c
|
||||
@@ -378,10 +378,9 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error *local_err = NULL;
|
||||
QemuUUID uuid_link, uuid_parent;
|
||||
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
- false, errp);
|
||||
- if (!bs->file) {
|
||||
- return -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
logout("\n");
|
||||
diff --git a/block/vhdx.c b/block/vhdx.c
|
||||
index d6070b6fa8..7a414de2e4 100644
|
||||
--- a/block/vhdx.c
|
||||
+++ b/block/vhdx.c
|
||||
@@ -904,10 +904,9 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
uint64_t signature;
|
||||
Error *local_err = NULL;
|
||||
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
- false, errp);
|
||||
- if (!bs->file) {
|
||||
- return -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
s->bat = NULL;
|
||||
diff --git a/block/vmdk.c b/block/vmdk.c
|
||||
index bd36ece125..d0362b85f5 100644
|
||||
--- a/block/vmdk.c
|
||||
+++ b/block/vmdk.c
|
||||
@@ -1243,10 +1243,9 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
uint32_t magic;
|
||||
Error *local_err = NULL;
|
||||
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
- false, errp);
|
||||
- if (!bs->file) {
|
||||
- return -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
buf = vmdk_read_desc(bs->file, 0, errp);
|
||||
diff --git a/block/vpc.c b/block/vpc.c
|
||||
index 3a88e28e2b..291172707b 100644
|
||||
--- a/block/vpc.c
|
||||
+++ b/block/vpc.c
|
||||
@@ -228,10 +228,9 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
int ret;
|
||||
int64_t bs_size;
|
||||
|
||||
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
- false, errp);
|
||||
- if (!bs->file) {
|
||||
- return -EINVAL;
|
||||
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
opts = qemu_opts_create(&vpc_runtime_opts, NULL, 0, &error_abort);
|
||||
diff --git a/include/block/block.h b/include/block/block.h
|
||||
index 50a07c1c33..e07b0786e2 100644
|
||||
--- a/include/block/block.h
|
||||
+++ b/include/block/block.h
|
||||
@@ -303,6 +303,9 @@ BdrvChild *bdrv_open_child(const char *filename,
|
||||
BlockDriverState* parent,
|
||||
const BdrvChildRole *child_role,
|
||||
bool allow_none, Error **errp);
|
||||
+int bdrv_open_file_child(const char *filename,
|
||||
+ QDict *options, const char *bdref_key,
|
||||
+ BlockDriverState *parent, Error **errp);
|
||||
BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp);
|
||||
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
|
||||
Error **errp);
|
||||
--
|
||||
2.41.0.windows.1
|
||||
|
||||
71
mac_dbdma-Remove-leftover-dma_memory_unmap-calls-CVE.patch
Normal file
71
mac_dbdma-Remove-leftover-dma_memory_unmap-calls-CVE.patch
Normal file
@ -0,0 +1,71 @@
|
||||
From ee07f1cc2a423040ad9a862c8054893c84db7f01 Mon Sep 17 00:00:00 2001
|
||||
From: Mattias Nissler <mnissler@rivosinc.com>
|
||||
Date: Mon, 16 Sep 2024 10:57:08 -0700
|
||||
Subject: [PATCH 5/5] mac_dbdma: Remove leftover `dma_memory_unmap`
|
||||
calls(CVE-2024-8612)
|
||||
|
||||
cherry-pick from 2d0a071e625d7234e8c5623b7e7bf445e1bef72c
|
||||
|
||||
These were passing a NULL buffer pointer unconditionally, which happens
|
||||
to behave in a mostly benign way (except for the chance of an excess
|
||||
memory region unref and a bounce buffer leak). Per the function comment,
|
||||
this was never meant to be accepted though, and triggers an assertion
|
||||
with the "softmmu: Support concurrent bounce buffers" change.
|
||||
|
||||
Given that the code in question never sets up any mappings, just remove
|
||||
the unnecessary dma_memory_unmap calls along with the DBDMA_io struct
|
||||
fields that are now entirely unused.
|
||||
|
||||
Signed-off-by: Mattias Nissler <mnissler@rivosinc.com>
|
||||
Message-Id: <20240916175708.1829059-1-mnissler@rivosinc.com>
|
||||
Fixes: be1e343995 ("macio: switch over to new byte-aligned DMA helpers")
|
||||
Reviewed-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
|
||||
Tested-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
|
||||
Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
|
||||
---
|
||||
hw/ide/macio.c | 6 ------
|
||||
include/hw/ppc/mac_dbdma.h | 4 ----
|
||||
2 files changed, 10 deletions(-)
|
||||
|
||||
diff --git a/hw/ide/macio.c b/hw/ide/macio.c
|
||||
index 54571fed12..460b8cfd6a 100644
|
||||
--- a/hw/ide/macio.c
|
||||
+++ b/hw/ide/macio.c
|
||||
@@ -118,9 +118,6 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
|
||||
return;
|
||||
|
||||
done:
|
||||
- dma_memory_unmap(&address_space_memory, io->dma_mem, io->dma_len,
|
||||
- io->dir, io->dma_len);
|
||||
-
|
||||
if (ret < 0) {
|
||||
block_acct_failed(blk_get_stats(s->blk), &s->acct);
|
||||
} else {
|
||||
@@ -201,9 +198,6 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
|
||||
return;
|
||||
|
||||
done:
|
||||
- dma_memory_unmap(&address_space_memory, io->dma_mem, io->dma_len,
|
||||
- io->dir, io->dma_len);
|
||||
-
|
||||
if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) {
|
||||
if (ret < 0) {
|
||||
block_acct_failed(blk_get_stats(s->blk), &s->acct);
|
||||
diff --git a/include/hw/ppc/mac_dbdma.h b/include/hw/ppc/mac_dbdma.h
|
||||
index 26cc469de4..38c3d87964 100644
|
||||
--- a/include/hw/ppc/mac_dbdma.h
|
||||
+++ b/include/hw/ppc/mac_dbdma.h
|
||||
@@ -43,10 +43,6 @@ struct DBDMA_io {
|
||||
DBDMA_end dma_end;
|
||||
/* DMA is in progress, don't start another one */
|
||||
bool processing;
|
||||
- /* DMA request */
|
||||
- void *dma_mem;
|
||||
- dma_addr_t dma_len;
|
||||
- DMADirection dir;
|
||||
};
|
||||
|
||||
/*
|
||||
--
|
||||
2.45.1.windows.1
|
||||
|
||||
116
main-loop.h-introduce-qemu_in_main_thread.patch
Normal file
116
main-loop.h-introduce-qemu_in_main_thread.patch
Normal file
@ -0,0 +1,116 @@
|
||||
From f0cbb49e2a44bcf5a515922b96853acc1bed3b79 Mon Sep 17 00:00:00 2001
|
||||
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
Date: Thu, 3 Mar 2022 10:15:46 -0500
|
||||
Subject: [PATCH 7/8] main-loop.h: introduce qemu_in_main_thread()
|
||||
|
||||
When invoked from the main loop, this function is the same
|
||||
as qemu_mutex_iothread_locked, and returns true if the BQL is held.
|
||||
When invoked from iothreads or tests, it returns true only
|
||||
if the current AioContext is the Main Loop.
|
||||
|
||||
This essentially just extends qemu_mutex_iothread_locked to work
|
||||
also in unit tests or other users like storage-daemon, that run
|
||||
in the Main Loop but end up using the implementation in
|
||||
stubs/iothread-lock.c.
|
||||
|
||||
Using qemu_mutex_iothread_locked in unit tests defaults to false
|
||||
because they use the implementation in stubs/iothread-lock,
|
||||
making all assertions added in next patches fail despite the
|
||||
AioContext is still the main loop.
|
||||
|
||||
See the comment in the function header for more information.
|
||||
|
||||
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
Message-Id: <20220303151616.325444-2-eesposit@redhat.com>
|
||||
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||
---
|
||||
cpus.c | 5 +++++
|
||||
include/qemu/main-loop.h | 24 ++++++++++++++++++++++++
|
||||
stubs/Makefile.objs | 1 +
|
||||
stubs/iothread-lock-block.c | 8 ++++++++
|
||||
4 files changed, 38 insertions(+)
|
||||
create mode 100644 stubs/iothread-lock-block.c
|
||||
|
||||
diff --git a/cpus.c b/cpus.c
|
||||
index b2a26a1f11..d2d486129d 100644
|
||||
--- a/cpus.c
|
||||
+++ b/cpus.c
|
||||
@@ -1848,6 +1848,11 @@ bool qemu_mutex_iothread_locked(void)
|
||||
return iothread_locked;
|
||||
}
|
||||
|
||||
+bool qemu_in_main_thread(void)
|
||||
+{
|
||||
+ return qemu_mutex_iothread_locked();
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* The BQL is taken from so many places that it is worth profiling the
|
||||
* callers directly, instead of funneling them all through a single function.
|
||||
diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
|
||||
index cba048bc82..42d65cf270 100644
|
||||
--- a/include/qemu/main-loop.h
|
||||
+++ b/include/qemu/main-loop.h
|
||||
@@ -260,9 +260,33 @@ int qemu_add_child_watch(pid_t pid);
|
||||
* must always be taken outside other locks. This function helps
|
||||
* functions take different paths depending on whether the current
|
||||
* thread is running within the main loop mutex.
|
||||
+ *
|
||||
+ * This function should never be used in the block layer, because
|
||||
+ * unit tests, block layer tools and qemu-storage-daemon do not
|
||||
+ * have a BQL.
|
||||
+ * Please instead refer to qemu_in_main_thread().
|
||||
*/
|
||||
bool qemu_mutex_iothread_locked(void);
|
||||
|
||||
+/**
|
||||
+ * qemu_in_main_thread: return whether it's possible to safely access
|
||||
+ * the global state of the block layer.
|
||||
+ *
|
||||
+ * Global state of the block layer is not accessible from I/O threads
|
||||
+ * or worker threads; only from threads that "own" the default
|
||||
+ * AioContext that qemu_get_aio_context() returns. For tests, block
|
||||
+ * layer tools and qemu-storage-daemon there is a designated thread that
|
||||
+ * runs the event loop for qemu_get_aio_context(), and that is the
|
||||
+ * main thread.
|
||||
+ *
|
||||
+ * For emulators, however, any thread that holds the BQL can act
|
||||
+ * as the block layer main thread; this will be any of the actual
|
||||
+ * main thread, the vCPU threads or the RCU thread.
|
||||
+ *
|
||||
+ * For clarity, do not use this function outside the block layer.
|
||||
+ */
|
||||
+bool qemu_in_main_thread(void);
|
||||
+
|
||||
/**
|
||||
* qemu_mutex_lock_iothread: Lock the main loop mutex.
|
||||
*
|
||||
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
|
||||
index 9c7393b08c..e748ccd323 100644
|
||||
--- a/stubs/Makefile.objs
|
||||
+++ b/stubs/Makefile.objs
|
||||
@@ -11,6 +11,7 @@ stub-obj-y += gdbstub.o
|
||||
stub-obj-y += get-vm-name.o
|
||||
stub-obj-y += iothread.o
|
||||
stub-obj-y += iothread-lock.o
|
||||
+stub-obj-y += iothread-lock-block.o
|
||||
stub-obj-y += is-daemonized.o
|
||||
stub-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
||||
stub-obj-y += machine-init-done.o
|
||||
diff --git a/stubs/iothread-lock-block.c b/stubs/iothread-lock-block.c
|
||||
new file mode 100644
|
||||
index 0000000000..c88ed70462
|
||||
--- /dev/null
|
||||
+++ b/stubs/iothread-lock-block.c
|
||||
@@ -0,0 +1,8 @@
|
||||
+#include "qemu/osdep.h"
|
||||
+#include "qemu/main-loop.h"
|
||||
+
|
||||
+bool qemu_in_main_thread(void)
|
||||
+{
|
||||
+ return qemu_get_current_aio_context() == qemu_get_aio_context();
|
||||
+}
|
||||
+
|
||||
--
|
||||
2.45.1.windows.1
|
||||
|
||||
167
nbd-Add-max-connections-to-nbd-server-start.patch
Normal file
167
nbd-Add-max-connections-to-nbd-server-start.patch
Normal file
@ -0,0 +1,167 @@
|
||||
From 6a3b58ac04f70089d2a96a874d7213f63d808093 Mon Sep 17 00:00:00 2001
|
||||
From: Kevin Wolf <kwolf@redhat.com>
|
||||
Date: Thu, 24 Sep 2020 17:26:54 +0200
|
||||
Subject: [PATCH 3/8] nbd: Add max-connections to nbd-server-start
|
||||
|
||||
This is a QMP equivalent of qemu-nbd's --shared option, limiting the
|
||||
maximum number of clients that can attach at the same time.
|
||||
|
||||
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||
Reviewed-by: Eric Blake <eblake@redhat.com>
|
||||
Message-Id: <20200924152717.287415-9-kwolf@redhat.com>
|
||||
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||
Signed-off-by: liuxiangdong <liuxiangdong5@huawei.com>
|
||||
---
|
||||
blockdev-nbd.c | 30 ++++++++++++++++++++++++------
|
||||
include/block/nbd.h | 3 ++-
|
||||
monitor/hmp-cmds.c | 2 +-
|
||||
qapi/block.json | 5 ++++-
|
||||
4 files changed, 31 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
|
||||
index 0c14f033d2..c73a39fae9 100644
|
||||
--- a/blockdev-nbd.c
|
||||
+++ b/blockdev-nbd.c
|
||||
@@ -24,18 +24,28 @@ typedef struct NBDServerData {
|
||||
QIONetListener *listener;
|
||||
QCryptoTLSCreds *tlscreds;
|
||||
char *tlsauthz;
|
||||
+ uint32_t max_connections;
|
||||
+ uint32_t connections;
|
||||
} NBDServerData;
|
||||
|
||||
static NBDServerData *nbd_server;
|
||||
|
||||
+static void nbd_update_server_watch(NBDServerData *s);
|
||||
+
|
||||
static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
|
||||
{
|
||||
nbd_client_put(client);
|
||||
+ assert(nbd_server->connections > 0);
|
||||
+ nbd_server->connections--;
|
||||
+ nbd_update_server_watch(nbd_server);
|
||||
}
|
||||
|
||||
static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
|
||||
gpointer opaque)
|
||||
{
|
||||
+ nbd_server->connections++;
|
||||
+ nbd_update_server_watch(nbd_server);
|
||||
+
|
||||
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
|
||||
/* TODO - expose handshake timeout as QMP option */
|
||||
nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS,
|
||||
@@ -43,6 +53,14 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
|
||||
nbd_blockdev_client_closed, NULL);
|
||||
}
|
||||
|
||||
+static void nbd_update_server_watch(NBDServerData *s)
|
||||
+{
|
||||
+ if (!s->max_connections || s->connections < s->max_connections) {
|
||||
+ qio_net_listener_set_client_func(s->listener, nbd_accept, NULL, NULL);
|
||||
+ } else {
|
||||
+ qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
|
||||
+ }
|
||||
+}
|
||||
|
||||
static void nbd_server_free(NBDServerData *server)
|
||||
{
|
||||
@@ -91,7 +109,8 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
|
||||
|
||||
|
||||
void nbd_server_start(SocketAddress *addr, const char *tls_creds,
|
||||
- const char *tls_authz, Error **errp)
|
||||
+ const char *tls_authz, uint32_t max_connections,
|
||||
+ Error **errp)
|
||||
{
|
||||
if (nbd_server) {
|
||||
error_setg(errp, "NBD server already running");
|
||||
@@ -99,6 +118,7 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds,
|
||||
}
|
||||
|
||||
nbd_server = g_new0(NBDServerData, 1);
|
||||
+ nbd_server->max_connections = max_connections;
|
||||
nbd_server->listener = qio_net_listener_new();
|
||||
|
||||
qio_net_listener_set_name(nbd_server->listener,
|
||||
@@ -123,10 +143,7 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds,
|
||||
|
||||
nbd_server->tlsauthz = g_strdup(tls_authz);
|
||||
|
||||
- qio_net_listener_set_client_func(nbd_server->listener,
|
||||
- nbd_accept,
|
||||
- NULL,
|
||||
- NULL);
|
||||
+ nbd_update_server_watch(nbd_server);
|
||||
|
||||
return;
|
||||
|
||||
@@ -138,11 +155,12 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds,
|
||||
void qmp_nbd_server_start(SocketAddressLegacy *addr,
|
||||
bool has_tls_creds, const char *tls_creds,
|
||||
bool has_tls_authz, const char *tls_authz,
|
||||
+ bool has_max_connections, uint32_t max_connections,
|
||||
Error **errp)
|
||||
{
|
||||
SocketAddress *addr_flat = socket_address_flatten(addr);
|
||||
|
||||
- nbd_server_start(addr_flat, tls_creds, tls_authz, errp);
|
||||
+ nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp);
|
||||
qapi_free_SocketAddress(addr_flat);
|
||||
}
|
||||
|
||||
diff --git a/include/block/nbd.h b/include/block/nbd.h
|
||||
index 68667c31c8..d6fd188546 100644
|
||||
--- a/include/block/nbd.h
|
||||
+++ b/include/block/nbd.h
|
||||
@@ -355,7 +355,8 @@ void nbd_client_get(NBDClient *client);
|
||||
void nbd_client_put(NBDClient *client);
|
||||
|
||||
void nbd_server_start(SocketAddress *addr, const char *tls_creds,
|
||||
- const char *tls_authz, Error **errp);
|
||||
+ const char *tls_authz, uint32_t max_connections,
|
||||
+ Error **errp);
|
||||
|
||||
/* nbd_read
|
||||
* Reads @size bytes from @ioc. Returns 0 on success.
|
||||
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
|
||||
index 5ca3ebe942..bf468fe8eb 100644
|
||||
--- a/monitor/hmp-cmds.c
|
||||
+++ b/monitor/hmp-cmds.c
|
||||
@@ -2365,7 +2365,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
- nbd_server_start(addr, NULL, NULL, &local_err);
|
||||
+ nbd_server_start(addr, NULL, NULL, 0, &local_err);
|
||||
qapi_free_SocketAddress(addr);
|
||||
if (local_err != NULL) {
|
||||
goto exit;
|
||||
diff --git a/qapi/block.json b/qapi/block.json
|
||||
index 145c268bb6..e25a2a75a4 100644
|
||||
--- a/qapi/block.json
|
||||
+++ b/qapi/block.json
|
||||
@@ -230,6 +230,8 @@
|
||||
# is only resolved at time of use, so can be deleted and
|
||||
# recreated on the fly while the NBD server is active.
|
||||
# If missing, it will default to denying access (since 4.0).
|
||||
+# @max-connections: The maximum number of connections to allow at the same
|
||||
+# time, 0 for unlimited. (since 5.2; default: 0)
|
||||
#
|
||||
# Returns: error if the server is already running.
|
||||
#
|
||||
@@ -238,7 +240,8 @@
|
||||
{ 'command': 'nbd-server-start',
|
||||
'data': { 'addr': 'SocketAddressLegacy',
|
||||
'*tls-creds': 'str',
|
||||
- '*tls-authz': 'str'} }
|
||||
+ '*tls-authz': 'str',
|
||||
+ '*max-connections': 'uint32' } }
|
||||
|
||||
##
|
||||
# @nbd-server-add:
|
||||
--
|
||||
2.45.1.windows.1
|
||||
|
||||
49
nbd-Minor-style-and-typo-fixes.patch
Normal file
49
nbd-Minor-style-and-typo-fixes.patch
Normal file
@ -0,0 +1,49 @@
|
||||
From fc5f3bf2fb7eac6b348fd75407c6fd102f8459da Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Thu, 1 Aug 2024 16:49:20 -0500
|
||||
Subject: [PATCH 1/8] nbd: Minor style and typo fixes
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Touch up a comment with the wrong type name, and an over-long line,
|
||||
both noticed while working on upcoming patches.
|
||||
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20240807174943.771624-10-eblake@redhat.com>
|
||||
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
|
||||
---
|
||||
nbd/server.c | 2 +-
|
||||
qemu-nbd.c | 3 ++-
|
||||
2 files changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/nbd/server.c b/nbd/server.c
|
||||
index 2d81248967..b617e6a1a1 100644
|
||||
--- a/nbd/server.c
|
||||
+++ b/nbd/server.c
|
||||
@@ -1583,7 +1583,7 @@ void nbd_export_close(NBDExport *exp)
|
||||
|
||||
nbd_export_get(exp);
|
||||
/*
|
||||
- * TODO: Should we expand QMP NbdServerRemoveNode enum to allow a
|
||||
+ * TODO: Should we expand QMP BlockExportRemoveMode enum to allow a
|
||||
* close mode that stops advertising the export to new clients but
|
||||
* still permits existing clients to run to completion? Because of
|
||||
* that possibility, nbd_export_close() can be called more than
|
||||
diff --git a/qemu-nbd.c b/qemu-nbd.c
|
||||
index a8cb39e510..2dc2e8ea55 100644
|
||||
--- a/qemu-nbd.c
|
||||
+++ b/qemu-nbd.c
|
||||
@@ -657,7 +657,8 @@ int main(int argc, char **argv)
|
||||
pthread_t client_thread;
|
||||
const char *fmt = NULL;
|
||||
Error *local_err = NULL;
|
||||
- BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
|
||||
+ BlockdevDetectZeroesOptions detect_zeroes =
|
||||
+ BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
|
||||
QDict *options = NULL;
|
||||
const char *export_name = NULL; /* defaults to "" later for server mode */
|
||||
const char *export_description = NULL;
|
||||
--
|
||||
2.45.1.windows.1
|
||||
|
||||
90
nbd-server-CVE-2024-7409-Avoid-use-after-free-when-c.patch
Normal file
90
nbd-server-CVE-2024-7409-Avoid-use-after-free-when-c.patch
Normal file
@ -0,0 +1,90 @@
|
||||
From a9fb187686c5f431615a6f48a27806138cc43eb0 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Thu, 22 Aug 2024 09:35:29 -0500
|
||||
Subject: [PATCH] nbd/server: CVE-2024-7409: Avoid use-after-free when closing
|
||||
server
|
||||
|
||||
Commit 3e7ef738 plugged the use-after-free of the global nbd_server
|
||||
object, but overlooked a use-after-free of nbd_server->listener.
|
||||
Although this race is harder to hit, notice that our shutdown path
|
||||
first drops the reference count of nbd_server->listener, then triggers
|
||||
actions that can result in a pending client reaching the
|
||||
nbd_blockdev_client_closed() callback, which in turn calls
|
||||
qio_net_listener_set_client_func on a potentially stale object.
|
||||
|
||||
If we know we don't want any more clients to connect, and have already
|
||||
told the listener socket to shut down, then we should not be trying to
|
||||
update the listener socket's associated function.
|
||||
|
||||
Reproducer:
|
||||
|
||||
> #!/usr/bin/python3
|
||||
>
|
||||
> import os
|
||||
> from threading import Thread
|
||||
>
|
||||
> def start_stop():
|
||||
> while 1:
|
||||
> os.system('virsh qemu-monitor-command VM \'{"execute": "nbd-server-start",
|
||||
+"arguments":{"addr":{"type":"unix","data":{"path":"/tmp/nbd-sock"}}}}\'')
|
||||
> os.system('virsh qemu-monitor-command VM \'{"execute": "nbd-server-stop"}\'')
|
||||
>
|
||||
> def nbd_list():
|
||||
> while 1:
|
||||
> os.system('/path/to/build/qemu-nbd -L -k /tmp/nbd-sock')
|
||||
>
|
||||
> def test():
|
||||
> sst = Thread(target=start_stop)
|
||||
> sst.start()
|
||||
> nlt = Thread(target=nbd_list)
|
||||
> nlt.start()
|
||||
>
|
||||
> sst.join()
|
||||
> nlt.join()
|
||||
>
|
||||
> test()
|
||||
|
||||
Fixes: CVE-2024-7409
|
||||
Fixes: 3e7ef738c8 ("nbd/server: CVE-2024-7409: Close stray clients at server-stop")
|
||||
CC: qemu-stable@nongnu.org
|
||||
Reported-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20240822143617.800419-2-eblake@redhat.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
---
|
||||
blockdev-nbd.c | 12 ++++++++----
|
||||
1 file changed, 8 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
|
||||
index 09ad0bdffb..ec51c637d4 100644
|
||||
--- a/blockdev-nbd.c
|
||||
+++ b/blockdev-nbd.c
|
||||
@@ -75,10 +75,13 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
|
||||
|
||||
static void nbd_update_server_watch(NBDServerData *s)
|
||||
{
|
||||
- if (!s->max_connections || s->connections < s->max_connections) {
|
||||
- qio_net_listener_set_client_func(s->listener, nbd_accept, NULL, NULL);
|
||||
- } else {
|
||||
- qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
|
||||
+ if (s->listener) {
|
||||
+ if (!s->max_connections || s->connections < s->max_connections) {
|
||||
+ qio_net_listener_set_client_func(s->listener, nbd_accept, NULL,
|
||||
+ NULL);
|
||||
+ } else {
|
||||
+ qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +99,7 @@ static void nbd_server_free(NBDServerData *server)
|
||||
*/
|
||||
qio_net_listener_disconnect(server->listener);
|
||||
object_unref(OBJECT(server->listener));
|
||||
+ server->listener = NULL;
|
||||
QLIST_FOREACH_SAFE(conn, &server->conns, next, tmp) {
|
||||
qio_channel_shutdown(QIO_CHANNEL(conn->cioc), QIO_CHANNEL_SHUTDOWN_BOTH,
|
||||
NULL);
|
||||
--
|
||||
2.41.0.windows.1
|
||||
|
||||
139
nbd-server-CVE-2024-7409-Cap-default-max-connections.patch
Normal file
139
nbd-server-CVE-2024-7409-Cap-default-max-connections.patch
Normal file
@ -0,0 +1,139 @@
|
||||
From 6b5fc2e0040eca99684531565740236b3bb999eb Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Tue, 6 Aug 2024 13:53:00 -0500
|
||||
Subject: [PATCH 4/8] nbd/server: CVE-2024-7409: Cap default max-connections to
|
||||
100
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Allowing an unlimited number of clients to any web service is a recipe
|
||||
for a rudimentary denial of service attack: the client merely needs to
|
||||
open lots of sockets without closing them, until qemu no longer has
|
||||
any more fds available to allocate.
|
||||
|
||||
For qemu-nbd, we default to allowing only 1 connection unless more are
|
||||
explicitly asked for (-e or --shared); this was historically picked as
|
||||
a nice default (without an explicit -t, a non-persistent qemu-nbd goes
|
||||
away after a client disconnects, without needing any additional
|
||||
follow-up commands), and we are not going to change that interface now
|
||||
(besides, someday we want to point people towards qemu-storage-daemon
|
||||
instead of qemu-nbd).
|
||||
|
||||
But for qemu proper, and the newer qemu-storage-daemon, the QMP
|
||||
nbd-server-start command has historically had a default of unlimited
|
||||
number of connections, in part because unlike qemu-nbd it is
|
||||
inherently persistent until nbd-server-stop. Allowing multiple client
|
||||
sockets is particularly useful for clients that can take advantage of
|
||||
MULTI_CONN (creating parallel sockets to increase throughput),
|
||||
although known clients that do so (such as libnbd's nbdcopy) typically
|
||||
use only 8 or 16 connections (the benefits of scaling diminish once
|
||||
more sockets are competing for kernel attention). Picking a number
|
||||
large enough for typical use cases, but not unlimited, makes it
|
||||
slightly harder for a malicious client to perform a denial of service
|
||||
merely by opening lots of connections withot progressing through the
|
||||
handshake.
|
||||
|
||||
This change does not eliminate CVE-2024-7409 on its own, but reduces
|
||||
the chance for fd exhaustion or unlimited memory usage as an attack
|
||||
surface. On the other hand, by itself, it makes it more obvious that
|
||||
with a finite limit, we have the problem of an unauthenticated client
|
||||
holding 100 fds opened as a way to block out a legitimate client from
|
||||
being able to connect; thus, later patches will further add timeouts
|
||||
to reject clients that are not making progress.
|
||||
|
||||
This is an INTENTIONAL change in behavior, and will break any client
|
||||
of nbd-server-start that was not passing an explicit max-connections
|
||||
parameter, yet expects more than 100 simultaneous connections. We are
|
||||
not aware of any such client (as stated above, most clients aware of
|
||||
MULTI_CONN get by just fine on 8 or 16 connections, and probably cope
|
||||
with later connections failing by relying on the earlier connections;
|
||||
libvirt has not yet been passing max-connections, but generally
|
||||
creates NBD servers with the intent for a single client for the sake
|
||||
of live storage migration; meanwhile, the KubeSAN project anticipates
|
||||
a large cluster sharing multiple clients [up to 8 per node, and up to
|
||||
100 nodes in a cluster], but it currently uses qemu-nbd with an
|
||||
explicit --shared=0 rather than qemu-storage-daemon with
|
||||
nbd-server-start).
|
||||
|
||||
We considered using a deprecation period (declare that omitting
|
||||
max-parameters is deprecated, and make it mandatory in 3 releases -
|
||||
then we don't need to pick an arbitrary default); that has zero risk
|
||||
of breaking any apps that accidentally depended on more than 100
|
||||
connections, and where such breakage might not be noticed under unit
|
||||
testing but only under the larger loads of production usage. But it
|
||||
does not close the denial-of-service hole until far into the future,
|
||||
and requires all apps to change to add the parameter even if 100 was
|
||||
good enough. It also has a drawback that any app (like libvirt) that
|
||||
is accidentally relying on an unlimited default should seriously
|
||||
consider their own CVE now, at which point they are going to change to
|
||||
pass explicit max-connections sooner than waiting for 3 qemu releases.
|
||||
Finally, if our changed default breaks an app, that app can always
|
||||
pass in an explicit max-parameters with a larger value.
|
||||
|
||||
It is also intentional that the HMP interface to nbd-server-start is
|
||||
not changed to expose max-connections (any client needing to fine-tune
|
||||
things should be using QMP).
|
||||
|
||||
Suggested-by: Daniel P. Berrangé <berrange@redhat.com>
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20240807174943.771624-12-eblake@redhat.com>
|
||||
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
|
||||
[ericb: Expand commit message to summarize Dan's argument for why we
|
||||
break corner-case back-compat behavior without a deprecation period]
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
blockdev-nbd.c | 4 ++++
|
||||
include/block/nbd.h | 7 +++++++
|
||||
qapi/block.json | 2 +-
|
||||
3 files changed, 12 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
|
||||
index c73a39fae9..67bb3fe4ce 100644
|
||||
--- a/blockdev-nbd.c
|
||||
+++ b/blockdev-nbd.c
|
||||
@@ -160,6 +160,10 @@ void qmp_nbd_server_start(SocketAddressLegacy *addr,
|
||||
{
|
||||
SocketAddress *addr_flat = socket_address_flatten(addr);
|
||||
|
||||
+ if (!has_max_connections) {
|
||||
+ max_connections = NBD_DEFAULT_MAX_CONNECTIONS;
|
||||
+ }
|
||||
+
|
||||
nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp);
|
||||
qapi_free_SocketAddress(addr_flat);
|
||||
}
|
||||
diff --git a/include/block/nbd.h b/include/block/nbd.h
|
||||
index d6fd188546..d768ccb592 100644
|
||||
--- a/include/block/nbd.h
|
||||
+++ b/include/block/nbd.h
|
||||
@@ -31,6 +31,13 @@
|
||||
*/
|
||||
#define NBD_DEFAULT_HANDSHAKE_MAX_SECS 10
|
||||
|
||||
+/*
|
||||
+ * NBD_DEFAULT_MAX_CONNECTIONS: Number of client sockets to allow at
|
||||
+ * once; must be large enough to allow a MULTI_CONN-aware client like
|
||||
+ * nbdcopy to create its typical number of 8-16 sockets.
|
||||
+ */
|
||||
+#define NBD_DEFAULT_MAX_CONNECTIONS 100
|
||||
+
|
||||
/* Handshake phase structs - this struct is passed on the wire */
|
||||
|
||||
struct NBDOption {
|
||||
diff --git a/qapi/block.json b/qapi/block.json
|
||||
index e25a2a75a4..9e1356409b 100644
|
||||
--- a/qapi/block.json
|
||||
+++ b/qapi/block.json
|
||||
@@ -231,7 +231,7 @@
|
||||
# recreated on the fly while the NBD server is active.
|
||||
# If missing, it will default to denying access (since 4.0).
|
||||
# @max-connections: The maximum number of connections to allow at the same
|
||||
-# time, 0 for unlimited. (since 5.2; default: 0)
|
||||
+# time, 0 for unlimited. (since 5.2; default: 100)
|
||||
#
|
||||
# Returns: error if the server is already running.
|
||||
#
|
||||
--
|
||||
2.45.1.windows.1
|
||||
|
||||
163
nbd-server-CVE-2024-7409-Close-stray-clients-at-serv.patch
Normal file
163
nbd-server-CVE-2024-7409-Close-stray-clients-at-serv.patch
Normal file
@ -0,0 +1,163 @@
|
||||
From 9178d429f77b14343fa9489949a0e8126e34890f Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Wed, 7 Aug 2024 12:23:13 -0500
|
||||
Subject: [PATCH 8/8] nbd/server: CVE-2024-7409: Close stray clients at
|
||||
server-stop
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
A malicious client can attempt to connect to an NBD server, and then
|
||||
intentionally delay progress in the handshake, including if it does
|
||||
not know the TLS secrets. Although the previous two patches reduce
|
||||
this behavior by capping the default max-connections parameter and
|
||||
killing slow clients, they did not eliminate the possibility of a
|
||||
client waiting to close the socket until after the QMP nbd-server-stop
|
||||
command is executed, at which point qemu would SEGV when trying to
|
||||
dereference the NULL nbd_server global which is no longer present.
|
||||
This amounts to a denial of service attack. Worse, if another NBD
|
||||
server is started before the malicious client disconnects, I cannot
|
||||
rule out additional adverse effects when the old client interferes
|
||||
with the connection count of the new server (although the most likely
|
||||
is a crash due to an assertion failure when checking
|
||||
nbd_server->connections > 0).
|
||||
|
||||
For environments without this patch, the CVE can be mitigated by
|
||||
ensuring (such as via a firewall) that only trusted clients can
|
||||
connect to an NBD server. Note that using frameworks like libvirt
|
||||
that ensure that TLS is used and that nbd-server-stop is not executed
|
||||
while any trusted clients are still connected will only help if there
|
||||
is also no possibility for an untrusted client to open a connection
|
||||
but then stall on the NBD handshake.
|
||||
|
||||
Given the previous patches, it would be possible to guarantee that no
|
||||
clients remain connected by having nbd-server-stop sleep for longer
|
||||
than the default handshake deadline before finally freeing the global
|
||||
nbd_server object, but that could make QMP non-responsive for a long
|
||||
time. So intead, this patch fixes the problem by tracking all client
|
||||
sockets opened while the server is running, and forcefully closing any
|
||||
such sockets remaining without a completed handshake at the time of
|
||||
nbd-server-stop, then waiting until the coroutines servicing those
|
||||
sockets notice the state change. nbd-server-stop now has a second
|
||||
AIO_WAIT_WHILE_UNLOCKED (the first is indirectly through the
|
||||
blk_exp_close_all_type() that disconnects all clients that completed
|
||||
handshakes), but forced socket shutdown is enough to progress the
|
||||
coroutines and quickly tear down all clients before the server is
|
||||
freed, thus finally fixing the CVE.
|
||||
|
||||
This patch relies heavily on the fact that nbd/server.c guarantees
|
||||
that it only calls nbd_blockdev_client_closed() from the main loop
|
||||
(see the assertion in nbd_client_put() and the hoops used in
|
||||
nbd_client_put_nonzero() to achieve that); if we did not have that
|
||||
guarantee, we would also need a mutex protecting our accesses of the
|
||||
list of connections to survive re-entrancy from independent iothreads.
|
||||
|
||||
Although I did not actually try to test old builds, it looks like this
|
||||
problem has existed since at least commit 862172f45c (v2.12.0, 2017) -
|
||||
even back when that patch started using a QIONetListener to handle
|
||||
listening on multiple sockets, nbd_server_free() was already unaware
|
||||
that the nbd_blockdev_client_closed callback can be reached later by a
|
||||
client thread that has not completed handshakes (and therefore the
|
||||
client's socket never got added to the list closed in
|
||||
nbd_export_close_all), despite that patch intentionally tearing down
|
||||
the QIONetListener to prevent new clients.
|
||||
|
||||
Reported-by: Alexander Ivanov <alexander.ivanov@virtuozzo.com>
|
||||
Fixes: CVE-2024-7409
|
||||
CC: qemu-stable@nongnu.org
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20240807174943.771624-14-eblake@redhat.com>
|
||||
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
|
||||
---
|
||||
blockdev-nbd.c | 35 ++++++++++++++++++++++++++++++++++-
|
||||
1 file changed, 34 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
|
||||
index 67bb3fe4ce..09ad0bdffb 100644
|
||||
--- a/blockdev-nbd.c
|
||||
+++ b/blockdev-nbd.c
|
||||
@@ -20,12 +20,18 @@
|
||||
#include "io/channel-socket.h"
|
||||
#include "io/net-listener.h"
|
||||
|
||||
+typedef struct NBDConn {
|
||||
+ QIOChannelSocket *cioc;
|
||||
+ QLIST_ENTRY(NBDConn) next;
|
||||
+} NBDConn;
|
||||
+
|
||||
typedef struct NBDServerData {
|
||||
QIONetListener *listener;
|
||||
QCryptoTLSCreds *tlscreds;
|
||||
char *tlsauthz;
|
||||
uint32_t max_connections;
|
||||
uint32_t connections;
|
||||
+ QLIST_HEAD(, NBDConn) conns;
|
||||
} NBDServerData;
|
||||
|
||||
static NBDServerData *nbd_server;
|
||||
@@ -34,6 +40,14 @@ static void nbd_update_server_watch(NBDServerData *s);
|
||||
|
||||
static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
|
||||
{
|
||||
+ NBDConn *conn = nbd_client_owner(client);
|
||||
+
|
||||
+ assert(qemu_in_main_thread() && nbd_server);
|
||||
+
|
||||
+ object_unref(OBJECT(conn->cioc));
|
||||
+ QLIST_REMOVE(conn, next);
|
||||
+ g_free(conn);
|
||||
+
|
||||
nbd_client_put(client);
|
||||
assert(nbd_server->connections > 0);
|
||||
nbd_server->connections--;
|
||||
@@ -43,14 +57,20 @@ static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
|
||||
static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
|
||||
gpointer opaque)
|
||||
{
|
||||
+ NBDConn *conn = g_new0(NBDConn, 1);
|
||||
+
|
||||
+ assert(qemu_in_main_thread() && nbd_server);
|
||||
nbd_server->connections++;
|
||||
+ object_ref(OBJECT(cioc));
|
||||
+ conn->cioc = cioc;
|
||||
+ QLIST_INSERT_HEAD(&nbd_server->conns, conn, next);
|
||||
nbd_update_server_watch(nbd_server);
|
||||
|
||||
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
|
||||
/* TODO - expose handshake timeout as QMP option */
|
||||
nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS,
|
||||
nbd_server->tlscreds, nbd_server->tlsauthz,
|
||||
- nbd_blockdev_client_closed, NULL);
|
||||
+ nbd_blockdev_client_closed, conn);
|
||||
}
|
||||
|
||||
static void nbd_update_server_watch(NBDServerData *s)
|
||||
@@ -64,12 +84,25 @@ static void nbd_update_server_watch(NBDServerData *s)
|
||||
|
||||
static void nbd_server_free(NBDServerData *server)
|
||||
{
|
||||
+ NBDConn *conn, *tmp;
|
||||
+
|
||||
if (!server) {
|
||||
return;
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * Forcefully close the listener socket, and any clients that have
|
||||
+ * not yet disconnected on their own.
|
||||
+ */
|
||||
qio_net_listener_disconnect(server->listener);
|
||||
object_unref(OBJECT(server->listener));
|
||||
+ QLIST_FOREACH_SAFE(conn, &server->conns, next, tmp) {
|
||||
+ qio_channel_shutdown(QIO_CHANNEL(conn->cioc), QIO_CHANNEL_SHUTDOWN_BOTH,
|
||||
+ NULL);
|
||||
+ }
|
||||
+
|
||||
+ AIO_WAIT_WHILE_UNLOCKED(NULL, server->connections > 0);
|
||||
+
|
||||
if (server->tlscreds) {
|
||||
object_unref(OBJECT(server->tlscreds));
|
||||
}
|
||||
--
|
||||
2.45.1.windows.1
|
||||
|
||||
121
nbd-server-CVE-2024-7409-Drop-non-negotiating-client.patch
Normal file
121
nbd-server-CVE-2024-7409-Drop-non-negotiating-client.patch
Normal file
@ -0,0 +1,121 @@
|
||||
From 5b8f6de9445b18b0eac122b536a3fcca77a3ed1f Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Thu, 8 Aug 2024 16:05:08 -0500
|
||||
Subject: [PATCH 5/8] nbd/server: CVE-2024-7409: Drop non-negotiating clients
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
A client that opens a socket but does not negotiate is merely hogging
|
||||
qemu's resources (an open fd and a small amount of memory); and a
|
||||
malicious client that can access the port where NBD is listening can
|
||||
attempt a denial of service attack by intentionally opening and
|
||||
abandoning lots of unfinished connections. The previous patch put a
|
||||
default bound on the number of such ongoing connections, but once that
|
||||
limit is hit, no more clients can connect (including legitimate ones).
|
||||
The solution is to insist that clients complete handshake within a
|
||||
reasonable time limit, defaulting to 10 seconds. A client that has
|
||||
not successfully completed NBD_OPT_GO by then (including the case of
|
||||
where the client didn't know TLS credentials to even reach the point
|
||||
of NBD_OPT_GO) is wasting our time and does not deserve to stay
|
||||
connected. Later patches will allow fine-tuning the limit away from
|
||||
the default value (including disabling it for doing integration
|
||||
testing of the handshake process itself).
|
||||
|
||||
Note that this patch in isolation actually makes it more likely to see
|
||||
qemu SEGV after nbd-server-stop, as any client socket still connected
|
||||
when the server shuts down will now be closed after 10 seconds rather
|
||||
than at the client's whims. That will be addressed in the next patch.
|
||||
|
||||
For a demo of this patch in action:
|
||||
$ qemu-nbd -f raw -r -t -e 10 file &
|
||||
$ nbdsh --opt-mode -c '
|
||||
H = list()
|
||||
for i in range(20):
|
||||
print(i)
|
||||
H.insert(i, nbd.NBD())
|
||||
H[i].set_opt_mode(True)
|
||||
H[i].connect_uri("nbd://localhost")
|
||||
'
|
||||
$ kill $!
|
||||
|
||||
where later connections get to start progressing once earlier ones are
|
||||
forcefully dropped for taking too long, rather than hanging.
|
||||
|
||||
Suggested-by: Daniel P. Berrangé <berrange@redhat.com>
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20240807174943.771624-13-eblake@redhat.com>
|
||||
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
|
||||
[eblake: rebase to changes earlier in series, reduce scope of timer]
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
nbd/server.c | 28 +++++++++++++++++++++++++++-
|
||||
nbd/trace-events | 1 +
|
||||
2 files changed, 28 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/nbd/server.c b/nbd/server.c
|
||||
index 68d78888c6..112b987440 100644
|
||||
--- a/nbd/server.c
|
||||
+++ b/nbd/server.c
|
||||
@@ -2416,22 +2416,48 @@ static void nbd_client_receive_next_request(NBDClient *client)
|
||||
}
|
||||
}
|
||||
|
||||
+static void nbd_handshake_timer_cb(void *opaque)
|
||||
+{
|
||||
+ QIOChannel *ioc = opaque;
|
||||
+
|
||||
+ trace_nbd_handshake_timer_cb();
|
||||
+ qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
|
||||
+}
|
||||
+
|
||||
static coroutine_fn void nbd_co_client_start(void *opaque)
|
||||
{
|
||||
NBDClient *client = opaque;
|
||||
Error *local_err = NULL;
|
||||
+ QEMUTimer *handshake_timer = NULL;
|
||||
|
||||
qemu_co_mutex_init(&client->send_lock);
|
||||
|
||||
- /* TODO - utilize client->handshake_max_secs */
|
||||
+ /*
|
||||
+ * Create a timer to bound the time spent in negotiation. If the
|
||||
+ * timer expires, it is likely nbd_negotiate will fail because the
|
||||
+ * socket was shutdown.
|
||||
+ */
|
||||
+ if (client->handshake_max_secs > 0) {
|
||||
+ handshake_timer = aio_timer_new(qemu_get_aio_context(),
|
||||
+ QEMU_CLOCK_REALTIME,
|
||||
+ SCALE_NS,
|
||||
+ nbd_handshake_timer_cb,
|
||||
+ client->sioc);
|
||||
+ timer_mod(handshake_timer,
|
||||
+ qemu_clock_get_ns(QEMU_CLOCK_REALTIME) +
|
||||
+ client->handshake_max_secs * NANOSECONDS_PER_SECOND);
|
||||
+ }
|
||||
+
|
||||
if (nbd_negotiate(client, &local_err)) {
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
}
|
||||
+ timer_free(handshake_timer);
|
||||
client_close(client, false);
|
||||
return;
|
||||
}
|
||||
|
||||
+ timer_free(handshake_timer);
|
||||
nbd_client_receive_next_request(client);
|
||||
}
|
||||
|
||||
diff --git a/nbd/trace-events b/nbd/trace-events
|
||||
index 7ab6b3788c..57a859146e 100644
|
||||
--- a/nbd/trace-events
|
||||
+++ b/nbd/trace-events
|
||||
@@ -73,3 +73,4 @@ nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const char *n
|
||||
nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) "Payload received: handle = %" PRIu64 ", len = %" PRIu32
|
||||
nbd_co_receive_align_compliance(const char *op, uint64_t from, uint32_t len, uint32_t align) "client sent non-compliant unaligned %s request: from=0x%" PRIx64 ", len=0x%" PRIx32 ", align=0x%" PRIx32
|
||||
nbd_trip(void) "Reading request"
|
||||
+nbd_handshake_timer_cb(void) "client took too long to negotiate"
|
||||
--
|
||||
2.45.1.windows.1
|
||||
|
||||
162
nbd-server-Plumb-in-new-args-to-nbd_client_add.patch
Normal file
162
nbd-server-Plumb-in-new-args-to-nbd_client_add.patch
Normal file
@ -0,0 +1,162 @@
|
||||
From 8343eb0b064fd0d6a270812d43a5f4c6051b6714 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Blake <eblake@redhat.com>
|
||||
Date: Wed, 7 Aug 2024 08:50:01 -0500
|
||||
Subject: [PATCH 2/8] nbd/server: Plumb in new args to nbd_client_add()
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Upcoming patches to fix a CVE need to track an opaque pointer passed
|
||||
in by the owner of a client object, as well as request for a time
|
||||
limit on how fast negotiation must complete. Prepare for that by
|
||||
changing the signature of nbd_client_new() and adding an accessor to
|
||||
get at the opaque pointer, although for now the two servers
|
||||
(qemu-nbd.c and blockdev-nbd.c) do not change behavior even though
|
||||
they pass in a new default timeout value.
|
||||
|
||||
Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
Message-ID: <20240807174943.771624-11-eblake@redhat.com>
|
||||
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
|
||||
[eblake: s/LIMIT/MAX_SECS/ as suggested by Dan]
|
||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||||
---
|
||||
blockdev-nbd.c | 6 ++++--
|
||||
include/block/nbd.h | 11 ++++++++++-
|
||||
nbd/server.c | 20 +++++++++++++++++---
|
||||
qemu-nbd.c | 4 +++-
|
||||
4 files changed, 34 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
|
||||
index 66eebab318..0c14f033d2 100644
|
||||
--- a/blockdev-nbd.c
|
||||
+++ b/blockdev-nbd.c
|
||||
@@ -37,8 +37,10 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
|
||||
gpointer opaque)
|
||||
{
|
||||
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
|
||||
- nbd_client_new(cioc, nbd_server->tlscreds, nbd_server->tlsauthz,
|
||||
- nbd_blockdev_client_closed);
|
||||
+ /* TODO - expose handshake timeout as QMP option */
|
||||
+ nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS,
|
||||
+ nbd_server->tlscreds, nbd_server->tlsauthz,
|
||||
+ nbd_blockdev_client_closed, NULL);
|
||||
}
|
||||
|
||||
|
||||
diff --git a/include/block/nbd.h b/include/block/nbd.h
|
||||
index bb9f5bc021..68667c31c8 100644
|
||||
--- a/include/block/nbd.h
|
||||
+++ b/include/block/nbd.h
|
||||
@@ -25,6 +25,12 @@
|
||||
#include "crypto/tlscreds.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
+/*
|
||||
+ * NBD_DEFAULT_HANDSHAKE_MAX_SECS: Number of seconds in which client must
|
||||
+ * succeed at NBD_OPT_GO before being forcefully dropped as too slow.
|
||||
+ */
|
||||
+#define NBD_DEFAULT_HANDSHAKE_MAX_SECS 10
|
||||
+
|
||||
/* Handshake phase structs - this struct is passed on the wire */
|
||||
|
||||
struct NBDOption {
|
||||
@@ -339,9 +345,12 @@ NBDExport *nbd_export_find(const char *name);
|
||||
void nbd_export_close_all(void);
|
||||
|
||||
void nbd_client_new(QIOChannelSocket *sioc,
|
||||
+ uint32_t handshake_max_secs,
|
||||
QCryptoTLSCreds *tlscreds,
|
||||
const char *tlsauthz,
|
||||
- void (*close_fn)(NBDClient *, bool));
|
||||
+ void (*close_fn)(NBDClient *, bool),
|
||||
+ void *owner);
|
||||
+void *nbd_client_owner(NBDClient *client);
|
||||
void nbd_client_get(NBDClient *client);
|
||||
void nbd_client_put(NBDClient *client);
|
||||
|
||||
diff --git a/nbd/server.c b/nbd/server.c
|
||||
index b617e6a1a1..68d78888c6 100644
|
||||
--- a/nbd/server.c
|
||||
+++ b/nbd/server.c
|
||||
@@ -111,10 +111,12 @@ typedef struct NBDExportMetaContexts {
|
||||
struct NBDClient {
|
||||
int refcount;
|
||||
void (*close_fn)(NBDClient *client, bool negotiated);
|
||||
+ void *owner;
|
||||
|
||||
NBDExport *exp;
|
||||
QCryptoTLSCreds *tlscreds;
|
||||
char *tlsauthz;
|
||||
+ uint32_t handshake_max_secs;
|
||||
QIOChannelSocket *sioc; /* The underlying data channel */
|
||||
QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
|
||||
|
||||
@@ -2421,6 +2423,7 @@ static coroutine_fn void nbd_co_client_start(void *opaque)
|
||||
|
||||
qemu_co_mutex_init(&client->send_lock);
|
||||
|
||||
+ /* TODO - utilize client->handshake_max_secs */
|
||||
if (nbd_negotiate(client, &local_err)) {
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
@@ -2433,14 +2436,17 @@ static coroutine_fn void nbd_co_client_start(void *opaque)
|
||||
}
|
||||
|
||||
/*
|
||||
- * Create a new client listener using the given channel @sioc.
|
||||
+ * Create a new client listener using the given channel @sioc and @owner.
|
||||
* Begin servicing it in a coroutine. When the connection closes, call
|
||||
- * @close_fn with an indication of whether the client completed negotiation.
|
||||
+ * @close_fn with an indication of whether the client completed negotiation
|
||||
+ * within @handshake_max_secs seconds (0 for unbounded).
|
||||
*/
|
||||
void nbd_client_new(QIOChannelSocket *sioc,
|
||||
+ uint32_t handshake_max_secs,
|
||||
QCryptoTLSCreds *tlscreds,
|
||||
const char *tlsauthz,
|
||||
- void (*close_fn)(NBDClient *, bool))
|
||||
+ void (*close_fn)(NBDClient *, bool),
|
||||
+ void *owner)
|
||||
{
|
||||
NBDClient *client;
|
||||
Coroutine *co;
|
||||
@@ -2452,12 +2458,20 @@ void nbd_client_new(QIOChannelSocket *sioc,
|
||||
object_ref(OBJECT(client->tlscreds));
|
||||
}
|
||||
client->tlsauthz = g_strdup(tlsauthz);
|
||||
+ client->handshake_max_secs = handshake_max_secs;
|
||||
client->sioc = sioc;
|
||||
object_ref(OBJECT(client->sioc));
|
||||
client->ioc = QIO_CHANNEL(sioc);
|
||||
object_ref(OBJECT(client->ioc));
|
||||
client->close_fn = close_fn;
|
||||
+ client->owner = owner;
|
||||
|
||||
co = qemu_coroutine_create(nbd_co_client_start, client);
|
||||
qemu_coroutine_enter(co);
|
||||
}
|
||||
+
|
||||
+void *
|
||||
+nbd_client_owner(NBDClient *client)
|
||||
+{
|
||||
+ return client->owner;
|
||||
+}
|
||||
diff --git a/qemu-nbd.c b/qemu-nbd.c
|
||||
index 2dc2e8ea55..5f3abf53cd 100644
|
||||
--- a/qemu-nbd.c
|
||||
+++ b/qemu-nbd.c
|
||||
@@ -448,7 +448,9 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
|
||||
|
||||
nb_fds++;
|
||||
nbd_update_server_watch();
|
||||
- nbd_client_new(cioc, tlscreds, tlsauthz, nbd_client_closed);
|
||||
+ /* TODO - expose handshake timeout as command line option */
|
||||
+ nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS,
|
||||
+ tlscreds, tlsauthz, nbd_client_closed, NULL);
|
||||
}
|
||||
|
||||
static void nbd_update_server_watch(void)
|
||||
--
|
||||
2.45.1.windows.1
|
||||
|
||||
211
qcow2-Do-not-reopen-data_file-in-invalidate_cache.patch
Normal file
211
qcow2-Do-not-reopen-data_file-in-invalidate_cache.patch
Normal file
@ -0,0 +1,211 @@
|
||||
From 013c6ad4645fc8411bb4615aa201b1f0b8a01003 Mon Sep 17 00:00:00 2001
|
||||
From: Hanna Reitz <hreitz@redhat.com>
|
||||
Date: Wed, 27 Apr 2022 13:40:55 +0200
|
||||
Subject: [PATCH] qcow2: Do not reopen data_file in invalidate_cache
|
||||
|
||||
qcow2_co_invalidate_cache() closes and opens the qcow2 file, by calling
|
||||
qcow2_close() and qcow2_do_open(). These two functions must thus be
|
||||
usable from both a global-state and an I/O context.
|
||||
|
||||
As they are, they are not safe to call in an I/O context, because they
|
||||
use bdrv_unref_child() and bdrv_open_child() to close/open the data_file
|
||||
child, respectively, both of which are global-state functions. When
|
||||
used from qcow2_co_invalidate_cache(), we do not need to close/open the
|
||||
data_file child, though (we do not do this for bs->file or bs->backing
|
||||
either), and so we should skip it in the qcow2_co_invalidate_cache()
|
||||
path.
|
||||
|
||||
To do so, add a parameter to qcow2_do_open() and qcow2_close() to make
|
||||
them skip handling s->data_file, and have qcow2_co_invalidate_cache()
|
||||
exempt it from the memset() on the BDRVQcow2State.
|
||||
|
||||
(Note that the QED driver similarly closes/opens the QED image by
|
||||
invoking bdrv_qed_close()+bdrv_qed_do_open(), but both functions seem
|
||||
safe to use in an I/O context.)
|
||||
|
||||
Fixes: https://gitlab.com/qemu-project/qemu/-/issues/945
|
||||
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
|
||||
Message-Id: <20220427114057.36651-3-hreitz@redhat.com>
|
||||
Reviewed-by: Eric Blake <eblake@redhat.com>
|
||||
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||
Signed-off-by: liuxiangdong <liuxiangdong5@huawei.com>
|
||||
---
|
||||
block/qcow2.c | 96 +++++++++++++++++++++++++++++++--------------------
|
||||
1 file changed, 58 insertions(+), 38 deletions(-)
|
||||
|
||||
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||
index 1909df6e1d..8e1b5ffe2d 100644
|
||||
--- a/block/qcow2.c
|
||||
+++ b/block/qcow2.c
|
||||
@@ -1204,7 +1204,8 @@ static int qcow2_update_options(BlockDriverState *bs, QDict *options,
|
||||
|
||||
/* Called with s->lock held. */
|
||||
static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
- int flags, Error **errp)
|
||||
+ int flags, bool open_data_file,
|
||||
+ Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
unsigned int len, i;
|
||||
@@ -1491,44 +1492,46 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
- /* Open external data file */
|
||||
- s->data_file = bdrv_open_child(NULL, options, "data-file", bs, &child_file,
|
||||
- true, &local_err);
|
||||
- if (local_err) {
|
||||
- error_propagate(errp, local_err);
|
||||
- ret = -EINVAL;
|
||||
- goto fail;
|
||||
- }
|
||||
+ if (open_data_file) {
|
||||
+ /* Open external data file */
|
||||
+ s->data_file = bdrv_open_child(NULL, options, "data-file", bs,
|
||||
+ &child_file, true, &local_err);
|
||||
+ if (local_err) {
|
||||
+ error_propagate(errp, local_err);
|
||||
+ ret = -EINVAL;
|
||||
+ goto fail;
|
||||
+ }
|
||||
|
||||
- if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) {
|
||||
- if (!s->data_file && s->image_data_file) {
|
||||
- s->data_file = bdrv_open_child(s->image_data_file, options,
|
||||
- "data-file", bs, &child_file,
|
||||
- false, errp);
|
||||
+ if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) {
|
||||
+ if (!s->data_file && s->image_data_file) {
|
||||
+ s->data_file = bdrv_open_child(s->image_data_file, options,
|
||||
+ "data-file", bs, &child_file,
|
||||
+ false, errp);
|
||||
+ if (!s->data_file) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ }
|
||||
if (!s->data_file) {
|
||||
+ error_setg(errp, "'data-file' is required for this image");
|
||||
+ ret = -EINVAL;
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ } else {
|
||||
+ if (s->data_file) {
|
||||
+ error_setg(errp, "'data-file' can only be set for images with "
|
||||
+ "an external data file");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
- }
|
||||
- if (!s->data_file) {
|
||||
- error_setg(errp, "'data-file' is required for this image");
|
||||
- ret = -EINVAL;
|
||||
- goto fail;
|
||||
- }
|
||||
- } else {
|
||||
- if (s->data_file) {
|
||||
- error_setg(errp, "'data-file' can only be set for images with an "
|
||||
- "external data file");
|
||||
- ret = -EINVAL;
|
||||
- goto fail;
|
||||
- }
|
||||
|
||||
- s->data_file = bs->file;
|
||||
+ s->data_file = bs->file;
|
||||
|
||||
- if (data_file_is_raw(bs)) {
|
||||
- error_setg(errp, "data-file-raw requires a data file");
|
||||
- ret = -EINVAL;
|
||||
- goto fail;
|
||||
+ if (data_file_is_raw(bs)) {
|
||||
+ error_setg(errp, "data-file-raw requires a data file");
|
||||
+ ret = -EINVAL;
|
||||
+ goto fail;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1705,7 +1708,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
|
||||
fail:
|
||||
g_free(s->image_data_file);
|
||||
- if (has_data_file(bs)) {
|
||||
+ if (open_data_file && has_data_file(bs)) {
|
||||
bdrv_unref_child(bs, s->data_file);
|
||||
}
|
||||
g_free(s->unknown_header_fields);
|
||||
@@ -1741,7 +1744,8 @@ static void coroutine_fn qcow2_open_entry(void *opaque)
|
||||
BDRVQcow2State *s = qoc->bs->opaque;
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
- qoc->ret = qcow2_do_open(qoc->bs, qoc->options, qoc->flags, qoc->errp);
|
||||
+ qoc->ret = qcow2_do_open(qoc->bs, qoc->options, qoc->flags, true,
|
||||
+ qoc->errp);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
}
|
||||
|
||||
@@ -2391,7 +2395,7 @@ static int qcow2_inactivate(BlockDriverState *bs)
|
||||
return result;
|
||||
}
|
||||
|
||||
-static void qcow2_close(BlockDriverState *bs)
|
||||
+static void qcow2_do_close(BlockDriverState *bs, bool close_data_file)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
qemu_vfree(s->l1_table);
|
||||
@@ -2416,7 +2420,7 @@ static void qcow2_close(BlockDriverState *bs)
|
||||
g_free(s->image_backing_file);
|
||||
g_free(s->image_backing_format);
|
||||
|
||||
- if (has_data_file(bs)) {
|
||||
+ if (close_data_file && has_data_file(bs)) {
|
||||
bdrv_unref_child(bs, s->data_file);
|
||||
}
|
||||
|
||||
@@ -2424,10 +2428,16 @@ static void qcow2_close(BlockDriverState *bs)
|
||||
qcow2_free_snapshots(bs);
|
||||
}
|
||||
|
||||
+static void qcow2_close(BlockDriverState *bs)
|
||||
+{
|
||||
+ qcow2_do_close(bs, true);
|
||||
+}
|
||||
+
|
||||
static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
+ BdrvChild *data_file;
|
||||
int flags = s->flags;
|
||||
QCryptoBlock *crypto = NULL;
|
||||
QDict *options;
|
||||
@@ -2442,14 +2452,24 @@ static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs,
|
||||
crypto = s->crypto;
|
||||
s->crypto = NULL;
|
||||
|
||||
- qcow2_close(bs);
|
||||
+ /*
|
||||
+ * Do not reopen s->data_file (i.e., have qcow2_do_close() not close it,
|
||||
+ * and then prevent qcow2_do_open() from opening it), because this function
|
||||
+ * runs in the I/O path and as such we must not invoke global-state
|
||||
+ * functions like bdrv_unref_child() and bdrv_open_child().
|
||||
+ */
|
||||
|
||||
+ qcow2_do_close(bs, false);
|
||||
+
|
||||
+ data_file = s->data_file;
|
||||
memset(s, 0, sizeof(BDRVQcow2State));
|
||||
+ s->data_file = data_file;
|
||||
+
|
||||
options = qdict_clone_shallow(bs->options);
|
||||
|
||||
flags &= ~BDRV_O_INACTIVE;
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
- ret = qcow2_do_open(bs, options, flags, &local_err);
|
||||
+ ret = qcow2_do_open(bs, options, flags, false, &local_err);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
qobject_unref(options);
|
||||
if (local_err) {
|
||||
--
|
||||
2.41.0.windows.1
|
||||
|
||||
108
qcow2-Don-t-open-data_file-with-BDRV_O_NO_IO-CVE-202.patch
Normal file
108
qcow2-Don-t-open-data_file-with-BDRV_O_NO_IO-CVE-202.patch
Normal file
@ -0,0 +1,108 @@
|
||||
From 89d32a19f14934bac61bb80955b8b29ac1ac9109 Mon Sep 17 00:00:00 2001
|
||||
From: Kevin Wolf <kwolf@redhat.com>
|
||||
Date: Thu, 11 Apr 2024 15:06:01 +0200
|
||||
Subject: [PATCH] qcow2: Don't open data_file with BDRV_O_NO_IO (CVE-2024-4467)
|
||||
|
||||
One use case for 'qemu-img info' is verifying that untrusted images
|
||||
don't reference an unwanted external file, be it as a backing file or an
|
||||
external data file. To make sure that calling 'qemu-img info' can't
|
||||
already have undesired side effects with a malicious image, just don't
|
||||
open the data file at all with BDRV_O_NO_IO. If nothing ever tries to do
|
||||
I/O, we don't need to have it open.
|
||||
|
||||
This changes the output of iotests case 061, which used 'qemu-img info'
|
||||
to show that opening an image with an invalid data file fails. After
|
||||
this patch, it succeeds. Replace this part of the test with a qemu-io
|
||||
call, but keep the final 'qemu-img info' to show that the invalid data
|
||||
file is correctly displayed in the output.
|
||||
|
||||
Fixes: CVE-2024-4467
|
||||
Cc: qemu-stable@nongnu.org
|
||||
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||
Reviewed-by: Eric Blake <eblake@redhat.com>
|
||||
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
|
||||
---
|
||||
block/qcow2.c | 17 ++++++++++++++++-
|
||||
tests/qemu-iotests/061 | 6 ++++--
|
||||
tests/qemu-iotests/061.out | 8 ++++++--
|
||||
3 files changed, 26 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||
index 8e1b5ffe2d..c1992c571a 100644
|
||||
--- a/block/qcow2.c
|
||||
+++ b/block/qcow2.c
|
||||
@@ -1492,7 +1492,22 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
- if (open_data_file) {
|
||||
+ if (open_data_file && (flags & BDRV_O_NO_IO)) {
|
||||
+ /*
|
||||
+ * Don't open the data file for 'qemu-img info' so that it can be used
|
||||
+ * to verify that an untrusted qcow2 image doesn't refer to external
|
||||
+ * files.
|
||||
+ *
|
||||
+ * Note: This still makes has_data_file() return true.
|
||||
+ */
|
||||
+ if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) {
|
||||
+ s->data_file = NULL;
|
||||
+ } else {
|
||||
+ s->data_file = bs->file;
|
||||
+ }
|
||||
+ qdict_extract_subqdict(options, NULL, "data-file.");
|
||||
+ qdict_del(options, "data-file");
|
||||
+ } else if (open_data_file) {
|
||||
/* Open external data file */
|
||||
s->data_file = bdrv_open_child(NULL, options, "data-file", bs,
|
||||
&child_file, true, &local_err);
|
||||
diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061
|
||||
index d7dbd7e2c7..ff8ca908d0 100755
|
||||
--- a/tests/qemu-iotests/061
|
||||
+++ b/tests/qemu-iotests/061
|
||||
@@ -268,12 +268,14 @@ $QEMU_IMG amend -o "data_file=foo" "$TEST_IMG"
|
||||
echo
|
||||
IMGOPTS="compat=1.1,data_file=$TEST_IMG.data" _make_test_img 64M
|
||||
$QEMU_IMG amend -o "data_file=foo" "$TEST_IMG"
|
||||
-_img_info --format-specific
|
||||
+$QEMU_IO -c "read 0 4k" "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt
|
||||
+$QEMU_IO -c "open -o data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" -c "read 0 4k" | _filter_qemu_io
|
||||
TEST_IMG="data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" _img_info --format-specific --image-opts
|
||||
|
||||
echo
|
||||
$QEMU_IMG amend -o "data_file=" --image-opts "data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG"
|
||||
-_img_info --format-specific
|
||||
+$QEMU_IO -c "read 0 4k" "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt
|
||||
+$QEMU_IO -c "open -o data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" -c "read 0 4k" | _filter_qemu_io
|
||||
TEST_IMG="data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" _img_info --format-specific --image-opts
|
||||
|
||||
echo
|
||||
diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out
|
||||
index 1aa7d37ff9..8c6f1dbde3 100644
|
||||
--- a/tests/qemu-iotests/061.out
|
||||
+++ b/tests/qemu-iotests/061.out
|
||||
@@ -512,7 +512,9 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
qemu-img: data-file can only be set for images that use an external data file
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data
|
||||
-qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Could not open 'foo': No such file or directory
|
||||
+qemu-io: can't open device TEST_DIR/t.IMGFMT: Could not open 'foo': No such file or directory
|
||||
+read 4096/4096 bytes at offset 0
|
||||
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
image: TEST_DIR/t.IMGFMT
|
||||
file format: IMGFMT
|
||||
virtual size: 64 MiB (67108864 bytes)
|
||||
@@ -525,7 +527,9 @@ Format specific information:
|
||||
data file raw: false
|
||||
corrupt: false
|
||||
|
||||
-qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'data-file' is required for this image
|
||||
+qemu-io: can't open device TEST_DIR/t.IMGFMT: 'data-file' is required for this image
|
||||
+read 4096/4096 bytes at offset 0
|
||||
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
image: TEST_DIR/t.IMGFMT
|
||||
file format: IMGFMT
|
||||
virtual size: 64 MiB (67108864 bytes)
|
||||
--
|
||||
2.41.0.windows.1
|
||||
|
||||
100
qdev-properties-add-size32-property-type.patch
Normal file
100
qdev-properties-add-size32-property-type.patch
Normal file
@ -0,0 +1,100 @@
|
||||
From 921e5443d80d6ed33bda10dd790e63b70d67cab0 Mon Sep 17 00:00:00 2001
|
||||
From: Roman Kagan <rvkagan@yandex-team.ru>
|
||||
Date: Fri, 29 May 2020 01:55:12 +0300
|
||||
Subject: [PATCH 3/5] qdev-properties: add size32 property type
|
||||
|
||||
cherry-pick from 914e74cda9a54ac860000aa18882dc40d3c8180b
|
||||
|
||||
Introduce size32 property type which handles size suffixes (k, m, g)
|
||||
just like size property, but is uint32_t rather than uint64_t. It's
|
||||
going to be useful for properties that are byte sizes but are inherently
|
||||
32bit, like BlkConf.opt_io_size or .discard_granularity (they are
|
||||
switched to this new property type in a followup commit).
|
||||
|
||||
The getter for size32 is left out for a separate patch as its benefit is
|
||||
less obvious, and it affects test output; for now the regular uint32
|
||||
getter is used.
|
||||
|
||||
Signed-off-by: Roman Kagan <rvkagan@yandex-team.ru>
|
||||
Message-Id: <20200528225516.1676602-5-rvkagan@yandex-team.ru>
|
||||
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||
---
|
||||
hw/core/qdev-properties.c | 40 ++++++++++++++++++++++++++++++++++++
|
||||
include/hw/qdev-properties.h | 3 +++
|
||||
2 files changed, 43 insertions(+)
|
||||
|
||||
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
|
||||
index 02a824fc68..881138b3e6 100644
|
||||
--- a/hw/core/qdev-properties.c
|
||||
+++ b/hw/core/qdev-properties.c
|
||||
@@ -766,6 +766,46 @@ const PropertyInfo qdev_prop_pci_devfn = {
|
||||
.set_default_value = set_default_value_int,
|
||||
};
|
||||
|
||||
+/* --- 32bit unsigned int 'size' type --- */
|
||||
+
|
||||
+static void set_size32(Object *obj, Visitor *v, const char *name, void *opaque,
|
||||
+ Error **errp)
|
||||
+{
|
||||
+ DeviceState *dev = DEVICE(obj);
|
||||
+ Property *prop = opaque;
|
||||
+ uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
|
||||
+ uint64_t value;
|
||||
+ Error *local_err = NULL;
|
||||
+
|
||||
+ if (dev->realized) {
|
||||
+ qdev_prop_set_after_realize(dev, name, errp);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ visit_type_size(v, name, &value, &local_err);
|
||||
+ if (local_err) {
|
||||
+ error_propagate(errp, local_err);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (value > UINT32_MAX) {
|
||||
+ error_setg(errp,
|
||||
+ "Property %s.%s doesn't take value %" PRIu64
|
||||
+ " (maximum: %u)",
|
||||
+ dev->id ? : "", name, value, UINT32_MAX);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ *ptr = value;
|
||||
+}
|
||||
+
|
||||
+const PropertyInfo qdev_prop_size32 = {
|
||||
+ .name = "size",
|
||||
+ .get = get_uint32,
|
||||
+ .set = set_size32,
|
||||
+ .set_default_value = set_default_value_uint,
|
||||
+};
|
||||
+
|
||||
/* --- blocksize --- */
|
||||
|
||||
static void set_blocksize(Object *obj, Visitor *v, const char *name,
|
||||
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
|
||||
index 4585c200df..a2a6598905 100644
|
||||
--- a/include/hw/qdev-properties.h
|
||||
+++ b/include/hw/qdev-properties.h
|
||||
@@ -33,6 +33,7 @@ extern const PropertyInfo qdev_prop_drive;
|
||||
extern const PropertyInfo qdev_prop_drive_iothread;
|
||||
extern const PropertyInfo qdev_prop_netdev;
|
||||
extern const PropertyInfo qdev_prop_pci_devfn;
|
||||
+extern const PropertyInfo qdev_prop_size32;
|
||||
extern const PropertyInfo qdev_prop_blocksize;
|
||||
extern const PropertyInfo qdev_prop_pci_host_devaddr;
|
||||
extern const PropertyInfo qdev_prop_uuid;
|
||||
@@ -221,6 +222,8 @@ extern const PropertyInfo qdev_prop_pcie_link_width;
|
||||
int64_t)
|
||||
#define DEFINE_PROP_BIOS_CHS_TRANS(_n, _s, _f, _d) \
|
||||
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_bios_chs_trans, int)
|
||||
+#define DEFINE_PROP_SIZE32(_n, _s, _f, _d) \
|
||||
+ DEFINE_PROP_UNSIGNED(_n, _s, _f, _d, qdev_prop_size32, uint32_t)
|
||||
#define DEFINE_PROP_BLOCKSIZE(_n, _s, _f) \
|
||||
DEFINE_PROP_UNSIGNED(_n, _s, _f, 0, qdev_prop_blocksize, uint16_t)
|
||||
#define DEFINE_PROP_PCI_HOST_DEVADDR(_n, _s, _f) \
|
||||
--
|
||||
2.45.1.windows.1
|
||||
|
||||
50
qemu.spec
50
qemu.spec
@ -1,6 +1,6 @@
|
||||
Name: qemu
|
||||
Version: 4.1.0
|
||||
Release: 84
|
||||
Release: 89
|
||||
Epoch: 10
|
||||
Summary: QEMU is a generic and open source machine emulator and virtualizer
|
||||
License: GPLv2 and BSD and MIT and CC-BY-SA-4.0
|
||||
@ -411,6 +411,25 @@ Patch0398: hw-char-virtio-serial-bus-Protect-from-DMA-re-entran.patch
|
||||
Patch0399: hw-virtio-virtio-crypto-Protect-from-DMA-re-entrancy.patch
|
||||
Patch0400: hw-ide-reset-cancel-async-DMA-operation-before-reset.patch
|
||||
Patch0401: tests-qtest-ahci-test-add-test-exposing-reset-issue-.patch
|
||||
Patch0402: qcow2-Do-not-reopen-data_file-in-invalidate_cache.patch
|
||||
Patch0403: qcow2-Don-t-open-data_file-with-BDRV_O_NO_IO-CVE-202.patch
|
||||
Patch0404: block-introduce-bdrv_open_file_child-helper.patch
|
||||
Patch0405: block-Parse-filenames-only-when-explicitly-requested.patch
|
||||
Patch0406: nbd-Minor-style-and-typo-fixes.patch
|
||||
Patch0407: nbd-server-Plumb-in-new-args-to-nbd_client_add.patch
|
||||
Patch0408: nbd-Add-max-connections-to-nbd-server-start.patch
|
||||
Patch0409: nbd-server-CVE-2024-7409-Cap-default-max-connections.patch
|
||||
Patch0410: nbd-server-CVE-2024-7409-Drop-non-negotiating-client.patch
|
||||
Patch0411: aio-wait.h-introduce-AIO_WAIT_WHILE_UNLOCKED.patch
|
||||
Patch0412: main-loop.h-introduce-qemu_in_main_thread.patch
|
||||
Patch0413: nbd-server-CVE-2024-7409-Close-stray-clients-at-serv.patch
|
||||
Patch0414: nbd-server-CVE-2024-7409-Avoid-use-after-free-when-c.patch
|
||||
Patch0415: system-physmem-Propagate-AddressSpace-to-MapClient-h.patch
|
||||
Patch0416: system-physmem-Per-AddressSpace-bounce-buffering.patch
|
||||
Patch0417: qdev-properties-add-size32-property-type.patch
|
||||
Patch0418: softmmu-Support-concurrent-bounce-buffers-CVE-2024-8.patch
|
||||
Patch0419: mac_dbdma-Remove-leftover-dma_memory_unmap-calls-CVE.patch
|
||||
Patch0420: Fix-the-missing-hmp_nbd_server_start-change-in-CVE-2.patch
|
||||
|
||||
BuildRequires: flex
|
||||
BuildRequires: bison
|
||||
@ -811,6 +830,35 @@ getent passwd qemu >/dev/null || \
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Mon Jan 06 2025 xiaoyuliang <xiaoyuliang@kylinos.cn> - 10:4.1.0-89
|
||||
- nbd: Fix the missing hmp_nbd_server_start change in CVE-2024-7409
|
||||
|
||||
* Mon Oct 14 2024 Jiabo Feng <fengjiabo1@huawei.com> - 10:4.1.0-88
|
||||
- mac_dbdma: Remove leftover `dma_memory_unmap` calls(CVE-2024-8612)
|
||||
- softmmu: Support concurrent bounce buffers(CVE-2024-8612)
|
||||
- qdev-properties: add size32 property type
|
||||
- system/physmem: Per-AddressSpace bounce buffering
|
||||
- system/physmem: Propagate AddressSpace to MapClient helpers
|
||||
|
||||
* Wed Sep 18 2024 Jiabo Feng <fengjiabo1@huawei.com> - 10:4.1.0-87
|
||||
- nbd/server: CVE-2024-7409: Avoid use-after-free when closing server
|
||||
|
||||
* Tue Aug 13 2024 Jiabo Feng <fengjiabo1@huawei.com> - 10:4.1.0-86
|
||||
- nbd/server: CVE-2024-7409: Close stray clients at server-stop
|
||||
- main-loop.h: introduce qemu_in_main_thread()
|
||||
- aio-wait.h: introduce AIO_WAIT_WHILE_UNLOCKED
|
||||
- nbd/server: CVE-2024-7409: Drop non-negotiating clients
|
||||
- nbd/server: CVE-2024-7409: Cap default max-connections to 100
|
||||
- nbd: Add max-connections to nbd-server-start
|
||||
- nbd/server: Plumb in new args to nbd_client_add()
|
||||
- nbd: Minor style and typo fixes
|
||||
|
||||
* Thu Jul 11 2024 Jiabo Feng <fengjiabo1@huawei.com>
|
||||
- block: Parse filenames only when explicitly requested (CVE-2024-4467)
|
||||
- block: introduce bdrv_open_file_child() helper
|
||||
- qcow2: Don't open data_file with BDRV_O_NO_IO (CVE-2024-4467)
|
||||
- qcow2: Do not reopen data_file in invalidate_cache
|
||||
|
||||
* Sat Jun 15 2024 Jiabo Feng <fengjiabo1@huawei.com>
|
||||
- tests/qtest: ahci-test: add test exposing reset issue with pending callback (Fix CVE-2023-5088)
|
||||
- hw/ide: reset: cancel async DMA operation before resetting state (Fix CVE-2023-5088)
|
||||
|
||||
286
softmmu-Support-concurrent-bounce-buffers-CVE-2024-8.patch
Normal file
286
softmmu-Support-concurrent-bounce-buffers-CVE-2024-8.patch
Normal file
@ -0,0 +1,286 @@
|
||||
From f622157a79ca2f7e45693ab32fa1a8114cdf3e54 Mon Sep 17 00:00:00 2001
|
||||
From: Mattias Nissler <mnissler@rivosinc.com>
|
||||
Date: Mon, 19 Aug 2024 06:54:54 -0700
|
||||
Subject: [PATCH 4/5] softmmu: Support concurrent bounce buffers(CVE-2024-8612)
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
cherry-pick from 637b0aa139565cb82a7b9269e62214f87082635c
|
||||
|
||||
When DMA memory can't be directly accessed, as is the case when
|
||||
running the device model in a separate process without shareable DMA
|
||||
file descriptors, bounce buffering is used.
|
||||
|
||||
It is not uncommon for device models to request mapping of several DMA
|
||||
regions at the same time. Examples include:
|
||||
* net devices, e.g. when transmitting a packet that is split across
|
||||
several TX descriptors (observed with igb)
|
||||
* USB host controllers, when handling a packet with multiple data TRBs
|
||||
(observed with xhci)
|
||||
|
||||
Previously, qemu only provided a single bounce buffer per AddressSpace
|
||||
and would fail DMA map requests while the buffer was already in use. In
|
||||
turn, this would cause DMA failures that ultimately manifest as hardware
|
||||
errors from the guest perspective.
|
||||
|
||||
This change allocates DMA bounce buffers dynamically instead of
|
||||
supporting only a single buffer. Thus, multiple DMA mappings work
|
||||
correctly also when RAM can't be mmap()-ed.
|
||||
|
||||
The total bounce buffer allocation size is limited individually for each
|
||||
AddressSpace. The default limit is 4096 bytes, matching the previous
|
||||
maximum buffer size. A new x-max-bounce-buffer-size parameter is
|
||||
provided to configure the limit for PCI devices.
|
||||
|
||||
Signed-off-by: Mattias Nissler <mnissler@rivosinc.com>
|
||||
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
Acked-by: Peter Xu <peterx@redhat.com>
|
||||
Link: https://lore.kernel.org/r/20240819135455.2957406-1-mnissler@rivosinc.com
|
||||
Signed-off-by: Peter Xu <peterx@redhat.com>
|
||||
---
|
||||
exec.c | 78 ++++++++++++++++++++++++++++++-------------
|
||||
hw/pci/pci.c | 8 +++++
|
||||
include/exec/memory.h | 14 +++-----
|
||||
include/hw/pci/pci.h | 3 ++
|
||||
memory.c | 5 +--
|
||||
5 files changed, 74 insertions(+), 34 deletions(-)
|
||||
|
||||
diff --git a/exec.c b/exec.c
|
||||
index ac4ebaaf27..c766939b4e 100644
|
||||
--- a/exec.c
|
||||
+++ b/exec.c
|
||||
@@ -3610,6 +3610,20 @@ void cpu_flush_icache_range(hwaddr start, hwaddr len)
|
||||
NULL, len, FLUSH_CACHE);
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * A magic value stored in the first 8 bytes of the bounce buffer struct. Used
|
||||
+ * to detect illegal pointers passed to address_space_unmap.
|
||||
+ */
|
||||
+#define BOUNCE_BUFFER_MAGIC 0xb4017ceb4ffe12ed
|
||||
+
|
||||
+typedef struct {
|
||||
+ uint64_t magic;
|
||||
+ MemoryRegion *mr;
|
||||
+ hwaddr addr;
|
||||
+ size_t len;
|
||||
+ uint8_t buffer[];
|
||||
+} BounceBuffer;
|
||||
+
|
||||
static void
|
||||
address_space_unregister_map_client_do(AddressSpaceMapClient *client)
|
||||
{
|
||||
@@ -3635,7 +3649,7 @@ void address_space_register_map_client(AddressSpace *as, QEMUBH *bh)
|
||||
qemu_mutex_lock(&as->map_client_list_lock);
|
||||
client->bh = bh;
|
||||
QLIST_INSERT_HEAD(&as->map_client_list, client, link);
|
||||
- if (!atomic_read(&as->bounce.in_use)) {
|
||||
+ if (atomic_read(&as->bounce_buffer_size) < as->max_bounce_buffer_size) {
|
||||
address_space_notify_map_clients_locked(as);
|
||||
}
|
||||
qemu_mutex_unlock(&as->map_client_list_lock);
|
||||
@@ -3769,30 +3783,41 @@ void *address_space_map(AddressSpace *as,
|
||||
mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs);
|
||||
|
||||
if (!memory_access_is_direct(mr, is_write)) {
|
||||
- if (atomic_xchg(&as->bounce.in_use, true)) {
|
||||
- rcu_read_unlock();
|
||||
+ size_t used = atomic_read(&as->bounce_buffer_size);
|
||||
+ for (;;) {
|
||||
+ hwaddr alloc = MIN(as->max_bounce_buffer_size - used, l);
|
||||
+ size_t new_size = used + alloc;
|
||||
+ size_t actual =
|
||||
+ atomic_cmpxchg(&as->bounce_buffer_size, used, new_size);
|
||||
+ if (actual == used) {
|
||||
+ l = alloc;
|
||||
+ break;
|
||||
+ }
|
||||
+ used = actual;
|
||||
+ }
|
||||
+
|
||||
+ if (l == 0) {
|
||||
*plen = 0;
|
||||
return NULL;
|
||||
}
|
||||
- /* Avoid unbounded allocations */
|
||||
- l = MIN(l, TARGET_PAGE_SIZE);
|
||||
- as->bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, l);
|
||||
- as->bounce.addr = addr;
|
||||
- as->bounce.len = l;
|
||||
|
||||
+ BounceBuffer *bounce = g_malloc0(l + sizeof(BounceBuffer));
|
||||
+ bounce->magic = BOUNCE_BUFFER_MAGIC;
|
||||
memory_region_ref(mr);
|
||||
- as->bounce.mr = mr;
|
||||
+ bounce->mr = mr;
|
||||
+ bounce->addr = addr;
|
||||
+ bounce->len = l;
|
||||
+
|
||||
if (!is_write) {
|
||||
flatview_read(fv, addr, MEMTXATTRS_UNSPECIFIED,
|
||||
- as->bounce.buffer, l);
|
||||
+ bounce->buffer, l);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
*plen = l;
|
||||
- return as->bounce.buffer;
|
||||
+ return bounce->buffer;
|
||||
}
|
||||
|
||||
-
|
||||
memory_region_ref(mr);
|
||||
*plen = flatview_extend_translation(fv, addr, len, mr, xlat,
|
||||
l, is_write, attrs);
|
||||
@@ -3809,12 +3834,11 @@ void *address_space_map(AddressSpace *as,
|
||||
void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
|
||||
int is_write, hwaddr access_len)
|
||||
{
|
||||
- if (buffer != as->bounce.buffer) {
|
||||
- MemoryRegion *mr;
|
||||
- ram_addr_t addr1;
|
||||
+ MemoryRegion *mr;
|
||||
+ ram_addr_t addr1;
|
||||
|
||||
- mr = memory_region_from_host(buffer, &addr1);
|
||||
- assert(mr != NULL);
|
||||
+ mr = memory_region_from_host(buffer, &addr1);
|
||||
+ if (mr != NULL) {
|
||||
if (is_write) {
|
||||
invalidate_and_set_dirty(mr, addr1, access_len);
|
||||
}
|
||||
@@ -3824,14 +3848,22 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
|
||||
memory_region_unref(mr);
|
||||
return;
|
||||
}
|
||||
+
|
||||
+
|
||||
+ BounceBuffer *bounce = container_of(buffer, BounceBuffer, buffer);
|
||||
+ assert(bounce->magic == BOUNCE_BUFFER_MAGIC);
|
||||
+
|
||||
if (is_write) {
|
||||
- address_space_write(as, as->bounce.addr, MEMTXATTRS_UNSPECIFIED,
|
||||
- as->bounce.buffer, access_len);
|
||||
+ address_space_write(as, bounce->addr, MEMTXATTRS_UNSPECIFIED,
|
||||
+ bounce->buffer, access_len);
|
||||
}
|
||||
- qemu_vfree(as->bounce.buffer);
|
||||
- as->bounce.buffer = NULL;
|
||||
- memory_region_unref(as->bounce.mr);
|
||||
- atomic_mb_set(&as->bounce.in_use, false);
|
||||
+
|
||||
+ atomic_sub(&as->bounce_buffer_size, bounce->len);
|
||||
+ bounce->magic = ~BOUNCE_BUFFER_MAGIC;
|
||||
+ memory_region_unref(bounce->mr);
|
||||
+ g_free(bounce);
|
||||
+ /* Write bounce_buffer_size before reading map_client_list. */
|
||||
+ smp_mb();
|
||||
address_space_notify_map_clients(as);
|
||||
}
|
||||
|
||||
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
|
||||
index 9f6632ae7d..4009f30e6c 100644
|
||||
--- a/hw/pci/pci.c
|
||||
+++ b/hw/pci/pci.c
|
||||
@@ -71,6 +71,8 @@ static Property pci_props[] = {
|
||||
QEMU_PCIE_LNKSTA_DLLLA_BITNR, true),
|
||||
DEFINE_PROP_BIT("x-pcie-extcap-init", PCIDevice, cap_present,
|
||||
QEMU_PCIE_EXTCAP_INIT_BITNR, true),
|
||||
+ DEFINE_PROP_SIZE32("x-max-bounce-buffer-size", PCIDevice,
|
||||
+ max_bounce_buffer_size, DEFAULT_MAX_BOUNCE_BUFFER_SIZE),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
@@ -1051,6 +1053,8 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev,
|
||||
"bus master container", UINT64_MAX);
|
||||
address_space_init(&pci_dev->bus_master_as,
|
||||
&pci_dev->bus_master_container_region, pci_dev->name);
|
||||
+ pci_dev->bus_master_as.max_bounce_buffer_size =
|
||||
+ pci_dev->max_bounce_buffer_size;
|
||||
|
||||
if (qdev_hotplug) {
|
||||
pci_init_bus_master(pci_dev);
|
||||
@@ -2600,6 +2604,10 @@ static void pci_device_class_init(ObjectClass *klass, void *data)
|
||||
k->unrealize = pci_qdev_unrealize;
|
||||
k->bus_type = TYPE_PCI_BUS;
|
||||
k->props = pci_props;
|
||||
+ object_class_property_set_description(
|
||||
+ klass, "x-max-bounce-buffer-size",
|
||||
+ "Maximum buffer size allocated for bounce buffers used for mapped "
|
||||
+ "access to indirect DMA memory", NULL);
|
||||
}
|
||||
|
||||
static void pci_device_class_base_init(ObjectClass *klass, void *data)
|
||||
diff --git a/include/exec/memory.h b/include/exec/memory.h
|
||||
index 84955de627..541a047990 100644
|
||||
--- a/include/exec/memory.h
|
||||
+++ b/include/exec/memory.h
|
||||
@@ -444,13 +444,7 @@ typedef struct AddressSpaceMapClient {
|
||||
QLIST_ENTRY(AddressSpaceMapClient) link;
|
||||
} AddressSpaceMapClient;
|
||||
|
||||
-typedef struct {
|
||||
- MemoryRegion *mr;
|
||||
- void *buffer;
|
||||
- hwaddr addr;
|
||||
- hwaddr len;
|
||||
- bool in_use;
|
||||
-} BounceBuffer;
|
||||
+#define DEFAULT_MAX_BOUNCE_BUFFER_SIZE (4096)
|
||||
|
||||
/**
|
||||
* AddressSpace: describes a mapping of addresses to #MemoryRegion objects
|
||||
@@ -469,8 +463,10 @@ struct AddressSpace {
|
||||
QTAILQ_HEAD(, MemoryListener) listeners;
|
||||
QTAILQ_ENTRY(AddressSpace) address_spaces_link;
|
||||
|
||||
- /* Bounce buffer to use for this address space. */
|
||||
- BounceBuffer bounce;
|
||||
+ /* Maximum DMA bounce buffer size used for indirect memory map requests */
|
||||
+ size_t max_bounce_buffer_size;
|
||||
+ /* Total size of bounce buffers currently allocated, atomically accessed */
|
||||
+ size_t bounce_buffer_size;
|
||||
/* List of callbacks to invoke when buffers free up */
|
||||
QemuMutex map_client_list_lock;
|
||||
QLIST_HEAD(, AddressSpaceMapClient) map_client_list;
|
||||
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
|
||||
index aaf1b9f70d..67bcde6ef9 100644
|
||||
--- a/include/hw/pci/pci.h
|
||||
+++ b/include/hw/pci/pci.h
|
||||
@@ -352,6 +352,9 @@ struct PCIDevice {
|
||||
MSIVectorUseNotifier msix_vector_use_notifier;
|
||||
MSIVectorReleaseNotifier msix_vector_release_notifier;
|
||||
MSIVectorPollNotifier msix_vector_poll_notifier;
|
||||
+
|
||||
+ /* Maximum DMA bounce buffer size used for indirect memory map requests */
|
||||
+ uint32_t max_bounce_buffer_size;
|
||||
};
|
||||
|
||||
void pci_register_bar(PCIDevice *pci_dev, int region_num,
|
||||
diff --git a/memory.c b/memory.c
|
||||
index 67f45f9d15..b77ee70356 100644
|
||||
--- a/memory.c
|
||||
+++ b/memory.c
|
||||
@@ -2810,7 +2810,8 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
|
||||
as->ioeventfds = NULL;
|
||||
QTAILQ_INIT(&as->listeners);
|
||||
QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link);
|
||||
- as->bounce.in_use = false;
|
||||
+ as->max_bounce_buffer_size = DEFAULT_MAX_BOUNCE_BUFFER_SIZE;
|
||||
+ as->bounce_buffer_size = 0;
|
||||
qemu_mutex_init(&as->map_client_list_lock);
|
||||
QLIST_INIT(&as->map_client_list);
|
||||
as->name = g_strdup(name ? name : "anonymous");
|
||||
@@ -2820,7 +2821,7 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
|
||||
|
||||
static void do_address_space_destroy(AddressSpace *as)
|
||||
{
|
||||
- assert(!atomic_read(&as->bounce.in_use));
|
||||
+ assert(atomic_read(&as->bounce_buffer_size) == 0);
|
||||
assert(QLIST_EMPTY(&as->map_client_list));
|
||||
qemu_mutex_destroy(&as->map_client_list_lock);
|
||||
|
||||
--
|
||||
2.45.1.windows.1
|
||||
|
||||
263
system-physmem-Per-AddressSpace-bounce-buffering.patch
Normal file
263
system-physmem-Per-AddressSpace-bounce-buffering.patch
Normal file
@ -0,0 +1,263 @@
|
||||
From 5f828d2fe42344b024e769c7313db85c80c39588 Mon Sep 17 00:00:00 2001
|
||||
From: Mattias Nissler <mnissler@rivosinc.com>
|
||||
Date: Thu, 7 Sep 2023 06:04:23 -0700
|
||||
Subject: [PATCH 2/5] system/physmem: Per-AddressSpace bounce buffering
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
cherry-pick from 69e78f1b3484e429274352a464a94fa1d78be339
|
||||
|
||||
Instead of using a single global bounce buffer, give each AddressSpace
|
||||
its own bounce buffer. The MapClient callback mechanism moves to
|
||||
AddressSpace accordingly.
|
||||
|
||||
This is in preparation for generalizing bounce buffer handling further
|
||||
to allow multiple bounce buffers, with a total allocation limit
|
||||
configured per AddressSpace.
|
||||
|
||||
Reviewed-by: Peter Xu <peterx@redhat.com>
|
||||
Tested-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
|
||||
Signed-off-by: Mattias Nissler <mnissler@rivosinc.com>
|
||||
Message-ID: <20240507094210.300566-2-mnissler@rivosinc.com>
|
||||
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
[PMD: Split patch, part 2/2]
|
||||
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
---
|
||||
exec.c | 79 ++++++++++++++++---------------------------
|
||||
include/exec/memory.h | 19 +++++++++++
|
||||
memory.c | 7 ++++
|
||||
3 files changed, 56 insertions(+), 49 deletions(-)
|
||||
|
||||
diff --git a/exec.c b/exec.c
|
||||
index bb549f63ce..ac4ebaaf27 100644
|
||||
--- a/exec.c
|
||||
+++ b/exec.c
|
||||
@@ -3610,26 +3610,8 @@ void cpu_flush_icache_range(hwaddr start, hwaddr len)
|
||||
NULL, len, FLUSH_CACHE);
|
||||
}
|
||||
|
||||
-typedef struct {
|
||||
- MemoryRegion *mr;
|
||||
- void *buffer;
|
||||
- hwaddr addr;
|
||||
- hwaddr len;
|
||||
- bool in_use;
|
||||
-} BounceBuffer;
|
||||
-
|
||||
-static BounceBuffer bounce;
|
||||
-
|
||||
-typedef struct MapClient {
|
||||
- QEMUBH *bh;
|
||||
- QLIST_ENTRY(MapClient) link;
|
||||
-} MapClient;
|
||||
-
|
||||
-QemuMutex map_client_list_lock;
|
||||
-static QLIST_HEAD(, MapClient) map_client_list
|
||||
- = QLIST_HEAD_INITIALIZER(map_client_list);
|
||||
-
|
||||
-static void address_space_unregister_map_client_do(MapClient *client)
|
||||
+static void
|
||||
+address_space_unregister_map_client_do(AddressSpaceMapClient *client)
|
||||
{
|
||||
QLIST_REMOVE(client, link);
|
||||
g_free(client);
|
||||
@@ -3637,10 +3619,10 @@ static void address_space_unregister_map_client_do(MapClient *client)
|
||||
|
||||
static void address_space_notify_map_clients_locked(AddressSpace *as)
|
||||
{
|
||||
- MapClient *client;
|
||||
+ AddressSpaceMapClient *client;
|
||||
|
||||
- while (!QLIST_EMPTY(&map_client_list)) {
|
||||
- client = QLIST_FIRST(&map_client_list);
|
||||
+ while (!QLIST_EMPTY(&as->map_client_list)) {
|
||||
+ client = QLIST_FIRST(&as->map_client_list);
|
||||
qemu_bh_schedule(client->bh);
|
||||
address_space_unregister_map_client_do(client);
|
||||
}
|
||||
@@ -3648,15 +3630,15 @@ static void address_space_notify_map_clients_locked(AddressSpace *as)
|
||||
|
||||
void address_space_register_map_client(AddressSpace *as, QEMUBH *bh)
|
||||
{
|
||||
- MapClient *client = g_malloc(sizeof(*client));
|
||||
+ AddressSpaceMapClient *client = g_malloc(sizeof(*client));
|
||||
|
||||
- qemu_mutex_lock(&map_client_list_lock);
|
||||
+ qemu_mutex_lock(&as->map_client_list_lock);
|
||||
client->bh = bh;
|
||||
- QLIST_INSERT_HEAD(&map_client_list, client, link);
|
||||
- if (!atomic_read(&bounce.in_use)) {
|
||||
+ QLIST_INSERT_HEAD(&as->map_client_list, client, link);
|
||||
+ if (!atomic_read(&as->bounce.in_use)) {
|
||||
address_space_notify_map_clients_locked(as);
|
||||
}
|
||||
- qemu_mutex_unlock(&map_client_list_lock);
|
||||
+ qemu_mutex_unlock(&as->map_client_list_lock);
|
||||
}
|
||||
|
||||
void cpu_exec_init_all(void)
|
||||
@@ -3672,28 +3654,27 @@ void cpu_exec_init_all(void)
|
||||
finalize_target_page_bits();
|
||||
io_mem_init();
|
||||
memory_map_init();
|
||||
- qemu_mutex_init(&map_client_list_lock);
|
||||
}
|
||||
|
||||
void address_space_unregister_map_client(AddressSpace *as, QEMUBH *bh)
|
||||
{
|
||||
- MapClient *client;
|
||||
+ AddressSpaceMapClient *client;
|
||||
|
||||
- qemu_mutex_lock(&map_client_list_lock);
|
||||
- QLIST_FOREACH(client, &map_client_list, link) {
|
||||
+ qemu_mutex_lock(&as->map_client_list_lock);
|
||||
+ QLIST_FOREACH(client, &as->map_client_list, link) {
|
||||
if (client->bh == bh) {
|
||||
address_space_unregister_map_client_do(client);
|
||||
break;
|
||||
}
|
||||
}
|
||||
- qemu_mutex_unlock(&map_client_list_lock);
|
||||
+ qemu_mutex_unlock(&as->map_client_list_lock);
|
||||
}
|
||||
|
||||
static void address_space_notify_map_clients(AddressSpace *as)
|
||||
{
|
||||
- qemu_mutex_lock(&map_client_list_lock);
|
||||
+ qemu_mutex_lock(&as->map_client_list_lock);
|
||||
address_space_notify_map_clients_locked(as);
|
||||
- qemu_mutex_unlock(&map_client_list_lock);
|
||||
+ qemu_mutex_unlock(&as->map_client_list_lock);
|
||||
}
|
||||
|
||||
static bool flatview_access_valid(FlatView *fv, hwaddr addr, hwaddr len,
|
||||
@@ -3788,27 +3769,27 @@ void *address_space_map(AddressSpace *as,
|
||||
mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs);
|
||||
|
||||
if (!memory_access_is_direct(mr, is_write)) {
|
||||
- if (atomic_xchg(&bounce.in_use, true)) {
|
||||
+ if (atomic_xchg(&as->bounce.in_use, true)) {
|
||||
rcu_read_unlock();
|
||||
*plen = 0;
|
||||
return NULL;
|
||||
}
|
||||
/* Avoid unbounded allocations */
|
||||
l = MIN(l, TARGET_PAGE_SIZE);
|
||||
- bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, l);
|
||||
- bounce.addr = addr;
|
||||
- bounce.len = l;
|
||||
+ as->bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, l);
|
||||
+ as->bounce.addr = addr;
|
||||
+ as->bounce.len = l;
|
||||
|
||||
memory_region_ref(mr);
|
||||
- bounce.mr = mr;
|
||||
+ as->bounce.mr = mr;
|
||||
if (!is_write) {
|
||||
flatview_read(fv, addr, MEMTXATTRS_UNSPECIFIED,
|
||||
- bounce.buffer, l);
|
||||
+ as->bounce.buffer, l);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
*plen = l;
|
||||
- return bounce.buffer;
|
||||
+ return as->bounce.buffer;
|
||||
}
|
||||
|
||||
|
||||
@@ -3828,7 +3809,7 @@ void *address_space_map(AddressSpace *as,
|
||||
void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
|
||||
int is_write, hwaddr access_len)
|
||||
{
|
||||
- if (buffer != bounce.buffer) {
|
||||
+ if (buffer != as->bounce.buffer) {
|
||||
MemoryRegion *mr;
|
||||
ram_addr_t addr1;
|
||||
|
||||
@@ -3844,13 +3825,13 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
|
||||
return;
|
||||
}
|
||||
if (is_write) {
|
||||
- address_space_write(as, bounce.addr, MEMTXATTRS_UNSPECIFIED,
|
||||
- bounce.buffer, access_len);
|
||||
+ address_space_write(as, as->bounce.addr, MEMTXATTRS_UNSPECIFIED,
|
||||
+ as->bounce.buffer, access_len);
|
||||
}
|
||||
- qemu_vfree(bounce.buffer);
|
||||
- bounce.buffer = NULL;
|
||||
- memory_region_unref(bounce.mr);
|
||||
- atomic_mb_set(&bounce.in_use, false);
|
||||
+ qemu_vfree(as->bounce.buffer);
|
||||
+ as->bounce.buffer = NULL;
|
||||
+ memory_region_unref(as->bounce.mr);
|
||||
+ atomic_mb_set(&as->bounce.in_use, false);
|
||||
address_space_notify_map_clients(as);
|
||||
}
|
||||
|
||||
diff --git a/include/exec/memory.h b/include/exec/memory.h
|
||||
index aa35416d80..84955de627 100644
|
||||
--- a/include/exec/memory.h
|
||||
+++ b/include/exec/memory.h
|
||||
@@ -439,6 +439,19 @@ struct MemoryListener {
|
||||
QTAILQ_ENTRY(MemoryListener) link_as;
|
||||
};
|
||||
|
||||
+typedef struct AddressSpaceMapClient {
|
||||
+ QEMUBH *bh;
|
||||
+ QLIST_ENTRY(AddressSpaceMapClient) link;
|
||||
+} AddressSpaceMapClient;
|
||||
+
|
||||
+typedef struct {
|
||||
+ MemoryRegion *mr;
|
||||
+ void *buffer;
|
||||
+ hwaddr addr;
|
||||
+ hwaddr len;
|
||||
+ bool in_use;
|
||||
+} BounceBuffer;
|
||||
+
|
||||
/**
|
||||
* AddressSpace: describes a mapping of addresses to #MemoryRegion objects
|
||||
*/
|
||||
@@ -455,6 +468,12 @@ struct AddressSpace {
|
||||
struct MemoryRegionIoeventfd *ioeventfds;
|
||||
QTAILQ_HEAD(, MemoryListener) listeners;
|
||||
QTAILQ_ENTRY(AddressSpace) address_spaces_link;
|
||||
+
|
||||
+ /* Bounce buffer to use for this address space. */
|
||||
+ BounceBuffer bounce;
|
||||
+ /* List of callbacks to invoke when buffers free up */
|
||||
+ QemuMutex map_client_list_lock;
|
||||
+ QLIST_HEAD(, AddressSpaceMapClient) map_client_list;
|
||||
};
|
||||
|
||||
typedef struct AddressSpaceDispatch AddressSpaceDispatch;
|
||||
diff --git a/memory.c b/memory.c
|
||||
index fa7053f9cb..67f45f9d15 100644
|
||||
--- a/memory.c
|
||||
+++ b/memory.c
|
||||
@@ -2810,6 +2810,9 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
|
||||
as->ioeventfds = NULL;
|
||||
QTAILQ_INIT(&as->listeners);
|
||||
QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link);
|
||||
+ as->bounce.in_use = false;
|
||||
+ qemu_mutex_init(&as->map_client_list_lock);
|
||||
+ QLIST_INIT(&as->map_client_list);
|
||||
as->name = g_strdup(name ? name : "anonymous");
|
||||
address_space_update_topology(as);
|
||||
address_space_update_ioeventfds(as);
|
||||
@@ -2817,6 +2820,10 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
|
||||
|
||||
static void do_address_space_destroy(AddressSpace *as)
|
||||
{
|
||||
+ assert(!atomic_read(&as->bounce.in_use));
|
||||
+ assert(QLIST_EMPTY(&as->map_client_list));
|
||||
+ qemu_mutex_destroy(&as->map_client_list_lock);
|
||||
+
|
||||
assert(QTAILQ_EMPTY(&as->listeners));
|
||||
|
||||
flatview_unref(as->current_map);
|
||||
--
|
||||
2.45.1.windows.1
|
||||
|
||||
206
system-physmem-Propagate-AddressSpace-to-MapClient-h.patch
Normal file
206
system-physmem-Propagate-AddressSpace-to-MapClient-h.patch
Normal file
@ -0,0 +1,206 @@
|
||||
From 3e13a72b4b99a74767267b2e9a9e4f6cafae7e66 Mon Sep 17 00:00:00 2001
|
||||
From: Mattias Nissler <mnissler@rivosinc.com>
|
||||
Date: Thu, 7 Sep 2023 06:04:23 -0700
|
||||
Subject: [PATCH 1/5] system/physmem: Propagate AddressSpace to MapClient
|
||||
helpers
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
cherry-pick from 5c62719710bab66a98f68ebdba333e2240ed6668
|
||||
|
||||
Propagate AddressSpace handler to following helpers:
|
||||
- register_map_client()
|
||||
- unregister_map_client()
|
||||
- notify_map_clients[_locked]()
|
||||
|
||||
Rename them using 'address_space_' prefix instead of 'cpu_'.
|
||||
|
||||
The AddressSpace argument will be used in the next commit.
|
||||
|
||||
Reviewed-by: Peter Xu <peterx@redhat.com>
|
||||
Tested-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
|
||||
Signed-off-by: Mattias Nissler <mnissler@rivosinc.com>
|
||||
Message-ID: <20240507094210.300566-2-mnissler@rivosinc.com>
|
||||
[PMD: Split patch, part 1/2]
|
||||
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
---
|
||||
dma-helpers.c | 4 ++--
|
||||
exec.c | 24 ++++++++++++------------
|
||||
include/exec/cpu-common.h | 2 --
|
||||
include/exec/memory.h | 26 ++++++++++++++++++++++++--
|
||||
4 files changed, 38 insertions(+), 18 deletions(-)
|
||||
|
||||
diff --git a/dma-helpers.c b/dma-helpers.c
|
||||
index d3871dc61e..397b437734 100644
|
||||
--- a/dma-helpers.c
|
||||
+++ b/dma-helpers.c
|
||||
@@ -155,7 +155,7 @@ static void dma_blk_cb(void *opaque, int ret)
|
||||
if (dbs->iov.size == 0) {
|
||||
trace_dma_map_wait(dbs);
|
||||
dbs->bh = aio_bh_new(dbs->ctx, reschedule_dma, dbs);
|
||||
- cpu_register_map_client(dbs->bh);
|
||||
+ address_space_register_map_client(dbs->sg->as, dbs->bh);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ static void dma_aio_cancel(BlockAIOCB *acb)
|
||||
}
|
||||
|
||||
if (dbs->bh) {
|
||||
- cpu_unregister_map_client(dbs->bh);
|
||||
+ address_space_unregister_map_client(dbs->sg->as, dbs->bh);
|
||||
qemu_bh_delete(dbs->bh);
|
||||
dbs->bh = NULL;
|
||||
}
|
||||
diff --git a/exec.c b/exec.c
|
||||
index 0a6ac67c84..bb549f63ce 100644
|
||||
--- a/exec.c
|
||||
+++ b/exec.c
|
||||
@@ -3629,24 +3629,24 @@ QemuMutex map_client_list_lock;
|
||||
static QLIST_HEAD(, MapClient) map_client_list
|
||||
= QLIST_HEAD_INITIALIZER(map_client_list);
|
||||
|
||||
-static void cpu_unregister_map_client_do(MapClient *client)
|
||||
+static void address_space_unregister_map_client_do(MapClient *client)
|
||||
{
|
||||
QLIST_REMOVE(client, link);
|
||||
g_free(client);
|
||||
}
|
||||
|
||||
-static void cpu_notify_map_clients_locked(void)
|
||||
+static void address_space_notify_map_clients_locked(AddressSpace *as)
|
||||
{
|
||||
MapClient *client;
|
||||
|
||||
while (!QLIST_EMPTY(&map_client_list)) {
|
||||
client = QLIST_FIRST(&map_client_list);
|
||||
qemu_bh_schedule(client->bh);
|
||||
- cpu_unregister_map_client_do(client);
|
||||
+ address_space_unregister_map_client_do(client);
|
||||
}
|
||||
}
|
||||
|
||||
-void cpu_register_map_client(QEMUBH *bh)
|
||||
+void address_space_register_map_client(AddressSpace *as, QEMUBH *bh)
|
||||
{
|
||||
MapClient *client = g_malloc(sizeof(*client));
|
||||
|
||||
@@ -3654,7 +3654,7 @@ void cpu_register_map_client(QEMUBH *bh)
|
||||
client->bh = bh;
|
||||
QLIST_INSERT_HEAD(&map_client_list, client, link);
|
||||
if (!atomic_read(&bounce.in_use)) {
|
||||
- cpu_notify_map_clients_locked();
|
||||
+ address_space_notify_map_clients_locked(as);
|
||||
}
|
||||
qemu_mutex_unlock(&map_client_list_lock);
|
||||
}
|
||||
@@ -3675,24 +3675,24 @@ void cpu_exec_init_all(void)
|
||||
qemu_mutex_init(&map_client_list_lock);
|
||||
}
|
||||
|
||||
-void cpu_unregister_map_client(QEMUBH *bh)
|
||||
+void address_space_unregister_map_client(AddressSpace *as, QEMUBH *bh)
|
||||
{
|
||||
MapClient *client;
|
||||
|
||||
qemu_mutex_lock(&map_client_list_lock);
|
||||
QLIST_FOREACH(client, &map_client_list, link) {
|
||||
if (client->bh == bh) {
|
||||
- cpu_unregister_map_client_do(client);
|
||||
+ address_space_unregister_map_client_do(client);
|
||||
break;
|
||||
}
|
||||
}
|
||||
qemu_mutex_unlock(&map_client_list_lock);
|
||||
}
|
||||
|
||||
-static void cpu_notify_map_clients(void)
|
||||
+static void address_space_notify_map_clients(AddressSpace *as)
|
||||
{
|
||||
qemu_mutex_lock(&map_client_list_lock);
|
||||
- cpu_notify_map_clients_locked();
|
||||
+ address_space_notify_map_clients_locked(as);
|
||||
qemu_mutex_unlock(&map_client_list_lock);
|
||||
}
|
||||
|
||||
@@ -3763,8 +3763,8 @@ flatview_extend_translation(FlatView *fv, hwaddr addr,
|
||||
* May map a subset of the requested range, given by and returned in *plen.
|
||||
* May return NULL if resources needed to perform the mapping are exhausted.
|
||||
* Use only for reads OR writes - not for read-modify-write operations.
|
||||
- * Use cpu_register_map_client() to know when retrying the map operation is
|
||||
- * likely to succeed.
|
||||
+ * Use address_space_register_map_client() to know when retrying the map
|
||||
+ * operation is likely to succeed.
|
||||
*/
|
||||
void *address_space_map(AddressSpace *as,
|
||||
hwaddr addr,
|
||||
@@ -3851,7 +3851,7 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
|
||||
bounce.buffer = NULL;
|
||||
memory_region_unref(bounce.mr);
|
||||
atomic_mb_set(&bounce.in_use, false);
|
||||
- cpu_notify_map_clients();
|
||||
+ address_space_notify_map_clients(as);
|
||||
}
|
||||
|
||||
void *cpu_physical_memory_map(hwaddr addr,
|
||||
diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h
|
||||
index f7dbe75fbc..6070fa83ff 100644
|
||||
--- a/include/exec/cpu-common.h
|
||||
+++ b/include/exec/cpu-common.h
|
||||
@@ -86,8 +86,6 @@ void *cpu_physical_memory_map(hwaddr addr,
|
||||
int is_write);
|
||||
void cpu_physical_memory_unmap(void *buffer, hwaddr len,
|
||||
int is_write, hwaddr access_len);
|
||||
-void cpu_register_map_client(QEMUBH *bh);
|
||||
-void cpu_unregister_map_client(QEMUBH *bh);
|
||||
|
||||
bool cpu_physical_memory_is_io(hwaddr phys_addr);
|
||||
|
||||
diff --git a/include/exec/memory.h b/include/exec/memory.h
|
||||
index ad260ebbd6..aa35416d80 100644
|
||||
--- a/include/exec/memory.h
|
||||
+++ b/include/exec/memory.h
|
||||
@@ -2072,8 +2072,8 @@ bool address_space_access_valid(AddressSpace *as, hwaddr addr, hwaddr len,
|
||||
* May return %NULL and set *@plen to zero(0), if resources needed to perform
|
||||
* the mapping are exhausted.
|
||||
* Use only for reads OR writes - not for read-modify-write operations.
|
||||
- * Use cpu_register_map_client() to know when retrying the map operation is
|
||||
- * likely to succeed.
|
||||
+ * Use address_space_register_map_client() to know when retrying the map
|
||||
+ * operation is likely to succeed.
|
||||
*
|
||||
* @as: #AddressSpace to be accessed
|
||||
* @addr: address within that address space
|
||||
@@ -2098,6 +2098,28 @@ void *address_space_map(AddressSpace *as, hwaddr addr,
|
||||
void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
|
||||
int is_write, hwaddr access_len);
|
||||
|
||||
+/*
|
||||
+ * address_space_register_map_client: Register a callback to invoke when
|
||||
+ * resources for address_space_map() are available again.
|
||||
+ *
|
||||
+ * address_space_map may fail when there are not enough resources available,
|
||||
+ * such as when bounce buffer memory would exceed the limit. The callback can
|
||||
+ * be used to retry the address_space_map operation. Note that the callback
|
||||
+ * gets automatically removed after firing.
|
||||
+ *
|
||||
+ * @as: #AddressSpace to be accessed
|
||||
+ * @bh: callback to invoke when address_space_map() retry is appropriate
|
||||
+ */
|
||||
+void address_space_register_map_client(AddressSpace *as, QEMUBH *bh);
|
||||
+
|
||||
+/*
|
||||
+ * address_space_unregister_map_client: Unregister a callback that has
|
||||
+ * previously been registered and not fired yet.
|
||||
+ *
|
||||
+ * @as: #AddressSpace to be accessed
|
||||
+ * @bh: callback to unregister
|
||||
+ */
|
||||
+void address_space_unregister_map_client(AddressSpace *as, QEMUBH *bh);
|
||||
|
||||
/* Internal functions, part of the implementation of address_space_read. */
|
||||
MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
|
||||
--
|
||||
2.45.1.windows.1
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user