RE: [PATCH] hv: hv_balloon: validate unballoon range count
Michael Kelley <[email protected]> Sat, 11 Jul 2026 18:09:53 +0000
| Newsgroups | org.kernel.vger.linux-hyperv,org.kernel.vger.linux-kernel,org.kernel.vger.stable |
|---|---|
| Message-ID | <SN6PR02MB4157893628C03F09EB14697BD4FC2@SN6PR02MB4157.namprd02.prod.outlook.com> |
From: Michael Bommarito <[email protected]> Sent: Thursday, July 9, 2026 7:29 PM > > The Hyper-V dynamic memory host supplies DM_UNBALLOON_REQUEST messages > with a header size and a range_count field. balloon_down() trusts > range_count and walks req->range_array without checking that the received > message contains that many ranges. > > A malformed host or backend message can therefore make the guest read > past the received VMBus packet while freeing balloon ranges. Validate the > received message size and reject range_count values that exceed the > present range array before walking it. Same comment applies here as I wrote for your proposed validations for the Hyper-V mouse driver. The balloon driver also has .allowed_in_isolated set to false, so it isn't loaded in a CoCo VM and it hasn't been hardened for the "untrusted host" threat model. Michael > > Impact: A malicious Hyper-V host or backend can crash a guest by sending > a short unballoon request with an oversized range_count. > > Fixes: 9aa8b50b2b3d ("Drivers: hv: Add Hyper-V balloon driver") > Cc: [email protected] > Assisted-by: Codex:gpt-5-5-xhigh > Signed-off-by: Michael Bommarito <[email protected]> > --- > drivers/hv/hv_balloon.c | 26 ++++++++++++++++++++++++-- > 1 file changed, 24 insertions(+), 2 deletions(-) > > diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c > index a848400a59a2d..f5bc8c9fea7b9 100644 > --- a/drivers/hv/hv_balloon.c > +++ b/drivers/hv/hv_balloon.c > @@ -1337,8 +1337,23 @@ static void balloon_up(struct work_struct *dummy) > } > } > > +static bool unballoon_request_valid(struct dm_unballoon_request *req, > + u32 msg_size) > +{ > + u32 max_ranges; > + > + if (msg_size < sizeof(*req) || req->hdr.size < sizeof(*req) || > + req->hdr.size > msg_size) > + return false; > + > + max_ranges = (req->hdr.size - sizeof(*req)) / > + sizeof(req->range_array[0]); > + > + return req->range_count <= max_ranges; > +} > + > static void balloon_down(struct hv_dynmem_device *dm, > - struct dm_unballoon_request *req) > + struct dm_unballoon_request *req, u32 msg_size) > { > union dm_mem_page_range *range_array = req->range_array; > int range_count = req->range_count; > @@ -1346,6 +1361,12 @@ static void balloon_down(struct hv_dynmem_device *dm, > int i; > unsigned int prev_pages_ballooned = dm->num_pages_ballooned; > > + if (!unballoon_request_valid(req, msg_size)) { > + pr_warn_ratelimited("Invalid unballoon request: size %u, header size > %u, range count %u\n", > + msg_size, req->hdr.size, req->range_count); > + return; > + } > + > for (i = 0; i < range_count; i++) { > free_balloon_pages(dm, &range_array[i]); > complete(&dm_device.config_event); > @@ -1527,7 +1548,8 @@ static void balloon_onchannelcallback(void *context) > > dm->state = DM_BALLOON_DOWN; > balloon_down(dm, > - (struct dm_unballoon_request *)recv_buffer); > + (struct dm_unballoon_request *)recv_buffer, > + recvlen); > break; > > case DM_MEM_HOT_ADD_REQUEST: > -- > 2.53.0