@samitouri / QOSamiQemu / commits / 370882d086

dmg: reject inconsistent UDRW chunk sector count and length (CVE-2026-65928)

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 <agx1657748706@gmail.com> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Message-ID: <20260723144519.364701-4-stefanha@redhat.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>

Stefan Hajnoczi committed Jul 23, 2026 at 10:45 UTC 370882d0869567e5f21b95229a763171e319d0db
1 file changed +25
block/dmg.c
+25
@@ -312,6 +312,21 @@ static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds,
312 goto fail;
313 }
314
315 + /*
316 + * Uncompressed chunk length must match sector count. Compressed chunks
317 + * are validated during dmg_read_chunk() since the uncompressed size is
318 + * not known ahead of time.
319 + */
320 + if (s->types[i] == UDRW) {
321 + if (s->sectorcounts[i] != DIV_ROUND_UP(s->lengths[i], 512)) {
322 + error_report("length %" PRIu64 " for chunk %" PRIu32
323 + " is inconsistent with sector count %" PRIu64,
324 + s->lengths[i], i, s->sectorcounts[i]);
325 + ret = -EINVAL;
326 + goto fail;
327 + }
328 + }
329 +
330 update_max_chunk_size(s, i, &ds->max_compressed_size,
331 &ds->max_sectors_per_chunk);
332 offset += 40;
@@ -722,6 +737,16 @@ dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
737 if (ret < 0) {
738 return -1;
739 }
740 +
741 + /*
742 + * Zero the unread part of the last sector when chunk length is
743 + * unaligned to avoid exposing uninitialized memory. Valid image
744 + * files may never hit this case, but cover it to be safe.
745 + */
746 + if (s->lengths[chunk] & 511) {
747 + size_t trailing_bytes = 512 - (s->lengths[chunk] & 511);
748 + memset(s->uncompressed_chunk + s->lengths[chunk], 0, trailing_bytes);
749 + }
750 break;
751 case UDZE: /* zeros */
752 case UDIG: /* ignore */