Re: [PATCH v1 12/13] hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader)

[email protected] Fri, 07 Aug 2026 16:22:50 +0000
Newsgroups org.kernel.vger.linux-hyperv
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 9 potential issue(s) to consider:
- [Critical] The x86 EFI stub stores the command line in a `.bss` static buffer which is wiped by the kernel decompressor.
- [High] Unconditional increment of `cmdline_len` with `snprintf` return value leads to buffer overflow.
- [High] Incorrect linked list traversal causes truncation of the existing `setup_data` chain.
- [Medium] Missing NULL pointer check before calling `strlen` on the command line.
- [Medium] Skipping invalid e820 indices leaves uninitialized holes in `hv_mshv_res`, breaking resource registration.
- [Medium] MSHV root partition boot enablement is completely unreachable on ARM64.
- [Low] Memory leak of `mshv_reserved` array in x86 EFI stub.
- [Low] Memory leak of `mem_map` in ARM64 EFI stub.
- [Low] Parsing error in `hyperv_resvd_new` bypasses state updates, silencing previous valid ranges.
--

--- Patch [12]: [PATCH v1 12/13] hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader) ---
commit fcbf57de7557f7a5bf335c4003de65861d33a7c5
Author: Wei Hu <[email protected]>

hyperv: add MSHV Dom0 root-partition boot enablement (EFI HvLoader)

This commit ports the EFI HvLoader protocol handshake and the hyperv_resvd_new
memory reservation. It provides the Linux-side enablement necessary to boot an
upstream kernel as a Microsoft Hypervisor (MSHV) root partition.

> --- a/arch/x86/kernel/cpu/mshyperv.c
> +++ b/arch/x86/kernel/cpu/mshyperv.c
[ ... ]
> @@ -496,6 +499,75 @@ EXPORT_SYMBOL_GPL(hv_get_hypervisor_version);
> +static void __init hv_resv_mshv_memory(void)
> +{
> +	u64 start, end, size;
> +	int i, idx, max = ARRAY_SIZE(hv_resvd_ranges);
> +
> +	for (i = 0; i < max && hv_resvd_ranges[i] != -1; i++) {
> +		idx = hv_resvd_ranges[i];
> +		if (idx < 0 || idx >= e820_table->nr_entries) {
> +			pr_info("Hyper-V: invalid resvd idx %d\n", idx);
> +			continue;

[Severity: Medium]
Does skipping the invalid index here leave the hv_mshv_res array
zero-initialized for this slot?

Later on, hv_mark_resources() iterates over this array using:
    for (i = 0; i < max && hv_mshv_res[i].end; i++)

Could this cause the registration loop to terminate prematurely when it hits
the hole, silently dropping all subsequent valid memory ranges?

> +		}
> +
> +		start = e820_table->entries[idx].addr;

> --- /dev/null
> +++ b/drivers/firmware/efi/libstub/arm64-efi-mshv.c
> @@ -0,0 +1,41 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <linux/efi.h>
> +#include <asm/efi.h>
> +#include <asm/setup.h>
> +
> +#include "efistub.h"
> +#include "efi-mshv.h"
> +
> +efi_status_t mshv_efi_setup(char **cmdline_ptr)
> +{

[Severity: Medium]
Is this function ever invoked? It appears to be defined here but never hooked
into the ARM64 EFI stub entry point, making the ARM64 MSHV boot enablement
unreachable code.

> +	efi_status_t status;
> +	efi_memory_desc_t *mem_map;
> +	unsigned long map_sz, desc_sz, new_cmdline_addr;
> +
> +	status = mshv_efi_init();
> +	if (status == EFI_NOT_FOUND) // we are in a standard Linux boot
> +		return EFI_SUCCESS;
> +
> +	map_sz = 0;
> +	mshv_get_hv_ranges((void *)&mem_map, &map_sz, &desc_sz);
> +
> +	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, COMMAND_LINE_SIZE,
> +			     (void **)&new_cmdline_addr);

[Severity: Low]
Does this code leak the mem_map pool allocation? Unlike the x86 implementation
in this patch, it doesn't appear to be freed before returning EFI_SUCCESS.

> +	if (status != EFI_SUCCESS)

> --- /dev/null
> +++ b/drivers/firmware/efi/libstub/efi-mshv-common.c
[ ... ]
> +void mshv_efi_update_cmdline(efi_memory_desc_t *mem_map,
> +			     unsigned long map_sz, unsigned long desc_sz, char *old_cmdline,
> +		char *buf, unsigned long buf_sz)
> +{
> +	int i, cmdline_len, nr_desc;
> +
> +	cmdline_len = strlen(old_cmdline);

[Severity: Medium]
Can old_cmdline be NULL if the kernel is booted without a command line?
If so, passing it directly to strlen() would lead to a NULL pointer
dereference.

> +	memcpy(buf, old_cmdline, cmdline_len + 1);
> +
> +	cmdline_len += snprintf(buf + cmdline_len,
> +				buf_sz - cmdline_len,
> +				" hyperv_resvd_new=");
> +
> +	nr_desc = map_sz / desc_sz;
> +	for (i = 0; i < nr_desc; ++i) {
> +		efi_memory_desc_t *d;
> +		u64 start, end, sz;
> +
> +		d = efi_memdesc_ptr(mem_map, desc_sz, i);
> +		start = d->phys_addr;
> +		sz = d->num_pages << PAGE_SHIFT;
> +		end = start + sz - 1;
> +
> +		cmdline_len += snprintf(buf + cmdline_len,
> +					buf_sz - cmdline_len,
> +					"%s0x%llx!0x%llx", i > 0 ? "," : "",
> +					sz, start);

[Severity: High]
Could this result in a buffer overflow?

If the appended string exceeds buf_sz, snprintf returns the number of
characters that would have been written, not the number actually written. 
Adding this value to cmdline_len could cause buf_sz - cmdline_len to underflow
into a very large unsigned integer.

Will the next loop iteration then write out of bounds via buf + cmdline_len?

> +	}

> --- /dev/null
> +++ b/drivers/firmware/efi/libstub/x86-efi-mshv.c
[ ... ]
> +static efi_status_t mshv_populate_ranges(struct boot_params *boot_params,
> +					 efi_memory_desc_t *mem_map, unsigned long map_sz,
> +			unsigned long desc_sz)
> +{
> +	unsigned long cmdline_ptr;
> +	u32 cmdline_size;
> +	static u8 mshv_cmdline[COMMAND_LINE_SIZE];

[Severity: Critical]
Will this static buffer be placed in the .bss segment and subsequently
destroyed?

When the EFI stub hands over control to the kernel decompressor
(e.g. startup_64), the decompressor zeroes its .bss segment before the command
line is parsed. Using a static buffer here might cause the entire kernel
command line to be lost.

> +
> +	memset(mshv_cmdline, 0, sizeof(mshv_cmdline));
> +
> +	cmdline_ptr = boot_params->hdr.cmd_line_ptr;
> +	cmdline_ptr |= (u64)boot_params->ext_cmd_line_ptr << 32;
> +	cmdline_size = boot_params->hdr.cmdline_size;
> +
> +	mshv_efi_update_cmdline(mem_map, map_sz, desc_sz,
> +				(char *)cmdline_ptr,
> +				(char *)mshv_cmdline, COMMAND_LINE_SIZE);
> +
> +	boot_params->hdr.cmd_line_ptr = (u32)((unsigned long)mshv_cmdline);
[ ... ]
> +efi_status_t mshv_efi_setup(struct boot_params *boot_params)
> +{
[ ... ]
> +	/*
> +	 * Build an array of kernel 'struct resource' objects that contain mshv
> +	 * reserved ranges. This array is populated via a command line parameter
> +	 * called 'hyperv_resvd_new'.
> +	 */
> +
> +	status = mshv_realloc_ranges(&mshv_reserved,
> +				     &mshv_reserved_sz,
> +				MSHV_RESERVED_RANGES_COUNT);

[Severity: Low]
Does this code leak the mshv_reserved array memory? It is dynamically
allocated here and possibly reallocated during the loop, but it doesn't appear
to be freed at the end of the function.

[ ... ]
> +	setup_data_itr = (struct setup_data **)&boot_params->hdr.setup_data;
> +
> +	while (*setup_data_itr && (*setup_data_itr)->next)
> +		setup_data_itr = (struct setup_data **)&(*setup_data_itr)->next;
> +
> +	*setup_data_itr = (struct setup_data *)sd_block;

[Severity: High]
Does this loop terminate one node early?

Because it checks (*setup_data_itr)->next in the while condition, it exits
when pointing to the last node. Overwriting *setup_data_itr here seems to
overwrite the pointer to the last element rather than appending to it, which
could drop critical boot parameters passed by the bootloader.

> +
> +	for (i = 0; i < nr_ranges; i++) {

> --- a/drivers/hv/hv_common.c
> +++ b/drivers/hv/hv_common.c
[ ... ]
> @@ -863,3 +867,81 @@ const char *hv_result_to_string(u64 status)
> +static int __init hv_parse_hyperv_resvd_new(char *arg)
> +{
> +	int i = 0;
> +
> +	if (is_kdump_kernel())
> +		return 0;
> +
> +	while (arg && *arg) {
> +		unsigned long long region_start, region_sz;
> +		char *range, *addr_str;
> +
> +		if (i >= HV_MAX_RESVD_RANGES) {
> +			pr_err("Hyper-V: too many hyperv_resvd_new ranges specified\n");
> +			return 0;
> +		}
> +
> +		/* Each range is "<size>!<address>", ranges separated by ','. */
> +		range = strsep(&arg, ",");
> +		addr_str = strchr(range, '!');
> +		if (!addr_str) {
> +			pr_err("Hyper-V: invalid format for hyperv_resvd_new: %s\n", range);
> +			return 0;

[Severity: Low]
If an invalid format is encountered, does this early return bypass updating
the global ranges_nr counter?

By returning early instead of breaking the loop, ranges_nr = i is skipped,
which might cause any previously parsed valid memory ranges to be silently
ignored by hv_dump_mshv_memory() later.

> +		}

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=12