Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2221791/?format=api
{ "id": 2221791, "url": "http://patchwork.ozlabs.org/api/patches/2221791/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260410121128.484298-1-stefanha@redhat.com/", "project": { "id": 14, "url": "http://patchwork.ozlabs.org/api/projects/14/?format=api", "name": "QEMU Development", "link_name": "qemu-devel", "list_id": "qemu-devel.nongnu.org", "list_email": "qemu-devel@nongnu.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260410121128.484298-1-stefanha@redhat.com>", "list_archive_url": null, "date": "2026-04-10T12:11:28", "name": "[for-11.0,v2] virtio-blk: fix zone report buffer out-of-memory (CVE-2026-5761)", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "76b010c90ca8c323bd14ccffca441ee6428a2141", "submitter": { "id": 17227, "url": "http://patchwork.ozlabs.org/api/people/17227/?format=api", "name": "Stefan Hajnoczi", "email": "stefanha@redhat.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260410121128.484298-1-stefanha@redhat.com/mbox/", "series": [ { "id": 499448, "url": "http://patchwork.ozlabs.org/api/series/499448/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=499448", "date": "2026-04-10T12:11:28", "name": "[for-11.0,v2] virtio-blk: fix zone report buffer out-of-memory (CVE-2026-5761)", "version": 2, "mbox": "http://patchwork.ozlabs.org/series/499448/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2221791/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2221791/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>", "X-Original-To": "incoming@patchwork.ozlabs.org", "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=SbANj8+P;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)" ], "Received": [ "from lists.gnu.org (lists1p.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fsbKJ4YhJz1y2d\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 10 Apr 2026 22:12:34 +1000 (AEST)", "from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1wBAi9-0002TX-9L; Fri, 10 Apr 2026 08:11:45 -0400", "from eggs.gnu.org ([2001:470:142:3::10])\n by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <stefanha@redhat.com>)\n id 1wBAi6-0002T1-W8\n for qemu-devel@nongnu.org; Fri, 10 Apr 2026 08:11:43 -0400", "from us-smtp-delivery-124.mimecast.com ([170.10.129.124])\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <stefanha@redhat.com>)\n id 1wBAi2-0007k3-Nh\n for qemu-devel@nongnu.org; Fri, 10 Apr 2026 08:11:42 -0400", "from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-35-qq7-JAkwMW-4ULZraZ6mEg-1; Fri,\n 10 Apr 2026 08:11:34 -0400", "from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id 5A6D918002CA; Fri, 10 Apr 2026 12:11:32 +0000 (UTC)", "from localhost (unknown [10.44.33.180])\n by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP\n id 745291800451; Fri, 10 Apr 2026 12:11:29 +0000 (UTC)" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1775823098;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:\n content-transfer-encoding:content-transfer-encoding;\n bh=s64fkcW9RNZ/Nid4PqeO03AMfVCBp32bi6Piw+jLGMc=;\n b=SbANj8+PO1sci8Sa0nNDJ5CQ/7+1fsGYhUuSDDiewBf3jKdfrNPEayt+8Mbn0H3MZ2BgvG\n b58SSbo/zC+ybTPKV+mCnKdJdAS7A+TDIuW9ih23DULnD5qBBbFQKcmtWfH+vY2T/kxlNN\n J/zoOSGdmvKHaqYTIpaqSUz9F7ZTYJs=", "X-MC-Unique": "qq7-JAkwMW-4ULZraZ6mEg-1", "X-Mimecast-MFC-AGG-ID": "qq7-JAkwMW-4ULZraZ6mEg_1775823093", "From": "Stefan Hajnoczi <stefanha@redhat.com>", "To": "qemu-devel@nongnu.org", "Cc": "\"Michael S. Tsirkin\" <mst@redhat.com>, qemu-block@nongnu.org,\n Stefan Hajnoczi <stefanha@redhat.com>, <qemu-stable@nongnu.org>,\n Hanna Reitz <hreitz@redhat.com>, Peter Maydell <peter.maydell@linaro.org>,\n Kevin Wolf <kwolf@redhat.com>, Sam Li <faithilikerun@gmail.com>,\n Damien Le Moal <dlemoal@kernel.org>,\n Dmitry Fomichev <dmitry.fomichev@wdc.com>,\n Mingyuan Luo <myluo24@m.fudan.edu.cn>", "Subject": "[PATCH for-11.0 v2] virtio-blk: fix zone report buffer out-of-memory\n (CVE-2026-5761)", "Date": "Fri, 10 Apr 2026 08:11:28 -0400", "Message-ID": "<20260410121128.484298-1-stefanha@redhat.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "X-Scanned-By": "MIMEDefang 3.4.1 on 10.30.177.93", "Received-SPF": "pass client-ip=170.10.129.124;\n envelope-from=stefanha@redhat.com;\n helo=us-smtp-delivery-124.mimecast.com", "X-Spam_score_int": "-25", "X-Spam_score": "-2.6", "X-Spam_bar": "--", "X-Spam_report": "(-2.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.54,\n DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1,\n RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001,\n RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001,\n SPF_HELO_PASS=-0.001,\n SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no", "X-Spam_action": "no action", "X-BeenThere": "qemu-devel@nongnu.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "qemu development <qemu-devel.nongnu.org>", "List-Unsubscribe": "<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>", "List-Archive": "<https://lists.nongnu.org/archive/html/qemu-devel>", "List-Post": "<mailto:qemu-devel@nongnu.org>", "List-Help": "<mailto:qemu-devel-request@nongnu.org?subject=help>", "List-Subscribe": "<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>", "Errors-To": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org", "Sender": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org" }, "content": "An internal buffer is used when processing VIRTIO_BLK_T_ZONE_REPORT\nrequests. The buffer's size is controlled by the guest. A large value\ncan result in g_malloc() failure and the QEMU process aborts, resulting\nin a Denial of Service (DoS) (most likely in cases where an untrusted\nguest application or a nested guest with virtio-blk passthrough is able\nto abort QEMU).\n\nModify the zone report implementation to work incrementally with a\nbounded buffer size.\n\nThis is purely a QEMU implementation issue and no VIRTIO spec changes\nare needed.\n\nMingyuan Luo found this bug and provided a reproducer which I haven't\nput into tests/qtest/ because it requires a zoned storage device (e.g.\nroot and modprobe null_blk):\n\n1) Prepare a zoned nullblk backend (/dev/nullb0):\n\nsudo modprobe -r null_blk || true\nsudo modprobe null_blk nr_devices=1 zoned=1\nsudo chmod 0666 /dev/nullb0\ncat /sys/block/nullb0/queue/zoned\n\n2) Create qtest input:\n\ncat >/tmp/vblk-zone-report-oom.qtest <<'EOF'\noutl 0xcf8 0x80002004\noutw 0xcfc 0x0007\noutl 0xcf8 0x80002010\noutl 0xcfc 0x0000c001\noutb 0xc012 0x00\noutb 0xc012 0x01\noutb 0xc012 0x03\noutl 0xc004 0x00000000\noutw 0xc00e 0x0000\noutl 0xc008 0x00000100\noutb 0xc012 0x07\nwritel 0x00020000 0x00000010\nwritel 0x00020004 0x00000000\nwriteq 0x00020008 0x0000000000000000\nwriteq 0x00100000 0x0000000000020000\nwritel 0x00100008 0x00000010\nwritew 0x0010000c 0x0001\nwritew 0x0010000e 0x0001\nEOF\n\nfor i in $(seq 1 1022); do\nd=$((0x00100000 + i * 16))\nn=$((i + 1))\nprintf 'writeq 0x%08x 0x0000000000200000\\n' \"$d\" >> /tmp/vblk-zone-report-oom.qtest\nprintf 'writel 0x%08x 0x1fe00000\\n' $((d + 8)) >> /tmp/vblk-zone-report-oom.qtest\nprintf 'writew 0x%08x 0x0003\\n' $((d + 12)) >> /tmp/vblk-zone-report-oom.qtest\nprintf 'writew 0x%08x 0x%04x\\n' $((d + 14)) \"$n\" >> /tmp/vblk-zone-report-oom.qtest\ndone\n\nd=$((0x00100000 + 1023 * 16))\nprintf 'writeq 0x%08x 0x0000000000200000\\n' \"$d\" >> /tmp/vblk-zone-report-oom.qtest\nprintf 'writel 0x%08x 0x1fe00000\\n' $((d + 8)) >> /tmp/vblk-zone-report-oom.qtest\nprintf 'writew 0x%08x 0x0002\\n' $((d + 12)) >> /tmp/vblk-zone-report-oom.qtest\nprintf 'writew 0x%08x 0x0000\\n' $((d + 14)) >> /tmp/vblk-zone-report-oom.qtest\ncat >> /tmp/vblk-zone-report-oom.qtest <<'EOF'\nwritew 0x00104000 0x0000\nwritew 0x00104002 0x0001\nwritew 0x00104004 0x0000\noutw 0xc010 0x0000\nEOF\n\n3) Run the qtest input with ASAN build (compile qemu with --enable-asan):\n\nbuild/qemu-system-x86_64 -display none \\\n-accel qtest -qtest stdio \\\n-machine pc -nodefaults -m 512M -monitor none -serial none \\\n-blockdev driver=host_device,node-name=disk0,filename=/dev/nullb0 \\\n-device virtio-blk-pci-transitional,drive=disk0,addr=04.0,queue-size=1024 \\\n< /tmp/vblk-zone-report-oom.qtest\n\nCc: Sam Li <faithilikerun@gmail.com>\nCc: Damien Le Moal <dlemoal@kernel.org>\nCc: Dmitry Fomichev <dmitry.fomichev@wdc.com>\nFixes: CVE-2026-5761\nFixes: 4f7366506a9 (\"virtio-blk: add zoned storage emulation for zoned devices\")\nReported-by: Mingyuan Luo <myluo24@m.fudan.edu.cn>\nReviewed-by: Damien Le Moal <dlemoal@kernel.org>\nSigned-off-by: Stefan Hajnoczi <stefanha@redhat.com>\n---\nv2:\n- Fix int64_t nz vs size_t j type mismatch in\n virtio_blk_zone_report_complete() [Damien]\n\n hw/block/virtio-blk.c | 100 ++++++++++++++++++++++++++++--------------\n 1 file changed, 67 insertions(+), 33 deletions(-)", "diff": "diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c\nindex ddf0e9ee53..9cb9f1fb2b 100644\n--- a/hw/block/virtio-blk.c\n+++ b/hw/block/virtio-blk.c\n@@ -38,6 +38,9 @@\n #include \"hw/virtio/virtio-blk-common.h\"\n #include \"qemu/coroutine.h\"\n \n+/* Internal buffer size limit for zone report */\n+#define VIRTIO_BLK_MAX_ZONES_PER_BATCH 4096\n+\n static void virtio_blk_ioeventfd_attach(VirtIOBlock *s);\n \n static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq,\n@@ -447,15 +450,22 @@ err:\n return err_status;\n }\n \n+typedef struct {\n+ unsigned int total_nr_zones; /* max zones to fill in this request */\n+ unsigned int nr_zones_done; /* how many zones have been filled in */\n+ int64_t iov_offset; /* current byte position in in_iov[] */\n+ int64_t offset; /* current zone report disk offset */\n+ unsigned int nr_zones; /* for zone report calls */\n+ unsigned int zones_per_batch; /* size of zone report buffer */\n+ BlockZoneDescriptor *zones; /* zone report buffer */\n+} ZoneReportData;\n+\n typedef struct ZoneCmdData {\n VirtIOBlockReq *req;\n struct iovec *in_iov;\n unsigned in_num;\n union {\n- struct {\n- unsigned int nr_zones;\n- BlockZoneDescriptor *zones;\n- } zone_report_data;\n+ ZoneReportData zone_report_data;\n struct {\n int64_t offset;\n } zone_append_data;\n@@ -512,16 +522,15 @@ static bool check_zoned_request(VirtIOBlock *s, int64_t offset, int64_t len,\n static void virtio_blk_zone_report_complete(void *opaque, int ret)\n {\n ZoneCmdData *data = opaque;\n+ ZoneReportData *zrd = &data->zone_report_data;\n VirtIOBlockReq *req = data->req;\n VirtIODevice *vdev = VIRTIO_DEVICE(req->dev);\n struct iovec *in_iov = data->in_iov;\n unsigned in_num = data->in_num;\n- int64_t zrp_size, n, j = 0;\n- int64_t nz = data->zone_report_data.nr_zones;\n+ int64_t n;\n+ unsigned nz = zrd->nr_zones;\n int8_t err_status = VIRTIO_BLK_S_OK;\n- struct virtio_blk_zone_report zrp_hdr = (struct virtio_blk_zone_report) {\n- .nr_zones = cpu_to_le64(nz),\n- };\n+ struct virtio_blk_zone_report zrp_hdr = {};\n \n trace_virtio_blk_zone_report_complete(vdev, req, nz, ret);\n if (ret) {\n@@ -529,28 +538,18 @@ static void virtio_blk_zone_report_complete(void *opaque, int ret)\n goto out;\n }\n \n- zrp_size = sizeof(struct virtio_blk_zone_report)\n- + sizeof(struct virtio_blk_zone_descriptor) * nz;\n- n = iov_from_buf(in_iov, in_num, 0, &zrp_hdr, sizeof(zrp_hdr));\n- if (n != sizeof(zrp_hdr)) {\n- virtio_error(vdev, \"Driver provided input buffer that is too small!\");\n- err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD;\n- goto out;\n- }\n-\n- for (size_t i = sizeof(zrp_hdr); i < zrp_size;\n- i += sizeof(struct virtio_blk_zone_descriptor), ++j) {\n+ for (unsigned j = 0; j < nz; j++) {\n struct virtio_blk_zone_descriptor desc =\n (struct virtio_blk_zone_descriptor) {\n- .z_start = cpu_to_le64(data->zone_report_data.zones[j].start\n+ .z_start = cpu_to_le64(zrd->zones[j].start\n >> BDRV_SECTOR_BITS),\n- .z_cap = cpu_to_le64(data->zone_report_data.zones[j].cap\n+ .z_cap = cpu_to_le64(zrd->zones[j].cap\n >> BDRV_SECTOR_BITS),\n- .z_wp = cpu_to_le64(data->zone_report_data.zones[j].wp\n+ .z_wp = cpu_to_le64(zrd->zones[j].wp\n >> BDRV_SECTOR_BITS),\n };\n \n- switch (data->zone_report_data.zones[j].type) {\n+ switch (zrd->zones[j].type) {\n case BLK_ZT_CONV:\n desc.z_type = VIRTIO_BLK_ZT_CONV;\n break;\n@@ -564,7 +563,7 @@ static void virtio_blk_zone_report_complete(void *opaque, int ret)\n g_assert_not_reached();\n }\n \n- switch (data->zone_report_data.zones[j].state) {\n+ switch (zrd->zones[j].state) {\n case BLK_ZS_RDONLY:\n desc.z_state = VIRTIO_BLK_ZS_RDONLY;\n break;\n@@ -594,18 +593,47 @@ static void virtio_blk_zone_report_complete(void *opaque, int ret)\n }\n \n /* TODO: it takes O(n^2) time complexity. Optimizations required. */\n- n = iov_from_buf(in_iov, in_num, i, &desc, sizeof(desc));\n+ n = iov_from_buf(in_iov, in_num, zrd->iov_offset, &desc, sizeof(desc));\n if (n != sizeof(desc)) {\n virtio_error(vdev, \"Driver provided input buffer \"\n \"for descriptors that is too small!\");\n err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD;\n+ goto out;\n }\n+\n+ zrd->iov_offset += sizeof(desc);\n+ }\n+\n+ if (nz > 0) {\n+ BlockZoneDescriptor *zone = &zrd->zones[nz - 1];\n+ zrd->offset = zone->start + zone->length;\n+ }\n+\n+ zrd->nr_zones_done += nz;\n+\n+ /* Call zone report again if the end hasn't been reached yet */\n+ if (nz == zrd->zones_per_batch &&\n+ zrd->nr_zones_done < zrd->total_nr_zones) {\n+ zrd->nr_zones = MIN(zrd->zones_per_batch,\n+ zrd->total_nr_zones - zrd->nr_zones_done);\n+ blk_aio_zone_report(req->dev->blk, zrd->offset, &zrd->nr_zones,\n+ zrd->zones, virtio_blk_zone_report_complete, data);\n+ return;\n+ }\n+\n+ /* Fill in header now that all zones have been reported */\n+ zrp_hdr.nr_zones = cpu_to_le64(zrd->nr_zones_done);\n+ n = iov_from_buf(in_iov, in_num, 0, &zrp_hdr, sizeof(zrp_hdr));\n+ if (n != sizeof(zrp_hdr)) {\n+ virtio_error(vdev, \"Driver provided input buffer that is too small!\");\n+ err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD;\n+ goto out;\n }\n \n out:\n virtio_blk_req_complete(req, err_status);\n g_free(req);\n- g_free(data->zone_report_data.zones);\n+ g_free(zrd->zones);\n g_free(data);\n }\n \n@@ -617,7 +645,8 @@ static void virtio_blk_handle_zone_report(VirtIOBlockReq *req,\n VirtIODevice *vdev = VIRTIO_DEVICE(s);\n unsigned int nr_zones;\n ZoneCmdData *data;\n- int64_t zone_size, offset;\n+ ZoneReportData *zrd;\n+ int64_t offset;\n uint8_t err_status;\n \n if (req->in_len < sizeof(struct virtio_blk_inhdr) +\n@@ -639,16 +668,21 @@ static void virtio_blk_handle_zone_report(VirtIOBlockReq *req,\n trace_virtio_blk_handle_zone_report(vdev, req,\n offset >> BDRV_SECTOR_BITS, nr_zones);\n \n- zone_size = sizeof(BlockZoneDescriptor) * nr_zones;\n data = g_malloc(sizeof(ZoneCmdData));\n data->req = req;\n data->in_iov = in_iov;\n data->in_num = in_num;\n- data->zone_report_data.nr_zones = nr_zones;\n- data->zone_report_data.zones = g_malloc(zone_size),\n \n- blk_aio_zone_report(s->blk, offset, &data->zone_report_data.nr_zones,\n- data->zone_report_data.zones,\n+ zrd = &data->zone_report_data;\n+ zrd->total_nr_zones = nr_zones;\n+ zrd->nr_zones_done = 0;\n+ zrd->iov_offset = sizeof(struct virtio_blk_zone_report);\n+ zrd->offset = offset;\n+ zrd->zones_per_batch = MIN(nr_zones, VIRTIO_BLK_MAX_ZONES_PER_BATCH);\n+ zrd->zones = g_malloc(zrd->zones_per_batch * sizeof(BlockZoneDescriptor));\n+\n+ zrd->nr_zones = zrd->zones_per_batch;\n+ blk_aio_zone_report(s->blk, offset, &zrd->nr_zones, zrd->zones,\n virtio_blk_zone_report_complete, data);\n return;\n out:\n", "prefixes": [ "for-11.0", "v2" ] }