Message ID | fe0e1a1133166ca4008840cd1a5959fa70632f07.1666824663.git.kai.huang@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | TDX host kernel support | expand |
> +/* Calculate the actual TDMR_INFO size */ > +static inline int cal_tdmr_size(void) > +{ > + int tdmr_sz; > + > + /* > + * The actual size of TDMR_INFO depends on the maximum number > + * of reserved areas. > + */ > + tdmr_sz = sizeof(struct tdmr_info); > + tdmr_sz += sizeof(struct tdmr_reserved_area) * > + tdx_sysinfo.max_reserved_per_tdmr; would seem safer to have a overflow check here.
On Thu, 2022-10-27 at 08:31 -0700, Andi Kleen wrote: > > +/* Calculate the actual TDMR_INFO size */ > > +static inline int cal_tdmr_size(void) > > +{ > > + int tdmr_sz; > > + > > + /* > > + * The actual size of TDMR_INFO depends on the maximum number > > + * of reserved areas. > > + */ > > + tdmr_sz = sizeof(struct tdmr_info); > > + tdmr_sz += sizeof(struct tdmr_reserved_area) * > > + tdx_sysinfo.max_reserved_per_tdmr; > > > would seem safer to have a overflow check here. > > How about below? --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -614,6 +614,14 @@ static inline int cal_tdmr_size(void) tdmr_sz += sizeof(struct tdmr_reserved_area) * tdx_sysinfo.max_reserved_per_tdmr; + /* + * Do simple check against overflow, and return 0 (invalid) + * TDMR_INFO size if it happened. Also WARN() as it should + * should never happen in reality. + */ + if (WARN_ON_ONCE(tdmr_sz < 0)) + return 0; + /* * TDX requires each TDMR_INFO to be 512-byte aligned. Always * round up TDMR_INFO size to the 512-byte boundary. @@ -623,19 +631,27 @@ static inline int cal_tdmr_size(void) static struct tdmr_info *alloc_tdmr_array(int *array_sz) { + int sz; + /* * TDX requires each TDMR_INFO to be 512-byte aligned. * Use alloc_pages_exact() to allocate all TDMRs at once. * Each TDMR_INFO will still be 512-byte aligned since * cal_tdmr_size() always return 512-byte aligned size. */ - *array_sz = cal_tdmr_size() * tdx_sysinfo.max_tdmrs; + sz = cal_tdmr_size() * tdx_sysinfo.max_tdmrs; + + /* Overflow */ + if (!sz || WARN_ON_ONCE(sz < 0)) + return NULL; + + *array_sz = sz; /* * Zero the buffer so 'struct tdmr_info::size' can be * used to determine whether a TDMR is valid. */ - return alloc_pages_exact(*array_sz, GFP_KERNEL | __GFP_ZERO); + return alloc_pages_exact(sz, GFP_KERNEL | __GFP_ZERO); } Btw, should I use alloc_contig_pages() instead of alloc_pages_exact() as IIUC the latter should fail if the size is larger than 4MB? In reality, the entire array only takes dozens of KBs, though.
On Fri, 2022-10-28 at 02:21 +0000, Huang, Kai wrote: > On Thu, 2022-10-27 at 08:31 -0700, Andi Kleen wrote: > > > +/* Calculate the actual TDMR_INFO size */ > > > +static inline int cal_tdmr_size(void) > > > +{ > > > + int tdmr_sz; > > > + > > > + /* > > > + * The actual size of TDMR_INFO depends on the maximum number > > > + * of reserved areas. > > > + */ > > > + tdmr_sz = sizeof(struct tdmr_info); > > > + tdmr_sz += sizeof(struct tdmr_reserved_area) * > > > + tdx_sysinfo.max_reserved_per_tdmr; > > > > > > would seem safer to have a overflow check here. > > > > > > How about below? > > --- a/arch/x86/virt/vmx/tdx/tdx.c > +++ b/arch/x86/virt/vmx/tdx/tdx.c > @@ -614,6 +614,14 @@ static inline int cal_tdmr_size(void) > tdmr_sz += sizeof(struct tdmr_reserved_area) * > tdx_sysinfo.max_reserved_per_tdmr; > > + /* > + * Do simple check against overflow, and return 0 (invalid) > + * TDMR_INFO size if it happened. Also WARN() as it should > + * should never happen in reality. > + */ > + if (WARN_ON_ONCE(tdmr_sz < 0)) > + return 0; > + > /* > * TDX requires each TDMR_INFO to be 512-byte aligned. Always > * round up TDMR_INFO size to the 512-byte boundary. > @@ -623,19 +631,27 @@ static inline int cal_tdmr_size(void) > > static struct tdmr_info *alloc_tdmr_array(int *array_sz) > { > + int sz; > + > /* > * TDX requires each TDMR_INFO to be 512-byte aligned. > * Use alloc_pages_exact() to allocate all TDMRs at once. > * Each TDMR_INFO will still be 512-byte aligned since > * cal_tdmr_size() always return 512-byte aligned size. > */ > - *array_sz = cal_tdmr_size() * tdx_sysinfo.max_tdmrs; > + sz = cal_tdmr_size() * tdx_sysinfo.max_tdmrs; > + > + /* Overflow */ > + if (!sz || WARN_ON_ONCE(sz < 0)) > + return NULL; > + > + *array_sz = sz; > > /* > * Zero the buffer so 'struct tdmr_info::size' can be > * used to determine whether a TDMR is valid. > */ > - return alloc_pages_exact(*array_sz, GFP_KERNEL | __GFP_ZERO); > + return alloc_pages_exact(sz, GFP_KERNEL | __GFP_ZERO); > } > > > Btw, should I use alloc_contig_pages() instead of alloc_pages_exact() as IIUC > the latter should fail if the size is larger than 4MB? In reality, the entire > array only takes dozens of KBs, though. Hi Andi, Could you take a look whether this is OK? Also could you take a look my replies to your other comments? Thanks!
On 10/27/22 08:31, Andi Kleen wrote: > >> +/* Calculate the actual TDMR_INFO size */ >> +static inline int cal_tdmr_size(void) >> +{ >> + int tdmr_sz; >> + >> + /* >> + * The actual size of TDMR_INFO depends on the maximum number >> + * of reserved areas. >> + */ >> + tdmr_sz = sizeof(struct tdmr_info); >> + tdmr_sz += sizeof(struct tdmr_reserved_area) * >> + tdx_sysinfo.max_reserved_per_tdmr; > > would seem safer to have a overflow check here. tdmr_reserved_area is 16 bytes. To overflow a signed int, tdmr_sz would need to be for an allocation >2GB. alloc_pages_exact() tops out at supplying 4MB allocations. So, sure, this breaks at max_reserved_per_tdmr>2^27, but it actually breaks *EARLIER* at max_reserved_per_tdmr>2^18 because the page allocator is borked. Plus, max_reserved_per_tdmr is barely in double digits today. It's a *LOOOOOOOOONG* way from either of those limits. If you want to add a warning here, then go for it and enforce a sane value on max_reserved_per_tdmr. But, the overflow is *LITERALLY* an order of magnitude more obscure than overwhelming the page allocator. Let's not clutter up the code with silly checks like that.
On Thu, 2022-11-03 at 08:05 -0700, Hansen, Dave wrote: > Plus, max_reserved_per_tdmr is barely in double digits today. It's a > *LOOOOOOOOONG* way from either of those limits. If you want to add a > warning here, then go for it and enforce a sane value on > max_reserved_per_tdmr. Hi Dave, Thanks. By "enforce a sane value on max_reserved_per_tdmr' could you be more specific? Did you mean if we find its value is insanely big, we can change it to a reasonable smaller value? But I don't think we can as the TDMR_INFO is used by the TDX module, so reducing max_reserved_per_tdmr by the kernel doesn't actually work? Perhaps for now we can make the kernel to assume TDMR_INFO won't exceed a reasonable value (i.e. 4K/8K/16K?) and max_tdmrs (which is 64 currently) won't exceed a reasonable value either (i.e. 1K/512/256?), so that we can just use alloc_pages_exact() to allocate the entire TDMR array? If kernel found either is too big, then kernel could just fail to initialize the TDX module.
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index ff3ef7ed4509..ba577d357aef 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -16,6 +16,8 @@ #include <linux/cpu.h> #include <linux/cpumask.h> #include <linux/smp.h> +#include <linux/gfp.h> +#include <linux/align.h> #include <linux/atomic.h> #include <asm/msr-index.h> #include <asm/msr.h> @@ -536,6 +538,53 @@ static int sanity_check_tdx_memory(void) return 0; } +/* Calculate the actual TDMR_INFO size */ +static inline int cal_tdmr_size(void) +{ + int tdmr_sz; + + /* + * The actual size of TDMR_INFO depends on the maximum number + * of reserved areas. + */ + tdmr_sz = sizeof(struct tdmr_info); + tdmr_sz += sizeof(struct tdmr_reserved_area) * + tdx_sysinfo.max_reserved_per_tdmr; + + /* + * TDX requires each TDMR_INFO to be 512-byte aligned. Always + * round up TDMR_INFO size to the 512-byte boundary. + */ + return ALIGN(tdmr_sz, TDMR_INFO_ALIGNMENT); +} + +static struct tdmr_info *alloc_tdmr_array(int *array_sz) +{ + /* + * TDX requires each TDMR_INFO to be 512-byte aligned. + * Use alloc_pages_exact() to allocate all TDMRs at once. + * Each TDMR_INFO will still be 512-byte aligned since + * cal_tdmr_size() always return 512-byte aligned size. + */ + *array_sz = cal_tdmr_size() * tdx_sysinfo.max_tdmrs; + + /* + * Zero the buffer so 'struct tdmr_info::size' can be + * used to determine whether a TDMR is valid. + */ + return alloc_pages_exact(*array_sz, GFP_KERNEL | __GFP_ZERO); +} + +/* + * Construct an array of TDMRs to cover all TDX memory ranges. + * The actual number of TDMRs is kept to @tdmr_num. + */ +static int construct_tdmrs(struct tdmr_info *tdmr_array, int *tdmr_num) +{ + /* Return -EINVAL until constructing TDMRs is done */ + return -EINVAL; +} + /* * Detect and initialize the TDX module. * @@ -545,6 +594,9 @@ static int sanity_check_tdx_memory(void) */ static int init_tdx_module(void) { + struct tdmr_info *tdmr_array; + int tdmr_array_sz; + int tdmr_num; int ret; /* @@ -572,11 +624,31 @@ static int init_tdx_module(void) ret = sanity_check_tdx_memory(); if (ret) goto out; + + /* Prepare enough space to construct TDMRs */ + tdmr_array = alloc_tdmr_array(&tdmr_array_sz); + if (!tdmr_array) { + ret = -ENOMEM; + goto out; + } + + /* Construct TDMRs to cover all TDX memory ranges */ + ret = construct_tdmrs(tdmr_array, &tdmr_num); + if (ret) + goto out_free_tdmrs; + /* * Return -EINVAL until all steps of TDX module initialization * process are done. */ ret = -EINVAL; +out_free_tdmrs: + /* + * The array of TDMRs is freed no matter the initialization is + * successful or not. They are not needed anymore after the + * module initialization. + */ + free_pages_exact(tdmr_array, tdmr_array_sz); out: return ret; } diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h index 8e273756098c..a737f2b51474 100644 --- a/arch/x86/virt/vmx/tdx/tdx.h +++ b/arch/x86/virt/vmx/tdx/tdx.h @@ -80,6 +80,29 @@ struct tdsysinfo_struct { }; } __packed __aligned(TDSYSINFO_STRUCT_ALIGNMENT); +struct tdmr_reserved_area { + u64 offset; + u64 size; +} __packed; + +#define TDMR_INFO_ALIGNMENT 512 + +struct tdmr_info { + u64 base; + u64 size; + u64 pamt_1g_base; + u64 pamt_1g_size; + u64 pamt_2m_base; + u64 pamt_2m_size; + u64 pamt_4k_base; + u64 pamt_4k_size; + /* + * Actual number of reserved areas depends on + * 'struct tdsysinfo_struct'::max_reserved_per_tdmr. + */ + struct tdmr_reserved_area reserved_areas[0]; +} __packed __aligned(TDMR_INFO_ALIGNMENT); + /* * Do not put any hardware-defined TDX structure representations below * this comment!