From: Mark Syms Subject: Validate guest blkif request segment bounds first_sect/last_sect in a blkif request segment are guest-controlled 8-bit values, but each segment addresses at most a single page (8 sectors). tapdisk_xenblkif_parse_request() only checked last_sect >= first_sect, so a segment with last_sect > 7 yielded an oversized transfer length. That drives out-of-bounds pointer arithmetic against the per-request buffer and overflows the uint16_t gntdev grant-copy length in guest_copy2() (e.g. 68KB truncates to 4KB, so stale buffer contents are transferred). Reject any segment whose sectors fall outside the page, replacing the long-standing TODO at the vectorisation loop. This is CVE-2026-79605, part of XSA-513. Signed-off-by: Mark Syms Co-Authored-By: Claude Opus 4.8 Reviewed-by: Tim Smith diff --git a/drivers/td-req.c b/drivers/td-req.c index 2a3fb05f4ad0..69a947f570ec 100644 --- a/drivers/td-req.c +++ b/drivers/td-req.c @@ -644,8 +644,16 @@ tapdisk_xenblkif_parse_request(struct td_xenblkif * const blkif, /* * Note that first and last may be equal, which means only one sector * must be transferred. + * + * first_sect/last_sect are guest-controlled 8-bit values, but each + * segment addresses at most a single page. Reject any segment whose + * sectors fall outside the page: an out-of-range last_sect would + * produce an oversized transfer length (which also overflows the + * uint16_t gntdev grant-copy length) and drive out-of-bounds accesses + * to the per-request buffer. */ - if (seg->last_sect < seg->first_sect) { + if (seg->last_sect < seg->first_sect || + seg->last_sect >= (PAGE_SIZE >> SECTOR_SHIFT)) { RING_ERR(blkif, "req %lu: invalid sectors %d-%d\n", req->msg.id, seg->first_sect, seg->last_sect); err = EINVAL; @@ -670,7 +678,7 @@ tapdisk_xenblkif_parse_request(struct td_xenblkif * const blkif, struct blkif_request_segment *seg = &req->msg.seg[i]; size_t size; - /* TODO check that first_sect/last_sect are within page */ + /* first_sect/last_sect are already validated, above */ next = page + (seg->first_sect << SECTOR_SHIFT); size = seg->last_sect - seg->first_sect + 1;