From: sashiko-bot@kernel.org
To: "Emerson Busson" <emersonbusson@gmail.com>
Cc: linux-hyperv@vger.kernel.org
Subject: Re: [PATCH v2 12/14] hv: netvsc: allocate RNDIS request descriptors with kvzalloc_obj()
Date: Thu, 08 Oct 2026 19:09:36 +0000 [thread overview]
Message-ID: <sashiko-outbox-164497@kernel.org> (raw)
In-Reply-To: <20261007190752.336426-13-emersonbusson@gmail.com>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Critical] hv_netvsc: Double-free and UAF on duplicate RNDIS response
- [Medium] selftests: rndis_request_size_premise_test fails unconditionally on architectures with >4KB pages
- [Medium] netvsc: netvsc_dma_map() leaks earlier DMA mappings when dma_map_single() fails
- [Medium] hyperv: netvsc: DMA mapping leak in RNDIS_MSG_HALT control message
- [Low] kconfig: HYPERV_NET_KUNIT_TEST cannot be enabled when driver is built as a module
--
commit 91156bed3a78e0ac932d5c6bf8e50ef88aa52fa1
Author: Emerson Busson <emersonbusson@gmail.com>
hv: netvsc: allocate RNDIS request descriptors with kvzalloc_obj()
This commit changes the allocation of RNDIS control requests from
kzalloc_obj() to kvzalloc_obj() to avoid order-2 page allocation failures
under buddy fragmentation. It resolves the vmalloc-backed descriptor into
an array of page buffers for the DMA layout and includes KUnit test cases
to cover the allocation and layout requirements.
> diff --git a/drivers/net/hyperv/Kconfig b/drivers/net/hyperv/Kconfig
> index 982964c1a9fb9..226728fd77d71 100644
> --- a/drivers/net/hyperv/Kconfig
> +++ b/drivers/net/hyperv/Kconfig
> @@ -6,3 +6,15 @@ config HYPERV_NET
> select NLS
> help
> Select this option to enable the Hyper-V virtual network driver.
> +
> +config HYPERV_NET_KUNIT_TEST
> + bool "Build Hyper-V netvsc KUnit tests"
> + depends on HYPERV_NET && KUNIT
[Severity: Low]
Will this test be unselectable for modular builds?
Because HYPERV_NET_KUNIT_TEST is a bool depending on the tristate
HYPERV_NET, if HYPERV_NET is built as a module (=m), this boolean symbol
will be clamped to n.
Should this use depends on HYPERV_NET != n && KUNIT instead?
> + default KUNIT_ALL_TESTS
> + help
> + Build the netvsc KUnit test suites into the hv_netvsc object.
[ ... ]
> diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
> index 9b6c44979b4ea..1ce40baa2d239 100644
> --- a/drivers/net/hyperv/rndis_filter.c
> +++ b/drivers/net/hyperv/rndis_filter.c
[ ... ]
> @@ -115,8 +102,8 @@ static struct rndis_request *get_rndis_request(struct rndis_device *dev,
> return request;
> }
>
> -static void put_rndis_request(struct rndis_device *dev,
> - struct rndis_request *req)
> +void put_rndis_request(struct rndis_device *dev,
> + struct rndis_request *req)
> {
> unsigned long flags;
>
> @@ -124,7 +111,8 @@ static void put_rndis_request(struct rndis_device *dev,
> list_del(&req->list_ent);
> spin_unlock_irqrestore(&dev->request_lock, flags);
>
> - kfree(req);
> + /* Paired with the kvzalloc_obj() in get_rndis_request(). */
> + kvfree(req);
> }
[Severity: Medium]
Does freeing the request here permanently leak its DMA mapping when the
device is halted?
When rndis_filter_halt_device() sends an RNDIS_MSG_HALT request, it gets
mapped via netvsc_dma_map(). Because it expects no response,
rndis_filter_receive_response() is never executed to unmap it.
Additionally, the TX completion path in netvsc_send_tx_complete() skips
unmapping for control messages since skb is NULL.
When rndis_filter_halt_device() subsequently calls put_rndis_request(),
the request and its req->pkt.dma_range array are freed, but
dma_unmap_single() is never called, leaking the SWIOTLB mappings.
[Severity: Critical]
Can a duplicate response from the host cause a double-free and
use-after-free here?
When rndis_filter_receive_response() processes a response, it looks up the
request in dev->req_list but does not remove it:
rndis_filter_receive_response() {
spin_lock_irqsave(&dev->request_lock, flags);
list_for_each_entry(request, &dev->req_list, list_ent) {
if (request->request_msg.msg.init_req.req_id == *req_id) {
found = true;
break;
}
}
spin_unlock_irqrestore(&dev->request_lock, flags);
...
}
If the host sends a duplicate RNDIS response with the same req_id, and the
first response has already woken the waiting thread, the waiting thread
can call put_rndis_request() to remove and free the request.
The duplicate response RX thread will then use the freed request pointer
and call netvsc_dma_unmap(), which calls kfree(packet->dma_range) without
checking for NULL.
For isolated VMs (like AMD SEV-SNP or Intel TDX), could an untrusted
Hyper-V host use this to compromise the guest?
> static void dump_rndis_message(struct net_device *netdev,
[ ... ]
> @@ -221,27 +209,78 @@ static void dump_rndis_message(struct net_device *netdev,
> }
> }
>
> +int rndis_build_page_buffers(const void *data, u32 len,
> + struct hv_page_buffer *page_bufs,
> + u32 *page_buf_cnt)
[ ... ]
> static int rndis_filter_send_request(struct rndis_device *dev,
> struct rndis_request *req)
> {
> struct hv_netvsc_packet *packet;
> - struct hv_page_buffer pb;
> + struct hv_page_buffer page_bufs[MAX_PAGE_BUFFER_COUNT];
> + u32 page_buf_cnt;
> int ret;
>
> /* Setup the packet to send it */
> packet = &req->pkt;
>
> packet->total_data_buflen = req->request_msg.msg_len;
> - packet->page_buf_cnt = 1;
> -
> - pb.pfn = virt_to_phys(&req->request_msg) >> HV_HYP_PAGE_SHIFT;
> - pb.len = req->request_msg.msg_len;
> - pb.offset = offset_in_hvpage(&req->request_msg);
> + ret = rndis_build_page_buffers(&req->request_msg,
> + req->request_msg.msg_len,
> + page_bufs, &page_buf_cnt);
> + if (ret)
> + return ret;
> + packet->page_buf_cnt = page_buf_cnt;
>
> trace_rndis_send(dev->ndev, 0, &req->request_msg);
>
> rcu_read_lock_bh();
> - ret = netvsc_send(dev->ndev, packet, NULL, &pb, NULL, false);
> + ret = netvsc_send(dev->ndev, packet, NULL, page_bufs, NULL, false);
> rcu_read_unlock_bh();
[Severity: Medium]
Because page_buf_cnt can now be greater than 1, does this introduce a
resource leak if DMA mapping partially fails?
Inside netvsc_dma_map(), it iterates over page_buf_cnt to map the pages:
netvsc_dma_map() {
...
for (i = 0; i < page_count; i++) {
...
dma = dma_map_single(&hv_dev->device, src, len, DMA_TO_DEVICE);
if (dma_mapping_error(&hv_dev->device, dma)) {
kfree(packet->dma_range);
return -ENOMEM;
}
}
...
}
If dma_map_single() fails on a later iteration (when i > 0), it returns
-ENOMEM but neglects to call dma_unmap_single() for the pages 0 to i - 1
that were successfully mapped in previous loop iterations.
> diff --git a/drivers/net/hyperv/rndis_request_test.c b/drivers/net/hyperv/rndis_request_test.c
> new file mode 100644
> index 0000000000000..6affa916e74e3
> --- /dev/null
> +++ b/drivers/net/hyperv/rndis_request_test.c
[ ... ]
> +static void rndis_request_size_premise_test(struct kunit *test)
> +{
> + KUNIT_EXPECT_GT(test, sizeof(struct rndis_request),
> + (size_t)KMALLOC_MAX_CACHE_SIZE);
[Severity: Medium]
Will this assertion unconditionally fail on architectures with page sizes
larger than 4KB?
KMALLOC_MAX_CACHE_SIZE scales with PAGE_SIZE (typically PAGE_SIZE * 2).
On architectures like ARM64 or PPC64 configured with 64KB pages,
KMALLOC_MAX_CACHE_SIZE is at least 64 KiB.
Since sizeof(struct rndis_request) is slightly larger than 8 KiB (because
RNDIS_EXT_LEN is fixed at 4096), this check will evaluate to roughly
8272 > 65536 on those platforms, causing the test suite to spuriously
fail.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261007190752.336426-1-emersonbusson@gmail.com?part=12
next prev parent reply other threads:[~2026-10-08 19:09 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-07 19:07 [PATCH v2 0/14] hv: vmbus: make rings and host-visible buffers survive buddy fragmentation Emerson Busson
2026-10-07 19:07 ` [PATCH v2 01/14] hv: vmbus: convert ring backing through the chunk allocator Emerson Busson
2026-10-07 19:07 ` [PATCH v2 02/14] hv: vmbus: validate chunk buffer allocation and cleanup Emerson Busson
2026-10-08 21:17 ` kernel test robot
2026-10-07 19:07 ` [PATCH v2 03/14] uio: hv_generic: describe buffers for owned allocation Emerson Busson
2026-10-07 19:07 ` [PATCH v2 04/14] hv: vmbus: add KUnit tests for GPADL post failure injection Emerson Busson
2026-10-07 19:07 ` [PATCH v2 05/14] hv: vmbus: add KUnit test for order-zero allocation fallback Emerson Busson
2026-10-07 19:07 ` [PATCH v2 06/14] hv: vmbus: cover all shared-page policy combinations Emerson Busson
2026-10-07 19:07 ` [PATCH v2 07/14] hv: vmbus: distinguish host rescind from local channel unload Emerson Busson
2026-10-07 19:07 ` [PATCH v2 08/14] hv: vmbus: retain backing until ownership and references clear Emerson Busson
2026-10-08 19:09 ` sashiko-bot
2026-10-07 19:07 ` [PATCH v2 09/14] hv: use owned VMBus buffers in NetVSC and UIO Emerson Busson
2026-10-08 19:09 ` sashiko-bot
2026-10-07 19:07 ` [PATCH v2 10/14] hv: vmbus: pin buffer pages across UIO mmap to close the reclaim race Emerson Busson
2026-10-08 16:49 ` kernel test robot
2026-10-08 17:51 ` Nathan Chancellor
2026-10-08 17:02 ` kernel test robot
2026-10-08 19:09 ` sashiko-bot
2026-10-07 19:07 ` [PATCH v2 11/14] hv: vmbus: vmalloc requestor metadata Emerson Busson
2026-10-08 19:09 ` sashiko-bot
2026-10-07 19:07 ` [PATCH v2 12/14] hv: netvsc: allocate RNDIS request descriptors with kvzalloc_obj() Emerson Busson
2026-10-08 19:09 ` sashiko-bot [this message]
2026-10-07 19:07 ` [PATCH v2 13/14] hv: netvsc: handle a NULL request address on empty completions Emerson Busson
2026-10-08 19:09 ` sashiko-bot
2026-10-07 19:07 ` [PATCH v2 14/14] hv: netvsc: use kvzalloc for device state Emerson Busson
2026-10-08 19:09 ` sashiko-bot
2026-10-08 16:55 ` [PATCH v2 0/14] hv: vmbus: make rings and host-visible buffers survive buddy fragmentation Easwar Hariharan
2026-10-13 17:52 ` 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=sashiko-outbox-164497@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=emersonbusson@gmail.com \
--cc=linux-hyperv@vger.kernel.org \
--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.