[PATCH v2 1/2] x86/sev-es: Allow copy_from_kernel_nofault in earlier boot

Adam Dunlap posted 2 patches 2 years, 3 months ago
[PATCH v2 1/2] x86/sev-es: Allow copy_from_kernel_nofault in earlier boot
Posted by Adam Dunlap 2 years, 3 months ago
Previously, if copy_from_kernel_nofault was called before
boot_cpu_data.x86_virt_bits was set up, then it would trigger undefined
behavior due to a shift by 64. This ended up causing boot failures in
the latest version of ubuntu2204 in the gcp project when using SEV-SNP.
Specifically, this function is called during an early #VC handler which
is triggered by a cpuid to check if nx is implemented.

Fixes: 1aa9aa8ee517 ("x86/sev-es: Setup GHCB-based boot #VC handler")
Suggested-by: Dave Hansen <dave.hansen@linux.intel.com>
Signed-off-by: Adam Dunlap <acdunlap@google.com>
---
 arch/x86/mm/maccess.c | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/arch/x86/mm/maccess.c b/arch/x86/mm/maccess.c
index 5a53c2cc169c..6993f026adec 100644
--- a/arch/x86/mm/maccess.c
+++ b/arch/x86/mm/maccess.c
@@ -9,12 +9,21 @@ bool copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size)
 	unsigned long vaddr = (unsigned long)unsafe_src;
 
 	/*
-	 * Range covering the highest possible canonical userspace address
-	 * as well as non-canonical address range. For the canonical range
-	 * we also need to include the userspace guard page.
+	 * Do not allow userspace addresses.  This disallows
+	 * normal userspace and the userspace guard page:
 	 */
-	return vaddr >= TASK_SIZE_MAX + PAGE_SIZE &&
-	       __is_canonical_address(vaddr, boot_cpu_data.x86_virt_bits);
+	if (vaddr < TASK_SIZE_MAX + PAGE_SIZE)
+		return false;
+
+	/*
+	 * Allow everything during early boot before 'x86_virt_bits'
+	 * is initialized.  Needed for instruction decoding in early
+	 * exception handlers.
+	 */
+	if (!boot_cpu_data.x86_virt_bits)
+		return true;
+
+	return __is_canonical_address(vaddr, boot_cpu_data.x86_virt_bits);
 }
 #else
 bool copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size)
-- 
2.42.0.283.g2d96d420d3-goog
Re: [PATCH v2 1/2] x86/sev-es: Allow copy_from_kernel_nofault in earlier boot
Posted by Sean Christopherson 2 years, 2 months ago
On Mon, Sep 11, 2023, Adam Dunlap wrote:
> Previously, if copy_from_kernel_nofault was called before
> boot_cpu_data.x86_virt_bits was set up, then it would trigger undefined
> behavior due to a shift by 64. This ended up causing boot failures in
> the latest version of ubuntu2204 in the gcp project when using SEV-SNP.
> Specifically, this function is called during an early #VC handler which
> is triggered by a cpuid to check if nx is implemented.

Why not stuff boot_cpu_data.x86_virt_bits to a "default" value that is guaranteed
to be accurate (or at least safe) for the purposes of the early boot code.  I.e.
set it to 48 for 64-bit kernels.

That'd avoid the extra conditional, and would avoid laying whack-a-mole with
anything else that consumes x86_virt_bits.

> Fixes: 1aa9aa8ee517 ("x86/sev-es: Setup GHCB-based boot #VC handler")
> Suggested-by: Dave Hansen <dave.hansen@linux.intel.com>
> Signed-off-by: Adam Dunlap <acdunlap@google.com>
> ---
>  arch/x86/mm/maccess.c | 19 ++++++++++++++-----
>  1 file changed, 14 insertions(+), 5 deletions(-)
> 
> diff --git a/arch/x86/mm/maccess.c b/arch/x86/mm/maccess.c
> index 5a53c2cc169c..6993f026adec 100644
> --- a/arch/x86/mm/maccess.c
> +++ b/arch/x86/mm/maccess.c
> @@ -9,12 +9,21 @@ bool copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size)
>  	unsigned long vaddr = (unsigned long)unsafe_src;
>  
>  	/*
> -	 * Range covering the highest possible canonical userspace address
> -	 * as well as non-canonical address range. For the canonical range
> -	 * we also need to include the userspace guard page.
> +	 * Do not allow userspace addresses.  This disallows
> +	 * normal userspace and the userspace guard page:
>  	 */
> -	return vaddr >= TASK_SIZE_MAX + PAGE_SIZE &&
> -	       __is_canonical_address(vaddr, boot_cpu_data.x86_virt_bits);
> +	if (vaddr < TASK_SIZE_MAX + PAGE_SIZE)
> +		return false;
> +
> +	/*
> +	 * Allow everything during early boot before 'x86_virt_bits'
> +	 * is initialized.  Needed for instruction decoding in early
> +	 * exception handlers.
> +	 */
> +	if (!boot_cpu_data.x86_virt_bits)
> +		return true;
> +
> +	return __is_canonical_address(vaddr, boot_cpu_data.x86_virt_bits);
>  }
>  #else
>  bool copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size)
> -- 
> 2.42.0.283.g2d96d420d3-goog
>
Re: [PATCH v2 1/2] x86/sev-es: Allow copy_from_kernel_nofault in earlier boot
Posted by Dave Hansen 2 years, 2 months ago
On 9/20/23 13:37, Sean Christopherson wrote:
> On Mon, Sep 11, 2023, Adam Dunlap wrote:
>> Previously, if copy_from_kernel_nofault was called before
>> boot_cpu_data.x86_virt_bits was set up, then it would trigger undefined
>> behavior due to a shift by 64. This ended up causing boot failures in
>> the latest version of ubuntu2204 in the gcp project when using SEV-SNP.
>> Specifically, this function is called during an early #VC handler which
>> is triggered by a cpuid to check if nx is implemented.
> Why not stuff boot_cpu_data.x86_virt_bits to a "default" value that is guaranteed
> to be accurate (or at least safe) for the purposes of the early boot code.  I.e.
> set it to 48 for 64-bit kernels.
> 
> That'd avoid the extra conditional, and would avoid laying whack-a-mole with
> anything else that consumes x86_virt_bits.

I'd be worried that could break things even more subtly.

If we're truly worried about whack-a-mole, we should stick
'x86_virt_bits' in a wrapper, whine if it's accessed inadvertently, and
*then* return some mostly sane data.

That way we can actually go look at the caller and see what the heck
it's doing.

I did poke around and managed to convince myself that this site _is_ the
only one at the moment.
[tip: x86/mm] x86/sev-es: Allow copy_from_kernel_nofault() in earlier boot
Posted by tip-bot2 for Adam Dunlap 2 years, 2 months ago
The following commit has been merged into the x86/mm branch of tip:

Commit-ID:     f79936545fb122856bd78b189d3c7ee59928c751
Gitweb:        https://git.kernel.org/tip/f79936545fb122856bd78b189d3c7ee59928c751
Author:        Adam Dunlap <acdunlap@google.com>
AuthorDate:    Mon, 11 Sep 2023 17:27:02 -07:00
Committer:     Ingo Molnar <mingo@kernel.org>
CommitterDate: Thu, 28 Sep 2023 22:49:35 +02:00

x86/sev-es: Allow copy_from_kernel_nofault() in earlier boot

Previously, if copy_from_kernel_nofault() was called before
boot_cpu_data.x86_virt_bits was set up, then it would trigger undefined
behavior due to a shift by 64.

This ended up causing boot failures in the latest version of ubuntu2204
in the gcp project when using SEV-SNP.

Specifically, this function is called during an early #VC handler which
is triggered by a CPUID to check if NX is implemented.

Fixes: 1aa9aa8ee517 ("x86/sev-es: Setup GHCB-based boot #VC handler")
Suggested-by: Dave Hansen <dave.hansen@linux.intel.com>
Signed-off-by: Adam Dunlap <acdunlap@google.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Tested-by: Jacob Xu <jacobhxu@google.com>
Link: https://lore.kernel.org/r/20230912002703.3924521-2-acdunlap@google.com
---
 arch/x86/mm/maccess.c | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/arch/x86/mm/maccess.c b/arch/x86/mm/maccess.c
index 5a53c2c..6993f02 100644
--- a/arch/x86/mm/maccess.c
+++ b/arch/x86/mm/maccess.c
@@ -9,12 +9,21 @@ bool copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size)
 	unsigned long vaddr = (unsigned long)unsafe_src;
 
 	/*
-	 * Range covering the highest possible canonical userspace address
-	 * as well as non-canonical address range. For the canonical range
-	 * we also need to include the userspace guard page.
+	 * Do not allow userspace addresses.  This disallows
+	 * normal userspace and the userspace guard page:
 	 */
-	return vaddr >= TASK_SIZE_MAX + PAGE_SIZE &&
-	       __is_canonical_address(vaddr, boot_cpu_data.x86_virt_bits);
+	if (vaddr < TASK_SIZE_MAX + PAGE_SIZE)
+		return false;
+
+	/*
+	 * Allow everything during early boot before 'x86_virt_bits'
+	 * is initialized.  Needed for instruction decoding in early
+	 * exception handlers.
+	 */
+	if (!boot_cpu_data.x86_virt_bits)
+		return true;
+
+	return __is_canonical_address(vaddr, boot_cpu_data.x86_virt_bits);
 }
 #else
 bool copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size)