[PATCH v12 6/8] x86/traps: Communicate a LASS violation in #GP message

Sohil Mehta posted 8 patches 2 months, 3 weeks ago
There is a newer version of this series
[PATCH v12 6/8] x86/traps: Communicate a LASS violation in #GP message
Posted by Sohil Mehta 2 months, 3 weeks ago
From: Alexander Shishkin <alexander.shishkin@linux.intel.com>

A LASS violation typically results in a #GP. With LASS active, any
invalid access to user memory (including the first page frame) would be
reported as a #GP, instead of a #PF.

Unfortunately, the #GP error messages provide limited information about
the cause of the fault. This could be confusing for kernel developers
and users who are accustomed to the friendly #PF messages.

To make the transition easier, enhance the #GP Oops message to include a
hint about LASS violations. Also, add a special hint for kernel NULL
pointer dereferences to match with the existing #PF message.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Sohil Mehta <sohil.mehta@intel.com>
Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
---
v12:
 - Pick up review tag.

v11:
 - Improve commit log.
---
 arch/x86/kernel/traps.c | 45 ++++++++++++++++++++++++++++++-----------
 1 file changed, 33 insertions(+), 12 deletions(-)

diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 6b22611e69cc..30d5c690f9a1 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -635,13 +635,23 @@ 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]	= "probably LASS violation for address",
+	[GP_NULL_POINTER]	= "kernel NULL pointer dereference",
 };
 
 /*
  * When an uncaught #GP occurs, try to determine the memory address accessed by
  * the instruction and return that address to the caller. Also, try to figure
- * out whether any part of the access to that address was non-canonical.
+ * out whether any part of the access to that address was non-canonical or
+ * across privilege levels.
  */
 static enum kernel_gp_hint get_kernel_gp_address(struct pt_regs *regs,
 						 unsigned long *addr)
@@ -663,14 +673,27 @@ 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 active, a NULL pointer dereference generates a #GP
+	 * instead of a #PF.
+	 */
+	if (*addr < PAGE_SIZE)
+		return GP_NULL_POINTER;
+
+	/*
+	 * Assume that LASS caused the exception, because the address is
+	 * canonical and in the user half.
+	 */
+	if (cpu_feature_enabled(X86_FEATURE_LASS))
+		return GP_LASS_VIOLATION;
 #endif
 
 	return GP_CANONICAL;
@@ -833,9 +856,7 @@ DEFINE_IDTENTRY_ERRORCODE(exc_general_protection)
 
 	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.43.0
Re: [PATCH v12 6/8] x86/traps: Communicate a LASS violation in #GP message
Posted by Borislav Petkov 2 months, 3 weeks ago
On Thu, Nov 13, 2025 at 02:42:02PM -0800, Sohil Mehta wrote:
> +	/*
> +	 * If LASS is active, a NULL pointer dereference generates a #GP
> +	 * instead of a #PF.
> +	 */

I'm having trouble matching the comment...

> +	if (*addr < PAGE_SIZE)

... to the conditional.

Aaaha:

"With LASS active, any invalid access to user memory (including the first page
frame) would be..."

Why is the first page frame special to justify a special check?

I must be missing something obvious here...

Thx.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
Re: [PATCH v12 6/8] x86/traps: Communicate a LASS violation in #GP message
Posted by Sohil Mehta 2 months, 3 weeks ago
On 11/17/2025 6:48 AM, Borislav Petkov wrote:

> Why is the first page frame special to justify a special check?
> 

Any dereference to a very low address (the first page frame) is
typically considered as a "NULL" pointer dereference.

$ git log | grep "BUG: kernel NULL pointer dereference"

    BUG: kernel NULL pointer dereference, address: 0000000000000000
    BUG: kernel NULL pointer dereference, address: 0000000000000010
    BUG: kernel NULL pointer dereference, address: 0000000000000008
    BUG: kernel NULL pointer dereference, address: 0000000000000008
    BUG: kernel NULL pointer dereference, address: 0000000000000040
    BUG: kernel NULL pointer dereference, address: 0000000000000264
    BUG: kernel NULL pointer dereference, address: 0000000000000000
    BUG: kernel NULL pointer dereference, address: 0000000000000098
    BUG: kernel NULL pointer dereference, address: 0000000000000000
    BUG: kernel NULL pointer dereference, address: 00000000000003c0

The page fault error messages have similar logic:

if (address < PAGE_SIZE && !user_mode(regs))
	pr_alert("BUG: kernel NULL pointer dereference, address: %px\n",
		(void *)address);

I believe the check is to account for arithmetic or other operations
that may have happened on the "NULL" pointer before it is dereferenced.
Re: [PATCH v12 6/8] x86/traps: Communicate a LASS violation in #GP message
Posted by Borislav Petkov 2 months, 3 weeks ago
On Mon, Nov 17, 2025 at 09:24:20AM -0800, Sohil Mehta wrote:
> The page fault error messages have similar logic:
> 
> if (address < PAGE_SIZE && !user_mode(regs))
> 	pr_alert("BUG: kernel NULL pointer dereference, address: %px\n",
> 		(void *)address);
> 
> I believe the check is to account for arithmetic or other operations
> that may have happened on the "NULL" pointer before it is dereferenced.

Ah ok, that makes sense. That comment still reads weird:

+       /*
+        * If LASS is active, a NULL pointer dereference generates a #GP
+        * instead of a #PF.
+        */
+       if (*addr < PAGE_SIZE)

It says "if LASS is active" but the check if
(cpu_feature_enabled(X86_FEATURE_LASS)) is below it.

I guess you want to have

	if (cpu_feature_enabled(X86_FEATURE_LASS)) {
		if (*addr < PAGE_SIZE)
			return GP_NULL_POINTER;
		else
			return GP_LASS_VIOLATION;
	}

so that it is perfectly clear.

Then the catch-all GP_CANONICAL will take care of the absurd cases, if we ever
hit them.

Thx.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
Re: [PATCH v12 6/8] x86/traps: Communicate a LASS violation in #GP message
Posted by Sohil Mehta 2 months, 3 weeks ago
On 11/17/2025 10:29 AM, Borislav Petkov wrote:
> 
> Ah ok, that makes sense. That comment still reads weird:
> 

I see. The idea with the comment was to clarify why the check is outside
cpu_feature_enabled(X86_FEATURE_LASS). That clearly failed! :)

 > I guess you want to have
> 
> 	if (cpu_feature_enabled(X86_FEATURE_LASS)) {
> 		if (*addr < PAGE_SIZE)
> 			return GP_NULL_POINTER;
> 		else
> 			return GP_LASS_VIOLATION;
> 	}
> 
> so that it is perfectly clear.
> 

You are right. The condition would typically hit only with LASS enabled.
But since we are adding an extra hint for NULL pointers, I figured it
would be helpful even without LASS (even though it is unlikely to happen).

Therefore, the printed message isn't LASS specific either:
Oops: general protection fault, kernel NULL pointer dereference 0x0:

Also, it makes the code a tiny bit prettier (aesthetically, without the
nested if). We now have 4 hints and a check for each:

if (condition1)
	return HINT_1;
...

if (condition4)
	return HINT_4;


Would this update to the comment help clarify?

	/*
	 * A NULL pointer dereference usually causes a #PF. However, it
	 * can result in a #GP when LASS is active. Provide the same
	 * hint in the rare case that the condition is hit without LASS.
	 */
	if (*addr < PAGE_SIZE)
		return GP_NULL_POINTER;

	/*
	 * Assume that LASS caused the exception, because the address is
	 * canonical and in the user half.
	 */
	if (cpu_feature_enabled(X86_FEATURE_LASS))
		return GP_LASS_VIOLATION;


Though, I won't push for it further. Code clarity is more important than
reducing indentation.
Re: [PATCH v12 6/8] x86/traps: Communicate a LASS violation in #GP message
Posted by Borislav Petkov 2 months, 3 weeks ago
On Mon, Nov 17, 2025 at 11:45:38AM -0800, Sohil Mehta wrote:
> Would this update to the comment help clarify?

Yap, that LGTM.

> 	/*
> 	 * A NULL pointer dereference usually causes a #PF. However, it
> 	 * can result in a #GP when LASS is active. Provide the same
> 	 * hint in the rare case that the condition is hit without LASS.
> 	 */
> 	if (*addr < PAGE_SIZE)
> 		return GP_NULL_POINTER;
> 
> 	/*
> 	 * Assume that LASS caused the exception, because the address is
> 	 * canonical and in the user half.
> 	 */
> 	if (cpu_feature_enabled(X86_FEATURE_LASS))
> 		return GP_LASS_VIOLATION;

Thx.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette