[PATCHv9 11/16] x86/traps: Communicate a LASS violation in #GP message

Kirill A. Shutemov posted 16 patches 3 months ago
There is a newer version of this series
[PATCHv9 11/16] x86/traps: Communicate a LASS violation in #GP message
Posted by Kirill A. Shutemov 3 months ago
From: Alexander Shishkin <alexander.shishkin@linux.intel.com>

Provide a more helpful message on #GP when a kernel side LASS violation
is detected.

A NULL pointer dereference is reported if a LASS violation occurs due to
accessing the first page frame.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
---
 arch/x86/kernel/traps.c | 41 +++++++++++++++++++++++++++++------------
 1 file changed, 29 insertions(+), 12 deletions(-)

diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 59bfbdf0a1a0..4a4194e1d119 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -636,7 +636,16 @@ DEFINE_IDTENTRY(exc_bounds)
 enum kernel_gp_hint {
 	GP_NO_HINT,
 	GP_NON_CANONICAL,
-	GP_CANONICAL
+	GP_CANONICAL,
+	GP_LASS_VIOLATION,
+	GP_NULL_POINTER,
+};
+
+static const char * const kernel_gp_hint_help[] = {
+	[GP_NON_CANONICAL]	= "probably for non-canonical address",
+	[GP_CANONICAL]		= "maybe for address",
+	[GP_LASS_VIOLATION]	= "LASS prevented access to address",
+	[GP_NULL_POINTER]	= "kernel NULL pointer dereference",
 };
 
 /*
@@ -664,14 +673,23 @@ static enum kernel_gp_hint get_kernel_gp_address(struct pt_regs *regs,
 		return GP_NO_HINT;
 
 #ifdef CONFIG_X86_64
-	/*
-	 * Check that:
-	 *  - the operand is not in the kernel half
-	 *  - the last byte of the operand is not in the user canonical half
-	 */
-	if (*addr < ~__VIRTUAL_MASK &&
-	    *addr + insn.opnd_bytes - 1 > __VIRTUAL_MASK)
+	/* Operand is in the kernel half */
+	if (*addr >= ~__VIRTUAL_MASK)
+		return GP_CANONICAL;
+
+	/* The last byte of the operand is not in the user canonical half */
+	if (*addr + insn.opnd_bytes - 1 > __VIRTUAL_MASK)
 		return GP_NON_CANONICAL;
+
+	/*
+	 * If LASS is enabled, NULL pointer dereference generates
+	 * #GP instead of #PF.
+	 */
+	if (*addr < PAGE_SIZE)
+		return GP_NULL_POINTER;
+
+	if (cpu_feature_enabled(X86_FEATURE_LASS))
+		return GP_LASS_VIOLATION;
 #endif
 
 	return GP_CANONICAL;
@@ -833,11 +851,10 @@ DEFINE_IDTENTRY_ERRORCODE(exc_general_protection)
 	else
 		hint = get_kernel_gp_address(regs, &gp_addr);
 
-	if (hint != GP_NO_HINT)
+	if (hint != GP_NO_HINT) {
 		snprintf(desc, sizeof(desc), GPFSTR ", %s 0x%lx",
-			 (hint == GP_NON_CANONICAL) ? "probably for non-canonical address"
-						    : "maybe for address",
-			 gp_addr);
+			 kernel_gp_hint_help[hint], gp_addr);
+	}
 
 	/*
 	 * KASAN is interested only in the non-canonical case, clear it
-- 
2.47.2
Re: [PATCHv9 11/16] x86/traps: Communicate a LASS violation in #GP message
Posted by Sohil Mehta 3 months ago
On 7/7/2025 1:03 AM, Kirill A. Shutemov wrote:
> From: Alexander Shishkin <alexander.shishkin@linux.intel.com>
> 
> Provide a more helpful message on #GP when a kernel side LASS violation
> is detected.
> 
> A NULL pointer dereference is reported if a LASS violation occurs due to
> accessing the first page frame.
> 
> Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
> Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
> ---
>  arch/x86/kernel/traps.c | 41 +++++++++++++++++++++++++++++------------
>  1 file changed, 29 insertions(+), 12 deletions(-)
> 

Reviewed-by: Sohil Mehta <sohil.mehta@intel.com>

A nit below.

> diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
> index 59bfbdf0a1a0..4a4194e1d119 100644
> --- a/arch/x86/kernel/traps.c
> +++ b/arch/x86/kernel/traps.c
> @@ -636,7 +636,16 @@ DEFINE_IDTENTRY(exc_bounds)
>  enum kernel_gp_hint {
>  	GP_NO_HINT,
>  	GP_NON_CANONICAL,
> -	GP_CANONICAL
> +	GP_CANONICAL,
> +	GP_LASS_VIOLATION,
> +	GP_NULL_POINTER,
> +};
> +
> +static const char * const kernel_gp_hint_help[] = {
> +	[GP_NON_CANONICAL]	= "probably for non-canonical address",
> +	[GP_CANONICAL]		= "maybe for address",
> +	[GP_LASS_VIOLATION]	= "LASS prevented access to address",
> +	[GP_NULL_POINTER]	= "kernel NULL pointer dereference",
>  };
>  
>  /*
> @@ -664,14 +673,23 @@ static enum kernel_gp_hint get_kernel_gp_address(struct pt_regs *regs,
>  		return GP_NO_HINT;
>  
>  #ifdef CONFIG_X86_64

Might as well get rid of the #ifdef in C code, if possible.

if (!IS_ENABLED(CONFIG_X86_64)
	return GP_CANONICAL;

or combine it with the next check.

> -	/*
> -	 * Check that:
> -	 *  - the operand is not in the kernel half
> -	 *  - the last byte of the operand is not in the user canonical half
> -	 */
> -	if (*addr < ~__VIRTUAL_MASK &&
> -	    *addr + insn.opnd_bytes - 1 > __VIRTUAL_MASK)
> +	/* Operand is in the kernel half */
> +	if (*addr >= ~__VIRTUAL_MASK)
> +		return GP_CANONICAL;
> +
> +	/* The last byte of the operand is not in the user canonical half */
> +	if (*addr + insn.opnd_bytes - 1 > __VIRTUAL_MASK)
>  		return GP_NON_CANONICAL;
> +
> +	/*
> +	 * If LASS is enabled, NULL pointer dereference generates
> +	 * #GP instead of #PF.
> +	 */
> +	if (*addr < PAGE_SIZE)
> +		return GP_NULL_POINTER;
> +
> +	if (cpu_feature_enabled(X86_FEATURE_LASS))
> +		return GP_LASS_VIOLATION;
>  #endif
>  
>  	return GP_CANONICAL;
Re: [PATCHv9 11/16] x86/traps: Communicate a LASS violation in #GP message
Posted by Kirill A. Shutemov 3 months ago
On Tue, Jul 08, 2025 at 07:40:35PM -0700, Sohil Mehta wrote:
> > @@ -664,14 +673,23 @@ static enum kernel_gp_hint get_kernel_gp_address(struct pt_regs *regs,
> >  		return GP_NO_HINT;
> >  
> >  #ifdef CONFIG_X86_64
> 
> Might as well get rid of the #ifdef in C code, if possible.
> 
> if (!IS_ENABLED(CONFIG_X86_64)
> 	return GP_CANONICAL;
> 
> or combine it with the next check.

I tried this before. It triggers compiler error on 32-bit:

arch/x86/kernel/traps.c:673:16: error: shift count >= width of type [-Werror,-Wshift-count-overflow]
  673 |         if (*addr >= ~__VIRTUAL_MASK)
      |                       ^~~~~~~~~~~~~~

__VIRTUAL_MASK is not usable on 32-bit configs.

-- 
  Kiryl Shutsemau / Kirill A. Shutemov
Re: [PATCHv9 11/16] x86/traps: Communicate a LASS violation in #GP message
Posted by Geert Uytterhoeven 3 months ago
Hi Kirill,

On Wed, 9 Jul 2025 at 11:31, Kirill A. Shutemov
<kirill.shutemov@linux.intel.com> wrote:
> On Tue, Jul 08, 2025 at 07:40:35PM -0700, Sohil Mehta wrote:
> > > @@ -664,14 +673,23 @@ static enum kernel_gp_hint get_kernel_gp_address(struct pt_regs *regs,
> > >             return GP_NO_HINT;
> > >
> > >  #ifdef CONFIG_X86_64
> >
> > Might as well get rid of the #ifdef in C code, if possible.
> >
> > if (!IS_ENABLED(CONFIG_X86_64)
> >       return GP_CANONICAL;
> >
> > or combine it with the next check.
>
> I tried this before. It triggers compiler error on 32-bit:
>
> arch/x86/kernel/traps.c:673:16: error: shift count >= width of type [-Werror,-Wshift-count-overflow]
>   673 |         if (*addr >= ~__VIRTUAL_MASK)
>       |                       ^~~~~~~~~~~~~~
>
> __VIRTUAL_MASK is not usable on 32-bit configs.

arch/x86/include/asm/page_32_types.h:#define __VIRTUAL_MASK_SHIFT       32
arch/x86/include/asm/page_32_types.h:#define __VIRTUAL_MASK_SHIFT       32
arch/x86/include/asm/page_64_types.h:#define __VIRTUAL_MASK_SHIFT
 (pgtable_l5_enabled() ? 56 : 47)
arch/x86/include/asm/page_types.h:#define __VIRTUAL_MASK
 ((1UL << __VIRTUAL_MASK_SHIFT) - 1)

Given __VIRTUAL_MASK_SHIFT is 32 on 32-bit platforms, perhaps
__VIRTUAL_MASK should just be changed to shift 1ULL instead?
Or better, use GENMASK(__VIRTUAL_MASK_SHIFT - 1, 0), so the
resulting type is still unsigned long.

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
Re: [PATCHv9 11/16] x86/traps: Communicate a LASS violation in #GP message
Posted by Kirill A. Shutemov 3 months ago
On Wed, Jul 09, 2025 at 11:36:27AM +0200, Geert Uytterhoeven wrote:
> Hi Kirill,
> 
> On Wed, 9 Jul 2025 at 11:31, Kirill A. Shutemov
> <kirill.shutemov@linux.intel.com> wrote:
> > On Tue, Jul 08, 2025 at 07:40:35PM -0700, Sohil Mehta wrote:
> > > > @@ -664,14 +673,23 @@ static enum kernel_gp_hint get_kernel_gp_address(struct pt_regs *regs,
> > > >             return GP_NO_HINT;
> > > >
> > > >  #ifdef CONFIG_X86_64
> > >
> > > Might as well get rid of the #ifdef in C code, if possible.
> > >
> > > if (!IS_ENABLED(CONFIG_X86_64)
> > >       return GP_CANONICAL;
> > >
> > > or combine it with the next check.
> >
> > I tried this before. It triggers compiler error on 32-bit:
> >
> > arch/x86/kernel/traps.c:673:16: error: shift count >= width of type [-Werror,-Wshift-count-overflow]
> >   673 |         if (*addr >= ~__VIRTUAL_MASK)
> >       |                       ^~~~~~~~~~~~~~
> >
> > __VIRTUAL_MASK is not usable on 32-bit configs.
> 
> arch/x86/include/asm/page_32_types.h:#define __VIRTUAL_MASK_SHIFT       32
> arch/x86/include/asm/page_32_types.h:#define __VIRTUAL_MASK_SHIFT       32
> arch/x86/include/asm/page_64_types.h:#define __VIRTUAL_MASK_SHIFT
>  (pgtable_l5_enabled() ? 56 : 47)
> arch/x86/include/asm/page_types.h:#define __VIRTUAL_MASK
>  ((1UL << __VIRTUAL_MASK_SHIFT) - 1)
> 
> Given __VIRTUAL_MASK_SHIFT is 32 on 32-bit platforms, perhaps
> __VIRTUAL_MASK should just be changed to shift 1ULL instead?
> Or better, use GENMASK(__VIRTUAL_MASK_SHIFT - 1, 0), so the
> resulting type is still unsigned long.

Making __VIRTUAL_MASK unsigned long long is no-go. Virtual address are
unsigned long. I guess GENMASK() would work.

I think re-defining __VIRTUAL_MASK is out-of-scope for the patchset. Feel
free to prepare a separate patch to do it.

-- 
  Kiryl Shutsemau / Kirill A. Shutemov