Re: [PATCH v7 08/22] x86/virt/seamldr: Allocate and populate a module update request

From: Edgecombe, Rick P

Date: Wed Apr 15 2026 - 13:20:05 EST


On Wed, 2026-04-15 at 19:04 +0800, Chao Gao wrote:
> > I wasn't suggesting to merge them. I was suggesting to have them each do
> > a dedicated thing.
>
> Ok. See the code snippet below. Most checks are in init_seamldr_params(),
> except the limit checks on module_size and sig_size. Moving them there
> would require duplicating the module_size/sig_size calculations. So, I
> just keep the checks next to the calculations.

Like this. All checks are moved to get_and_check_blob(). alloc_seamldr_params()
loses all checks because it's about passing things to the TDX module. Also
alloc_seamldr_params() switches to handling page counts only and becomes
smaller.

Below is not even compile tested. Please verify that everything needed
is still checked if you take from it:

/*
* Blob fields are processed by the kernel and the payloads
* are passed to the TDX module. Do normal user input type
* check for any fields that don't get passed to the TDX module.
*/
static void *get_and_check_blob(const u8 *data, u32 size)
{
const struct tdx_blob *blob = (const void *)data;

/*
* Ensure the size is valid otherwise reading any field from the
* blob may overflow.
*/
if (size <= sizeof(struct tdx_blob) || size <= blob->offset_of_module)
return ERR_PTR(-EINVAL);

if (blob->version != TDX_BLOB_VERSION_1)
return ERR_PTR(-EINVAL);

if (blob->reserved0 || memchr_inv(blob->reserved1, 0, sizeof(blob->reserved1)))
return ERR_PTR(-EINVAL);

/*
* Don't care about user passing the wrong file, but protect
* kernel ABI by preventing accepting garbage.
*/
if (memcmp(blob->signature, "TDX-BLOB", 8))
return ERR_PTR(-EINVAL);

return blob;
}

static struct seamldr_params *alloc_seamldr_params(const struct tdx_blob *blob, unsigned int blob_size)
{
struct seamldr_params *params;
int module_pg_cnt, sig_pg_cnt;
const void *sig, *module;
const u8 *ptr;
int i;

params = (struct seamldr_params *)get_zeroed_page(GFP_KERNEL);
if (!params)
return ERR_PTR(-ENOMEM);

/*
* Split the blob into a sigstruct and a module. Assume all
* size/offsets are within bounds of blob_size due to prior checks.
*/
sig = blob->data;
sig_pg_cnt = (blob->offset_of_module - sizeof(struct tdx_blob)) >> PAGE_SHIFT;
module = blob->data + blob->offset_of_module;
module_pg_cnt = (blob_size - blob->offset_of_module) >> PAGE_SHIFT;

/*
* Only use version 1 when required (sigstruct > 4KB) for backward
* compatibility with P-SEAMLDR that lacks version 1 support.
*/
params->version = sig_pg_cnt > 1;
params->scenario = SEAMLDR_SCENARIO_UPDATE;

ptr = sig;
for (i = 0; i < MIN(sig_pg_cnt, SEAMLDR_MAX_NR_SIG_4KB_PAGES); i++) {
/*
* @sig is 4KB-aligned, but that does not imply PAGE_SIZE
* alignment when PAGE_SIZE != SZ_4K. Always include the
* in-page offset.
*/
params->sigstruct_pa[i] = vmalloc_to_pfn(ptr) << PAGE_SHIFT;
ptr += SZ_4K;
}

params->num_module_pages = MIN(module_pg_cnt, SEAMLDR_MAX_NR_MODULE_4KB_PAGES);
ptr = module;
for (i = 0; i < params->num_module_pages; i++) {
params->mod_pages_pa_list[i] = vmalloc_to_pfn(ptr) << PAGE_SHIFT;
ptr += SZ_4K;
}

return params;
}

static struct seamldr_params *init_seamldr_params(const u8 *data, u32 size)
{
const struct tdx_blob *blob;

blob = get_and_check_blob(data, size);
if (IS_ERR(blob))
return ERR_CAST(blob);

return alloc_seamldr_params(blob, size);
}