From: Stefan Hajnoczi <[email protected]>

The chunk metadata contains both:
- Sector count: number of 512-byte sectors in the virtual disk
- Length: number of bytes in the image file

The UDRW chunk type indicates uncompressed data that can be accessed
directly. The code is missing input validation to verify that sector
count is consistent with length.

If sector count is larger than length, then read requests can access
beyond the end of the s->uncompressed_chunk buffer. This is an
out-of-bounds heap access that could lead to a crash or an information
leak.

While we're at it, also zero the end of the last sector when length is
unaligned. This prevents information leaks from the
s->uncompressed_chunk buffer.

Fixes: CVE-2026-65928
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3846
Reported-by: boy juju <[email protected]>
Signed-off-by: Stefan Hajnoczi <[email protected]>
Message-ID: <[email protected]>
Reviewed-by: Kevin Wolf <[email protected]>
Signed-off-by: Kevin Wolf <[email protected]>
---
 block/dmg.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/block/dmg.c b/block/dmg.c
index 6f8120e0338..5d7d3b8901f 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -312,6 +312,21 @@ static int dmg_read_mish_block(BDRVDMGState *s, 
DmgHeaderState *ds,
             goto fail;
         }
 
+        /*
+         * Uncompressed chunk length must match sector count. Compressed chunks
+         * are validated during dmg_read_chunk() since the uncompressed size is
+         * not known ahead of time.
+         */
+        if (s->types[i] == UDRW) {
+            if (s->sectorcounts[i] != DIV_ROUND_UP(s->lengths[i], 512)) {
+                error_report("length %" PRIu64 " for chunk %" PRIu32
+                             " is inconsistent with sector count %" PRIu64,
+                             s->lengths[i], i, s->sectorcounts[i]);
+                ret = -EINVAL;
+                goto fail;
+            }
+        }
+
         update_max_chunk_size(s, i, &ds->max_compressed_size,
                               &ds->max_sectors_per_chunk);
         offset += 40;
@@ -722,6 +737,16 @@ dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
             if (ret < 0) {
                 return -1;
             }
+
+            /*
+             * Zero the unread part of the last sector when chunk length is
+             * unaligned to avoid exposing uninitialized memory. Valid image
+             * files may never hit this case, but cover it to be safe.
+             */
+            if (s->lengths[chunk] & 511) {
+                size_t trailing_bytes = 512 - (s->lengths[chunk] & 511);
+                memset(s->uncompressed_chunk + s->lengths[chunk], 0, 
trailing_bytes);
+            }
             break;
         case UDZE: /* zeros */
         case UDIG: /* ignore */
-- 
2.55.0


Reply via email to