public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 1/1] x86/fred: Clear WFE in missing-ENDBRANCH #CPs
@ 2024-11-13  9:13 Xin Li (Intel)
  2024-11-13  9:56 ` Ingo Molnar
  2024-11-13 17:49 ` Dave Hansen
  0 siblings, 2 replies; 4+ messages in thread
From: Xin Li (Intel) @ 2024-11-13  9:13 UTC (permalink / raw)
  To: linux-kernel
  Cc: tglx, mingo, bp, dave.hansen, x86, hpa, peterz, andrew.cooper3

An indirect branch instruction sets the CPU indirect branch tracker
(IBT) into WAIT_FOR_ENDBRANCH (WFE) state, and WFE stays asserted
across the instruction boundary.  When decoder finds an instruction
and WFE is set, and the instruction is not the appropriate ENDBR, it
raises a #CP fault.

For the kernel IBT no ENDBR selftest where #CPs are deliberately
triggerred, the WFE state of the interrupted context needs to be
cleared to let execution continue.  Otherwise when the CPU resumes
from the instruction that just caused the previous #CP, another
missing-ENDBRANCH #CP is raised and the CPU enters a dead loop.

This is not a problem with IDT because it doesn't preserve WFE and
IRET doesn't set WFE.  But FRED provides space on the entry stack
(in an expanded CS area) to save and restore the WFE state, thus the
WFE state is no longer clobbered, so software must clear it.

Clear WFE to avoid dead looping in ibt_clear_fred_wfe() and the
!ibt_fatal code path when execution is allowed to continue.

Clobbering WFE in any other circumstance is a security-relevant bug.

Signed-off-by: Xin Li (Intel) <xin@zytor.com>
---

Changes since v2:
* Explain why the FRED-specific code in ibt_clear_fred_wfe() doesn't
  do any FRED checks (Dave Hansen).
* ibt_clear_fred_wfe() needs to be inside the ibt_selftest_noendbr
  path (Andrew Cooper & Dave Hansen).
* Simplify the changelog and comment, create an IBT WFE document
  in Documentation/ later (Dave Hansen).
* Rewrite changelog based on feedbacks from Dave and Andrew.

Changes since v1:
* Rewrite the comment of ibt_clear_fred_wfe() using Andrew Cooper's
  write-up (Andrew Cooper).
* Unconditionally clear WFE in missing-ENDBRANCH #CPs (Peter Zijlstra).
* Don't check X86_FEATURE_FRED in ibt_clear_fred_wfe() (Dave Hansen &
  Andrew Cooper).
---
 arch/x86/kernel/cet.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/arch/x86/kernel/cet.c b/arch/x86/kernel/cet.c
index d2c732a34e5d..f14156b45c90 100644
--- a/arch/x86/kernel/cet.c
+++ b/arch/x86/kernel/cet.c
@@ -81,6 +81,34 @@ static void do_user_cp_fault(struct pt_regs *regs, unsigned long error_code)
 
 static __ro_after_init bool ibt_fatal = true;
 
+/*
+ * By definition, all missing-ENDBRANCH #CPs are a result of WFE && !ENDBR.
+ *
+ * For the kernel IBT no ENDBR selftest where #CPs are deliberately triggerred,
+ * the WFE state of the interrupted context needs to be cleared to let execution
+ * continue.  Otherwise when the CPU resumes from the instruction that just
+ * caused the previous #CP, another missing-ENDBRANCH #CP is raised and the CPU
+ * enters a dead loop.
+ *
+ * This is not a problem with IDT because it doesn't preserve WFE and IRET doesn't
+ * set WFE.  But FRED provides space on the entry stack (in an expanded CS area)
+ * to save and restore the WFE state, thus the WFE state is no longer clobbered,
+ * so software must clear it.
+ */
+static void ibt_clear_fred_wfe(struct pt_regs *regs)
+{
+	/*
+	 * No need to do any FRED checks.
+	 *
+	 * For IDT event delivery, the high-order 48 bits of CS are pushed
+	 * as 0s into stack, and later IRET ignores these bits.
+	 *
+	 * For FRED, a test to check if fred_cs.wfe is set would be dropped
+	 * by compilers.
+	 */
+	regs->fred_cs.wfe = 0;
+}
+
 static void do_kernel_cp_fault(struct pt_regs *regs, unsigned long error_code)
 {
 	if ((error_code & CP_EC) != CP_ENDBR) {
@@ -90,6 +118,7 @@ static void do_kernel_cp_fault(struct pt_regs *regs, unsigned long error_code)
 
 	if (unlikely(regs->ip == (unsigned long)&ibt_selftest_noendbr)) {
 		regs->ax = 0;
+		ibt_clear_fred_wfe(regs);
 		return;
 	}
 
@@ -97,6 +126,7 @@ static void do_kernel_cp_fault(struct pt_regs *regs, unsigned long error_code)
 	if (!ibt_fatal) {
 		printk(KERN_DEFAULT CUT_HERE);
 		__warn(__FILE__, __LINE__, (void *)regs->ip, TAINT_WARN, regs, NULL);
+		ibt_clear_fred_wfe(regs);
 		return;
 	}
 	BUG();

base-commit: be42adbe196a218ef345e2ffa0f8b70492df7dd1
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v3 1/1] x86/fred: Clear WFE in missing-ENDBRANCH #CPs
  2024-11-13  9:13 [PATCH v3 1/1] x86/fred: Clear WFE in missing-ENDBRANCH #CPs Xin Li (Intel)
@ 2024-11-13  9:56 ` Ingo Molnar
  2024-11-13 17:42   ` Xin Li
  2024-11-13 17:49 ` Dave Hansen
  1 sibling, 1 reply; 4+ messages in thread
From: Ingo Molnar @ 2024-11-13  9:56 UTC (permalink / raw)
  To: Xin Li (Intel)
  Cc: linux-kernel, tglx, mingo, bp, dave.hansen, x86, hpa, peterz,
	andrew.cooper3


* Xin Li (Intel) <xin@zytor.com> wrote:

> +/*
> + * By definition, all missing-ENDBRANCH #CPs are a result of WFE && !ENDBR.
> + *
> + * For the kernel IBT no ENDBR selftest where #CPs are deliberately triggerred,

s/triggerred
 /triggered

> +static void ibt_clear_fred_wfe(struct pt_regs *regs)
> +{
> +	/*
> +	 * No need to do any FRED checks.
> +	 *
> +	 * For IDT event delivery, the high-order 48 bits of CS are pushed
> +	 * as 0s into stack, and later IRET ignores these bits.

s/into stack
 /into the stack

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v3 1/1] x86/fred: Clear WFE in missing-ENDBRANCH #CPs
  2024-11-13  9:56 ` Ingo Molnar
@ 2024-11-13 17:42   ` Xin Li
  0 siblings, 0 replies; 4+ messages in thread
From: Xin Li @ 2024-11-13 17:42 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: linux-kernel, tglx, mingo, bp, dave.hansen, x86, hpa, peterz,
	andrew.cooper3

On 11/13/2024 1:56 AM, Ingo Molnar wrote:
> 
> * Xin Li (Intel) <xin@zytor.com> wrote:
> 
>> +/*
>> + * By definition, all missing-ENDBRANCH #CPs are a result of WFE && !ENDBR.
>> + *
>> + * For the kernel IBT no ENDBR selftest where #CPs are deliberately triggerred,
> 
> s/triggerred
>   /triggered
> 
>> +static void ibt_clear_fred_wfe(struct pt_regs *regs)
>> +{
>> +	/*
>> +	 * No need to do any FRED checks.
>> +	 *
>> +	 * For IDT event delivery, the high-order 48 bits of CS are pushed
>> +	 * as 0s into stack, and later IRET ignores these bits.
> 
> s/into stack
>   /into the stack
> 

Thanks, fixing and sending v4 now.
     Xin

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v3 1/1] x86/fred: Clear WFE in missing-ENDBRANCH #CPs
  2024-11-13  9:13 [PATCH v3 1/1] x86/fred: Clear WFE in missing-ENDBRANCH #CPs Xin Li (Intel)
  2024-11-13  9:56 ` Ingo Molnar
@ 2024-11-13 17:49 ` Dave Hansen
  1 sibling, 0 replies; 4+ messages in thread
From: Dave Hansen @ 2024-11-13 17:49 UTC (permalink / raw)
  To: Xin Li (Intel), linux-kernel
  Cc: tglx, mingo, bp, dave.hansen, x86, hpa, peterz, andrew.cooper3

On 11/13/24 01:13, Xin Li (Intel) wrote:
> An indirect branch instruction sets the CPU indirect branch tracker
> (IBT) into WAIT_FOR_ENDBRANCH (WFE) state, and WFE stays asserted
> across the instruction boundary.  When decoder finds an instruction
> and WFE is set, and the instruction is not the appropriate ENDBR, it
> raises a #CP fault.
> 
> For the kernel IBT no ENDBR selftest where #CPs are deliberately
> triggerred, the WFE state of the interrupted context needs to be
> cleared to let execution continue.  Otherwise when the CPU resumes
> from the instruction that just caused the previous #CP, another
> missing-ENDBRANCH #CP is raised and the CPU enters a dead loop.
> 
> This is not a problem with IDT because it doesn't preserve WFE and
> IRET doesn't set WFE.  But FRED provides space on the entry stack
> (in an expanded CS area) to save and restore the WFE state, thus the
> WFE state is no longer clobbered, so software must clear it.
> 
> Clear WFE to avoid dead looping in ibt_clear_fred_wfe() and the
> !ibt_fatal code path when execution is allowed to continue.
> 
> Clobbering WFE in any other circumstance is a security-relevant bug.

With the minor fixes Ingo mentioned:

Acked-by: Dave Hansen <dave.hansen@linux.intel.com>


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2024-11-13 17:49 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-13  9:13 [PATCH v3 1/1] x86/fred: Clear WFE in missing-ENDBRANCH #CPs Xin Li (Intel)
2024-11-13  9:56 ` Ingo Molnar
2024-11-13 17:42   ` Xin Li
2024-11-13 17:49 ` Dave Hansen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox