From: sashiko-bot@kernel.org
To: "Michael Bommarito" <michael.bommarito@gmail.com>
Cc: linux-hyperv@vger.kernel.org
Subject: Re: [PATCH] hv: hv_balloon: validate unballoon range count
Date: Fri, 10 Jul 2026 02:42:47 +0000 [thread overview]
Message-ID: <20260710024247.ABEE11F00A3A@smtp.kernel.org> (raw)
In-Reply-To: <20260710022914.3740453-1-michael.bommarito@gmail.com>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
Pre-existing issues:
- [Critical] free_balloon_pages() blindly trusts untrusted host-provided PFNs, causing arbitrary memory free.
- [High] Teardown race in balloon_remove() leads to Use-After-Free.
- [High] Data race when updating worker arguments from VMBus softirq callbacks.
- [High] Concurrent lockless modification of dm->num_pages_ballooned.
- [High] Pending workqueues are leaked on the balloon_probe() error path.
--
--- Patch [1]: [PATCH] hv: hv_balloon: validate unballoon range count ---
commit cffe2d5855cc693f743d4cc5594c147a6fff1bdb
Author: Michael Bommarito <michael.bommarito@gmail.com>
hv: hv_balloon: validate unballoon range count
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.
> diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c
> index 42ce27be344d5..0ebfc2524d630 100644
> --- a/drivers/hv/hv_balloon.c
> +++ b/drivers/hv/hv_balloon.c
[ ... ]
> @@ -1346,6 +1361,12 @@ static void balloon_down(struct hv_dynmem_device *dm,
> int i;
> unsigned int prev_pages_ballooned = dm->num_pages_ballooned;
[Severity: High]
This is a pre-existing issue, but is it safe to modify
dm->num_pages_ballooned without locking or atomic operations?
If a DM_UNBALLOON_REQUEST arrives while a DM_BALLOON_REQUEST is still being
processed, free_balloon_pages() executes synchronously in the VMBus tasklet
context and decrements the counter:
drivers/hv/hv_balloon.c:free_balloon_pages() {
...
dm->num_pages_ballooned--;
...
}
At the same time, alloc_balloon_pages() executes asynchronously in the
workqueue and increments it:
drivers/hv/hv_balloon.c:alloc_balloon_pages() {
...
dm->num_pages_ballooned += alloc_unit;
...
}
Could lost updates corrupt the guest's memory pressure statistics, leading
to incorrect accounting when reported back to the host via
get_pages_committed()?
>
> + 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);
[Severity: Critical]
This is a pre-existing issue, but does free_balloon_pages() blindly trust
the untrusted host-provided PFNs?
If a malicious host sends a DM_UNBALLOON_REQUEST specifying arbitrary PFNs
(e.g., kernel text or page tables), the guest iterates over these PFNs and
unconditionally frees them without validating if the pages were actually
ballooned out:
drivers/hv/hv_balloon.c:free_balloon_pages() {
...
for (i = 0; i < num_pages; i++) {
pg = pfn_to_page(i + start_frame);
__ClearPageOffline(pg);
__free_page(pg);
...
}
Can this lead to an immediate guest crash or arbitrary memory corruption via
freeing active kernel or user pages to the buddy allocator?
[ ... ]
> @@ -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:
[Severity: High]
This is a pre-existing issue, but are worker arguments safely updated during
VMBus softirq callbacks?
The VMBus interrupt handler balloon_onchannelcallback() directly overwrites
shared worker arguments without any locking or synchronization:
drivers/hv/hv_balloon.c:balloon_onchannelcallback() {
...
case DM_BALLOON_REQUEST:
...
dm_device.balloon_wrk.num_pages = bal_msg->num_pages;
schedule_work(&dm_device.balloon_wrk.wrk);
break;
...
case DM_MEM_HOT_ADD_REQUEST:
...
dm->ha_wrk.ha_page_range = *ha_pg_range;
dm->ha_wrk.ha_region_range.page_range = 0;
...
}
If an untrusted host floods the guest with back-to-back request messages,
can worker threads operate on torn, mixed, or corrupted parameters, leading
to incorrect guest memory operations?
[Severity: High]
This isn't a bug introduced by this patch, but does balloon_remove() have a
teardown race that violates subsystem rules?
The function calls cancel_work_sync() before vmbus_close():
drivers/hv/hv_balloon.c:balloon_remove() {
...
cancel_work_sync(&dm->balloon_wrk.wrk);
cancel_work_sync(&dm->ha_wrk.wrk);
...
if (dm_device.state != DM_INIT_ERROR) {
disable_page_reporting();
vmbus_close(dev->channel);
...
}
Because the VMBus channel remains open and active, an incoming interrupt can
trigger balloon_onchannelcallback() and independently reschedule the work
item after it was already cancelled.
Could this lead to a use-after-free of a work struct and device state,
causing a kernel crash upon module unload or device unbind?
[Severity: High]
This is a pre-existing issue, but does the probe_error path leak pending
workqueues?
In balloon_probe(), balloon_connect_vsp() opens the channel, allowing the
host to immediately send messages that schedule balloon_wrk or ha_wrk. If
the subsequent kthread_run() step fails, the code jumps to probe_error,
closes the channel, and returns an error without cancelling the work items:
drivers/hv/hv_balloon.c:balloon_probe() {
...
probe_error:
dm_device.state = DM_INIT_ERROR;
dm_device.thread = NULL;
disable_page_reporting();
vmbus_close(dev->channel);
connect_error:
...
}
Can this result in asynchronous execution of a leaked work item on
uninitialized or freed device state, causing a use-after-free?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260710022914.3740453-1-michael.bommarito@gmail.com?part=1
next prev parent reply other threads:[~2026-07-10 2:42 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-10 2:29 [PATCH] hv: hv_balloon: validate unballoon range count Michael Bommarito
2026-07-10 2:42 ` sashiko-bot [this message]
2026-07-11 18:09 ` Michael Kelley
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260710024247.ABEE11F00A3A@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=linux-hyperv@vger.kernel.org \
--cc=michael.bommarito@gmail.com \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.