diff mbox series

[v2,07/39] x86/cet: Add user control-protection fault handler

Message ID 20220929222936.14584-8-rick.p.edgecombe@intel.com (mailing list archive)
State New
Headers show
Series Shadowstacks for userspace | expand

Commit Message

Edgecombe, Rick P Sept. 29, 2022, 10:29 p.m. UTC
From: Yu-cheng Yu <yu-cheng.yu@intel.com>

A control-protection fault is triggered when a control-flow transfer
attempt violates Shadow Stack or Indirect Branch Tracking constraints.
For example, the return address for a RET instruction differs from the copy
on the shadow stack.

There already exists a control-protection fault handler for handling kernel
IBT. Refactor this fault handler into sparate user and kernel handlers,
like the page fault handler. Add a control-protection handler for usermode.

The control-protection fault handler works in a similar way as the general
protection fault handler. It provides the si_code SEGV_CPERR to the signal
handler.

Signed-off-by: Yu-cheng Yu <yu-cheng.yu@intel.com>
Co-developed-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>

---

v2:
 - Integrate with kernel IBT fault handler
 - Update printed messages. (Dave)
 - Remove array_index_nospec() usage. (Dave)
 - Remove IBT messages. (Dave)
 - Add enclave error code bit processing it case it can get triggered
   somehow.
 - Add extra "unknown" in control_protection_err.

v1:
 - Update static asserts for NSIGSEGV

Yu-cheng v29:
 - Remove pr_emerg() since it is followed by die().
 - Change boot_cpu_has() to cpu_feature_enabled().

Yu-cheng v25:
 - Change CONFIG_X86_CET to CONFIG_X86_SHADOW_STACK.
 - Change X86_FEATURE_CET to X86_FEATURE_SHSTK.

 arch/arm/kernel/signal.c           |  2 +-
 arch/arm64/kernel/signal.c         |  2 +-
 arch/arm64/kernel/signal32.c       |  2 +-
 arch/sparc/kernel/signal32.c       |  2 +-
 arch/sparc/kernel/signal_64.c      |  2 +-
 arch/x86/include/asm/idtentry.h    |  2 +-
 arch/x86/kernel/idt.c              |  2 +-
 arch/x86/kernel/signal_compat.c    |  2 +-
 arch/x86/kernel/traps.c            | 98 ++++++++++++++++++++++++++----
 arch/x86/xen/enlighten_pv.c        |  2 +-
 arch/x86/xen/xen-asm.S             |  2 +-
 include/uapi/asm-generic/siginfo.h |  3 +-
 12 files changed, 97 insertions(+), 24 deletions(-)

Comments

kirill.shutemov@linux.intel.com Oct. 3, 2022, 2:01 p.m. UTC | #1
On Thu, Sep 29, 2022 at 03:29:04PM -0700, Rick Edgecombe wrote:
> +#else
> +static void do_user_control_protection_fault(struct pt_regs *regs,
> +					     unsigned long error_code)
> +{
> +	WARN_ONCE(1, "User-mode control protection fault with shadow support disabled\n");

Why is this a warning, but runtime check for !X86_FEATURE_IBT and
!X86_FEATURE_SHSTK below is fatal?

> +}
> +#endif
> +
> +#ifdef CONFIG_X86_KERNEL_IBT
> +
> +static __ro_after_init bool ibt_fatal = true;
> +
> +extern void ibt_selftest_ip(void); /* code label defined in asm below */
>  
> +static void do_kernel_control_protection_fault(struct pt_regs *regs)
> +{
>  	if (unlikely(regs->ip == (unsigned long)&ibt_selftest_ip)) {
>  		regs->ax = 0;
>  		return;
> @@ -283,9 +335,29 @@ static int __init ibt_setup(char *str)
>  }
>  
>  __setup("ibt=", ibt_setup);
> -
> +#else
> +static void do_kernel_control_protection_fault(struct pt_regs *regs)
> +{
> +	WARN_ONCE(1, "Kernel-mode control protection fault with IBT disabled\n");

Ditto.

> +}
>  #endif /* CONFIG_X86_KERNEL_IBT */
>  
> +#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)
> +DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
> +{
> +	if (!cpu_feature_enabled(X86_FEATURE_IBT) &&
> +	    !cpu_feature_enabled(X86_FEATURE_SHSTK)) {
> +		pr_err("Unexpected #CP\n");
> +		BUG();
> +	}
> +
> +	if (user_mode(regs))
> +		do_user_control_protection_fault(regs, error_code);
> +	else
> +		do_kernel_control_protection_fault(regs);
> +}
> +#endif /* defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK) */
> +
>  #ifdef CONFIG_X86_F00F_BUG
>  void handle_invalid_op(struct pt_regs *regs)
>  #else
Kees Cook Oct. 3, 2022, 6:04 p.m. UTC | #2
On Thu, Sep 29, 2022 at 03:29:04PM -0700, Rick Edgecombe wrote:
> [...]
> -#ifdef CONFIG_X86_KERNEL_IBT
> +#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)

This pattern is repeated several times. Perhaps there needs to be a
CONFIG_X86_CET to make this more readable? Really just a style question.

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index b68eb75887b8..6cb52616e0cf 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1836,6 +1836,11 @@ config CC_HAS_IBT
 		  (CC_IS_CLANG && CLANG_VERSION >= 140000)) && \
 		  $(as-instr,endbr64)
 
+config X86_CET
+	def_bool n
+	help
+	  CET features are enabled (IBT and/or Shadow Stack)
+
 config X86_KERNEL_IBT
 	prompt "Indirect Branch Tracking"
 	bool
@@ -1843,6 +1848,7 @@ config X86_KERNEL_IBT
 	# https://github.com/llvm/llvm-project/commit/9d7001eba9c4cb311e03cd8cdc231f9e579f2d0f
 	depends on !LD_IS_LLD || LLD_VERSION >= 140000
 	select OBJTOOL
+	select X86_CET
 	help
 	  Build the kernel with support for Indirect Branch Tracking, a
 	  hardware support course-grain forward-edge Control Flow Integrity
@@ -1945,6 +1951,7 @@ config X86_SHADOW_STACK
 	def_bool n
 	depends on ARCH_HAS_SHADOW_STACK
 	select ARCH_USES_HIGH_VMA_FLAGS
+	select X86_CET
 	help
 	  Shadow Stack protection is a hardware feature that detects function
 	  return address corruption. Today the kernel's support is limited to

> [...]
> +#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)
> +DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
> +{
> +	if (!cpu_feature_enabled(X86_FEATURE_IBT) &&
> +	    !cpu_feature_enabled(X86_FEATURE_SHSTK)) {
> +		pr_err("Unexpected #CP\n");
> +		BUG();
> +	}

I second Kirill's question here. This seems an entirely survivable
(but highly unexpected) state. I think this whole "if" could just be
replaced with:

	WARN_ON_ONCE(!cpu_feature_enabled(X86_FEATURE_IBT) &&
		     !cpu_feature_enabled(X86_FEATURE_SHSTK),
		     "Unexpected #CP\n");

Otherwise this looks good to me.

Reviewed-by: Kees Cook <keescook@chromium.org>
Edgecombe, Rick P Oct. 3, 2022, 6:12 p.m. UTC | #3
On Mon, 2022-10-03 at 17:01 +0300, Kirill A . Shutemov wrote:
> On Thu, Sep 29, 2022 at 03:29:04PM -0700, Rick Edgecombe wrote:
> > +#else
> > +static void do_user_control_protection_fault(struct pt_regs *regs,
> > +                                          unsigned long
> > error_code)
> > +{
> > +     WARN_ONCE(1, "User-mode control protection fault with shadow
> > support disabled\n");
> 
> Why is this a warning, but runtime check for !X86_FEATURE_IBT and
> !X86_FEATURE_SHSTK below is fatal?

It was a BUG() in the original KERNEL_IBT focused handler IIRC. There
seems to be some renewed effort to stop doing those:

https://lore.kernel.org/all/20220923113426.52871-2-david@redhat.com/T/#u

...so I'll change it to a WARN for this. In the kernel specific portion
of the handler, it also does a BUG on endbranch violation. I'll leave
that one for this change.
Edgecombe, Rick P Oct. 3, 2022, 8:33 p.m. UTC | #4
On Mon, 2022-10-03 at 11:04 -0700, Kees Cook wrote:
> On Thu, Sep 29, 2022 at 03:29:04PM -0700, Rick Edgecombe wrote:
> > [...]
> > -#ifdef CONFIG_X86_KERNEL_IBT
> > +#if defined(CONFIG_X86_KERNEL_IBT) ||
> > defined(CONFIG_X86_SHADOW_STACK)
> 
> This pattern is repeated several times. Perhaps there needs to be a
> CONFIG_X86_CET to make this more readable? Really just a style
> question.

Hmm, good idea. Thanks.
Andy Lutomirski Oct. 3, 2022, 10:51 p.m. UTC | #5
On 9/29/22 15:29, Rick Edgecombe wrote:
> From: Yu-cheng Yu <yu-cheng.yu@intel.com>
> 

> +static void do_user_control_protection_fault(struct pt_regs *regs,
> +					     unsigned long error_code)
>   {
> -	if (!cpu_feature_enabled(X86_FEATURE_IBT)) {
> -		pr_err("Unexpected #CP\n");
> -		BUG();
> +	struct task_struct *tsk;
> +	unsigned long ssp;
> +
> +	/* Read SSP before enabling interrupts. */
> +	rdmsrl(MSR_IA32_PL3_SSP, ssp); > +
> +	cond_local_irq_enable(regs);

I feel like I'm missing something.  Either PL3_SSL is context switched 
correctly and reading it with IRQs off is useless, or it's not context 
switched, and I'm very confused.

Please either improve the comment or move it after the 
cond_local_irq_enable().

--Andy

> +
> +	if (!cpu_feature_enabled(X86_FEATURE_SHSTK))
> +		WARN_ONCE(1, "User-mode control protection fault with shadow support disabled\n");
> +
> +	tsk = current;
> +	tsk->thread.error_code = error_code;
> +	tsk->thread.trap_nr = X86_TRAP_CP;
> +
> +	/* Ratelimit to prevent log spamming. */
> +	if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
> +	    __ratelimit(&cpf_rate)) {
> +		unsigned int cpec;
> +
> +		cpec = error_code & CP_EC;
> +		if (cpec >= ARRAY_SIZE(control_protection_err))
> +			cpec = 0;
> +
> +		pr_emerg("%s[%d] control protection ip:%lx sp:%lx ssp:%lx error:%lx(%s)%s",
> +			 tsk->comm, task_pid_nr(tsk),
> +			 regs->ip, regs->sp, ssp, error_code,
> +			 control_protection_err[cpec],
> +			 error_code & CP_ENCL ? " in enclave" : "");
> +		print_vma_addr(KERN_CONT " in ", regs->ip);
> +		pr_cont("\n");
>   	}
>   
> -	if (WARN_ON_ONCE(user_mode(regs) || (error_code & CP_EC) != CP_ENDBR))
> -		return;
> +	force_sig_fault(SIGSEGV, SEGV_CPERR, (void __user *)0);
> +	cond_local_irq_disable(regs);
> +}
> +#else
> +static void do_user_control_protection_fault(struct pt_regs *regs,
> +					     unsigned long error_code)
> +{
> +	WARN_ONCE(1, "User-mode control protection fault with shadow support disabled\n");
> +}
> +#endif
> +
> +#ifdef CONFIG_X86_KERNEL_IBT
> +
> +static __ro_after_init bool ibt_fatal = true;
> +
> +extern void ibt_selftest_ip(void); /* code label defined in asm below */
>   
> +static void do_kernel_control_protection_fault(struct pt_regs *regs)
> +{
>   	if (unlikely(regs->ip == (unsigned long)&ibt_selftest_ip)) {
>   		regs->ax = 0;
>   		return;
> @@ -283,9 +335,29 @@ static int __init ibt_setup(char *str)
>   }
>   
>   __setup("ibt=", ibt_setup);
> -
> +#else
> +static void do_kernel_control_protection_fault(struct pt_regs *regs)
> +{
> +	WARN_ONCE(1, "Kernel-mode control protection fault with IBT disabled\n");
> +}
>   #endif /* CONFIG_X86_KERNEL_IBT */
>   
> +#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)
> +DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
> +{
> +	if (!cpu_feature_enabled(X86_FEATURE_IBT) &&
> +	    !cpu_feature_enabled(X86_FEATURE_SHSTK)) {
> +		pr_err("Unexpected #CP\n");
> +		BUG();
> +	}
> +
> +	if (user_mode(regs))
> +		do_user_control_protection_fault(regs, error_code);
> +	else
> +		do_kernel_control_protection_fault(regs);
> +}
> +#endif /* defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK) */
> +
>   #ifdef CONFIG_X86_F00F_BUG
>   void handle_invalid_op(struct pt_regs *regs)
>   #else
> diff --git a/arch/x86/xen/enlighten_pv.c b/arch/x86/xen/enlighten_pv.c
> index 0ed2e487a693..57faa287163f 100644
> --- a/arch/x86/xen/enlighten_pv.c
> +++ b/arch/x86/xen/enlighten_pv.c
> @@ -628,7 +628,7 @@ static struct trap_array_entry trap_array[] = {
>   	TRAP_ENTRY(exc_coprocessor_error,		false ),
>   	TRAP_ENTRY(exc_alignment_check,			false ),
>   	TRAP_ENTRY(exc_simd_coprocessor_error,		false ),
> -#ifdef CONFIG_X86_KERNEL_IBT
> +#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)
>   	TRAP_ENTRY(exc_control_protection,		false ),
>   #endif
>   };
> diff --git a/arch/x86/xen/xen-asm.S b/arch/x86/xen/xen-asm.S
> index 6b4fdf6b9542..e45ff6300c7d 100644
> --- a/arch/x86/xen/xen-asm.S
> +++ b/arch/x86/xen/xen-asm.S
> @@ -148,7 +148,7 @@ xen_pv_trap asm_exc_page_fault
>   xen_pv_trap asm_exc_spurious_interrupt_bug
>   xen_pv_trap asm_exc_coprocessor_error
>   xen_pv_trap asm_exc_alignment_check
> -#ifdef CONFIG_X86_KERNEL_IBT
> +#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)
>   xen_pv_trap asm_exc_control_protection
>   #endif
>   #ifdef CONFIG_X86_MCE
> diff --git a/include/uapi/asm-generic/siginfo.h b/include/uapi/asm-generic/siginfo.h
> index ffbe4cec9f32..0f52d0ac47c5 100644
> --- a/include/uapi/asm-generic/siginfo.h
> +++ b/include/uapi/asm-generic/siginfo.h
> @@ -242,7 +242,8 @@ typedef struct siginfo {
>   #define SEGV_ADIPERR	7	/* Precise MCD exception */
>   #define SEGV_MTEAERR	8	/* Asynchronous ARM MTE error */
>   #define SEGV_MTESERR	9	/* Synchronous ARM MTE exception */
> -#define NSIGSEGV	9
> +#define SEGV_CPERR	10	/* Control protection fault */
> +#define NSIGSEGV	10
>   
>   /*
>    * SIGBUS si_codes
H. Peter Anvin Oct. 3, 2022, 11:09 p.m. UTC | #6
On October 3, 2022 3:51:59 PM PDT, Andy Lutomirski <luto@kernel.org> wrote:
>On 9/29/22 15:29, Rick Edgecombe wrote:
>> From: Yu-cheng Yu <yu-cheng.yu@intel.com>
>> 
>
>> +static void do_user_control_protection_fault(struct pt_regs *regs,
>> +					     unsigned long error_code)
>>   {
>> -	if (!cpu_feature_enabled(X86_FEATURE_IBT)) {
>> -		pr_err("Unexpected #CP\n");
>> -		BUG();
>> +	struct task_struct *tsk;
>> +	unsigned long ssp;
>> +
>> +	/* Read SSP before enabling interrupts. */
>> +	rdmsrl(MSR_IA32_PL3_SSP, ssp); > +
>> +	cond_local_irq_enable(regs);
>
>I feel like I'm missing something.  Either PL3_SSL is context switched correctly and reading it with IRQs off is useless, or it's not context switched, and I'm very confused.
>
>Please either improve the comment or move it after the cond_local_irq_enable().
>
>--Andy
>
>> +
>> +	if (!cpu_feature_enabled(X86_FEATURE_SHSTK))
>> +		WARN_ONCE(1, "User-mode control protection fault with shadow support disabled\n");
>> +
>> +	tsk = current;
>> +	tsk->thread.error_code = error_code;
>> +	tsk->thread.trap_nr = X86_TRAP_CP;
>> +
>> +	/* Ratelimit to prevent log spamming. */
>> +	if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
>> +	    __ratelimit(&cpf_rate)) {
>> +		unsigned int cpec;
>> +
>> +		cpec = error_code & CP_EC;
>> +		if (cpec >= ARRAY_SIZE(control_protection_err))
>> +			cpec = 0;
>> +
>> +		pr_emerg("%s[%d] control protection ip:%lx sp:%lx ssp:%lx error:%lx(%s)%s",
>> +			 tsk->comm, task_pid_nr(tsk),
>> +			 regs->ip, regs->sp, ssp, error_code,
>> +			 control_protection_err[cpec],
>> +			 error_code & CP_ENCL ? " in enclave" : "");
>> +		print_vma_addr(KERN_CONT " in ", regs->ip);
>> +		pr_cont("\n");
>>   	}
>>   -	if (WARN_ON_ONCE(user_mode(regs) || (error_code & CP_EC) != CP_ENDBR))
>> -		return;
>> +	force_sig_fault(SIGSEGV, SEGV_CPERR, (void __user *)0);
>> +	cond_local_irq_disable(regs);
>> +}
>> +#else
>> +static void do_user_control_protection_fault(struct pt_regs *regs,
>> +					     unsigned long error_code)
>> +{
>> +	WARN_ONCE(1, "User-mode control protection fault with shadow support disabled\n");
>> +}
>> +#endif
>> +
>> +#ifdef CONFIG_X86_KERNEL_IBT
>> +
>> +static __ro_after_init bool ibt_fatal = true;
>> +
>> +extern void ibt_selftest_ip(void); /* code label defined in asm below */
>>   +static void do_kernel_control_protection_fault(struct pt_regs *regs)
>> +{
>>   	if (unlikely(regs->ip == (unsigned long)&ibt_selftest_ip)) {
>>   		regs->ax = 0;
>>   		return;
>> @@ -283,9 +335,29 @@ static int __init ibt_setup(char *str)
>>   }
>>     __setup("ibt=", ibt_setup);
>> -
>> +#else
>> +static void do_kernel_control_protection_fault(struct pt_regs *regs)
>> +{
>> +	WARN_ONCE(1, "Kernel-mode control protection fault with IBT disabled\n");
>> +}
>>   #endif /* CONFIG_X86_KERNEL_IBT */
>>   +#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)
>> +DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
>> +{
>> +	if (!cpu_feature_enabled(X86_FEATURE_IBT) &&
>> +	    !cpu_feature_enabled(X86_FEATURE_SHSTK)) {
>> +		pr_err("Unexpected #CP\n");
>> +		BUG();
>> +	}
>> +
>> +	if (user_mode(regs))
>> +		do_user_control_protection_fault(regs, error_code);
>> +	else
>> +		do_kernel_control_protection_fault(regs);
>> +}
>> +#endif /* defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK) */
>> +
>>   #ifdef CONFIG_X86_F00F_BUG
>>   void handle_invalid_op(struct pt_regs *regs)
>>   #else
>> diff --git a/arch/x86/xen/enlighten_pv.c b/arch/x86/xen/enlighten_pv.c
>> index 0ed2e487a693..57faa287163f 100644
>> --- a/arch/x86/xen/enlighten_pv.c
>> +++ b/arch/x86/xen/enlighten_pv.c
>> @@ -628,7 +628,7 @@ static struct trap_array_entry trap_array[] = {
>>   	TRAP_ENTRY(exc_coprocessor_error,		false ),
>>   	TRAP_ENTRY(exc_alignment_check,			false ),
>>   	TRAP_ENTRY(exc_simd_coprocessor_error,		false ),
>> -#ifdef CONFIG_X86_KERNEL_IBT
>> +#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)
>>   	TRAP_ENTRY(exc_control_protection,		false ),
>>   #endif
>>   };
>> diff --git a/arch/x86/xen/xen-asm.S b/arch/x86/xen/xen-asm.S
>> index 6b4fdf6b9542..e45ff6300c7d 100644
>> --- a/arch/x86/xen/xen-asm.S
>> +++ b/arch/x86/xen/xen-asm.S
>> @@ -148,7 +148,7 @@ xen_pv_trap asm_exc_page_fault
>>   xen_pv_trap asm_exc_spurious_interrupt_bug
>>   xen_pv_trap asm_exc_coprocessor_error
>>   xen_pv_trap asm_exc_alignment_check
>> -#ifdef CONFIG_X86_KERNEL_IBT
>> +#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)
>>   xen_pv_trap asm_exc_control_protection
>>   #endif
>>   #ifdef CONFIG_X86_MCE
>> diff --git a/include/uapi/asm-generic/siginfo.h b/include/uapi/asm-generic/siginfo.h
>> index ffbe4cec9f32..0f52d0ac47c5 100644
>> --- a/include/uapi/asm-generic/siginfo.h
>> +++ b/include/uapi/asm-generic/siginfo.h
>> @@ -242,7 +242,8 @@ typedef struct siginfo {
>>   #define SEGV_ADIPERR	7	/* Precise MCD exception */
>>   #define SEGV_MTEAERR	8	/* Asynchronous ARM MTE error */
>>   #define SEGV_MTESERR	9	/* Synchronous ARM MTE exception */
>> -#define NSIGSEGV	9
>> +#define SEGV_CPERR	10	/* Control protection fault */
>> +#define NSIGSEGV	10
>>     /*
>>    * SIGBUS si_codes
>

Could something change the value under a switched-out thread, though?
Edgecombe, Rick P Oct. 3, 2022, 11:11 p.m. UTC | #7
On Mon, 2022-10-03 at 15:51 -0700, Andy Lutomirski wrote:
> On 9/29/22 15:29, Rick Edgecombe wrote:
> > From: Yu-cheng Yu <yu-cheng.yu@intel.com>
> > 
> > +static void do_user_control_protection_fault(struct pt_regs *regs,
> > +                                          unsigned long
> > error_code)
> >    {
> > -     if (!cpu_feature_enabled(X86_FEATURE_IBT)) {
> > -             pr_err("Unexpected #CP\n");
> > -             BUG();
> > +     struct task_struct *tsk;
> > +     unsigned long ssp;
> > +
> > +     /* Read SSP before enabling interrupts. */
> > +     rdmsrl(MSR_IA32_PL3_SSP, ssp); > +
> > +     cond_local_irq_enable(regs);
> 
> I feel like I'm missing something.  Either PL3_SSL is context
> switched 
> correctly and reading it with IRQs off is useless, or it's not
> context 
> switched, and I'm very confused.
> 
> Please either improve the comment or move it after the 
> cond_local_irq_enable().

The thinking was, we were just in userspace and we took a #CP. Since we
were in userspace, we had a live SSP. After we re-enable interrupts we
could get scheduled and it would be in the xsave buffer. So we can grab
it for free now, otherwise we would have to force restore it and read
it after we re-enable interrupts.

I can clarify the comments, unless there is something wrong with that
reasoning.
Andrew Cooper Oct. 5, 2022, 1:20 a.m. UTC | #8
On 29/09/2022 23:29, Rick Edgecombe wrote:
> diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
> index d62b2cb85cea..b7dde8730236 100644
> --- a/arch/x86/kernel/traps.c
> +++ b/arch/x86/kernel/traps.c
> @@ -229,16 +223,74 @@ enum cp_error_code {
>  	CP_ENCL	     = 1 << 15,
>  };
>  
> -DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
> +#ifdef CONFIG_X86_SHADOW_STACK
> +static const char * const control_protection_err[] = {
> +	"unknown",
> +	"near-ret",
> +	"far-ret/iret",
> +	"endbranch",
> +	"rstorssp",
> +	"setssbsy",
> +};

These are a mix of SHSTK and IBT errors.  They should be inside
CONFIG_X86_CET using Kees' suggestion.

Also, if you express this as

static const char errors[][10] = {
    [0] = "unknown",
    [1] = "near ret",
    [2] = "far/iret",
    [3] = "endbranch",
    [4] = "rstorssp",
    [5] = "setssbsy",
};

then you can encode all the strings in roughly the space it takes to lay
out the pointers above.

> +
> +static DEFINE_RATELIMIT_STATE(cpf_rate, DEFAULT_RATELIMIT_INTERVAL,
> +			      DEFAULT_RATELIMIT_BURST);
> +
> +static void do_user_control_protection_fault(struct pt_regs *regs,
> +					     unsigned long error_code)
>  {
> -	if (!cpu_feature_enabled(X86_FEATURE_IBT)) {
> -		pr_err("Unexpected #CP\n");
> -		BUG();
> +	struct task_struct *tsk;
> +	unsigned long ssp;
> +
> +	/* Read SSP before enabling interrupts. */
> +	rdmsrl(MSR_IA32_PL3_SSP, ssp);
> +
> +	cond_local_irq_enable(regs);
> +
> +	if (!cpu_feature_enabled(X86_FEATURE_SHSTK))
> +		WARN_ONCE(1, "User-mode control protection fault with shadow support disabled\n");

So it's ok to get an unexpected #CP on CET-capable hardware, but not on
CET-incapable hardware?

The conditions for this WARN() (and others) probably want adjusting to
what the kernel has enabled, not what hardware is capable of.

> @@ -283,9 +335,29 @@ static int __init ibt_setup(char *str)
>  }
>  
>  __setup("ibt=", ibt_setup);
> -
> +#else
> +static void do_kernel_control_protection_fault(struct pt_regs *regs)
> +{
> +	WARN_ONCE(1, "Kernel-mode control protection fault with IBT disabled\n");
> +}
>  #endif /* CONFIG_X86_KERNEL_IBT */
>  
> +#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)
> +DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
> +{
> +	if (!cpu_feature_enabled(X86_FEATURE_IBT) &&
> +	    !cpu_feature_enabled(X86_FEATURE_SHSTK)) {
> +		pr_err("Unexpected #CP\n");

Do some future poor sole a favour and render the numeric error code
too.  Without it, the error is ambiguous between SHSTK and IBT when %rip
points at a call/ret instruction.

~Andrew
Peter Zijlstra Oct. 5, 2022, 9:39 a.m. UTC | #9
On Thu, Sep 29, 2022 at 03:29:04PM -0700, Rick Edgecombe wrote:

> diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
> index d62b2cb85cea..b7dde8730236 100644
> --- a/arch/x86/kernel/traps.c
> +++ b/arch/x86/kernel/traps.c

> @@ -229,16 +223,74 @@ enum cp_error_code {
>  	CP_ENCL	     = 1 << 15,
>  };
>  
> -DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
> +#ifdef CONFIG_X86_SHADOW_STACK
> +static const char * const control_protection_err[] = {
> +	"unknown",
> +	"near-ret",
> +	"far-ret/iret",
> +	"endbranch",
> +	"rstorssp",
> +	"setssbsy",
> +};
> +
> +static DEFINE_RATELIMIT_STATE(cpf_rate, DEFAULT_RATELIMIT_INTERVAL,
> +			      DEFAULT_RATELIMIT_BURST);
> +
> +static void do_user_control_protection_fault(struct pt_regs *regs,
> +					     unsigned long error_code)
>  {
> -	if (!cpu_feature_enabled(X86_FEATURE_IBT)) {
> -		pr_err("Unexpected #CP\n");
> -		BUG();
> +	struct task_struct *tsk;
> +	unsigned long ssp;
> +
> +	/* Read SSP before enabling interrupts. */
> +	rdmsrl(MSR_IA32_PL3_SSP, ssp);
> +
> +	cond_local_irq_enable(regs);
> +
> +	if (!cpu_feature_enabled(X86_FEATURE_SHSTK))
> +		WARN_ONCE(1, "User-mode control protection fault with shadow support disabled\n");
> +
> +	tsk = current;
> +	tsk->thread.error_code = error_code;
> +	tsk->thread.trap_nr = X86_TRAP_CP;
> +
> +	/* Ratelimit to prevent log spamming. */
> +	if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
> +	    __ratelimit(&cpf_rate)) {
> +		unsigned int cpec;
> +
> +		cpec = error_code & CP_EC;
> +		if (cpec >= ARRAY_SIZE(control_protection_err))
> +			cpec = 0;
> +
> +		pr_emerg("%s[%d] control protection ip:%lx sp:%lx ssp:%lx error:%lx(%s)%s",
> +			 tsk->comm, task_pid_nr(tsk),
> +			 regs->ip, regs->sp, ssp, error_code,
> +			 control_protection_err[cpec],
> +			 error_code & CP_ENCL ? " in enclave" : "");
> +		print_vma_addr(KERN_CONT " in ", regs->ip);
> +		pr_cont("\n");
>  	}
>  
> -	if (WARN_ON_ONCE(user_mode(regs) || (error_code & CP_EC) != CP_ENDBR))
> -		return;

Why are you removing the (error_code & CP_EC) != CP_ENDBR check from the
kernel handler?

> +	force_sig_fault(SIGSEGV, SEGV_CPERR, (void __user *)0);
> +	cond_local_irq_disable(regs);
> +}
> +#else
> +static void do_user_control_protection_fault(struct pt_regs *regs,
> +					     unsigned long error_code)
> +{
> +	WARN_ONCE(1, "User-mode control protection fault with shadow support disabled\n");
> +}
> +#endif
> +
> +#ifdef CONFIG_X86_KERNEL_IBT
> +
> +static __ro_after_init bool ibt_fatal = true;
> +
> +extern void ibt_selftest_ip(void); /* code label defined in asm below */
>  
> +static void do_kernel_control_protection_fault(struct pt_regs *regs)
> +{
>  	if (unlikely(regs->ip == (unsigned long)&ibt_selftest_ip)) {
>  		regs->ax = 0;
>  		return;
> @@ -283,9 +335,29 @@ static int __init ibt_setup(char *str)
>  }
>  
>  __setup("ibt=", ibt_setup);
> -
> +#else
> +static void do_kernel_control_protection_fault(struct pt_regs *regs)
> +{
> +	WARN_ONCE(1, "Kernel-mode control protection fault with IBT disabled\n");
> +}
>  #endif /* CONFIG_X86_KERNEL_IBT */
>  
> +#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)
> +DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
> +{
> +	if (!cpu_feature_enabled(X86_FEATURE_IBT) &&
> +	    !cpu_feature_enabled(X86_FEATURE_SHSTK)) {
> +		pr_err("Unexpected #CP\n");
> +		BUG();
> +	}
> +
> +	if (user_mode(regs))
> +		do_user_control_protection_fault(regs, error_code);
> +	else
> +		do_kernel_control_protection_fault(regs);

These function names are weirdly long, surely they can do without the
_fault part at the very least. And as stated above, I would really like
the kernel thing to retain the error_code argument.

> +}
> +#endif /* defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK) */
Edgecombe, Rick P Oct. 5, 2022, 10:44 p.m. UTC | #10
On Wed, 2022-10-05 at 01:20 +0000, Andrew Cooper wrote:
> On 29/09/2022 23:29, Rick Edgecombe wrote:
> > diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
> > index d62b2cb85cea..b7dde8730236 100644
> > --- a/arch/x86/kernel/traps.c
> > +++ b/arch/x86/kernel/traps.c
> > @@ -229,16 +223,74 @@ enum cp_error_code {
> >  	CP_ENCL	     = 1 << 15,
> >  };
> >  
> > -DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
> > +#ifdef CONFIG_X86_SHADOW_STACK
> > +static const char * const control_protection_err[] = {
> > +	"unknown",
> > +	"near-ret",
> > +	"far-ret/iret",
> > +	"endbranch",
> > +	"rstorssp",
> > +	"setssbsy",
> > +};
> 
> These are a mix of SHSTK and IBT errors.  They should be inside
> CONFIG_X86_CET using Kees' suggestion.
> 
> Also, if you express this as
> 
> static const char errors[][10] = {
>     [0] = "unknown",
>     [1] = "near ret",
>     [2] = "far/iret",
>     [3] = "endbranch",
>     [4] = "rstorssp",
>     [5] = "setssbsy",
> };
> 
> then you can encode all the strings in roughly the space it takes to
> lay
> out the pointers above.

It is only used in the user shadow stack side of the handler. I guess
the kernel IBT side of the handler could print these out too.

Can you explain more about why this array is better than the other one?

> 
> > +
> > +static DEFINE_RATELIMIT_STATE(cpf_rate,
> > DEFAULT_RATELIMIT_INTERVAL,
> > +			      DEFAULT_RATELIMIT_BURST);
> > +
> > +static void do_user_control_protection_fault(struct pt_regs *regs,
> > +					     unsigned long error_code)
> >  {
> > -	if (!cpu_feature_enabled(X86_FEATURE_IBT)) {
> > -		pr_err("Unexpected #CP\n");
> > -		BUG();
> > +	struct task_struct *tsk;
> > +	unsigned long ssp;
> > +
> > +	/* Read SSP before enabling interrupts. */
> > +	rdmsrl(MSR_IA32_PL3_SSP, ssp);
> > +
> > +	cond_local_irq_enable(regs);
> > +
> > +	if (!cpu_feature_enabled(X86_FEATURE_SHSTK))
> > +		WARN_ONCE(1, "User-mode control protection fault with
> > shadow support disabled\n");
> 
> So it's ok to get an unexpected #CP on CET-capable hardware, but not
> on
> CET-incapable hardware?
> 
> The conditions for this WARN() (and others) probably want adjusting
> to
> what the kernel has enabled, not what hardware is capable of.

Sorry, I don't follow. This code is only compiled in if the kernel has
been compiled for userspace shadow stacks. If the HW supports it and
the kernel is configured for it, it should be enabled. If you clear it
with the clearcpuid command line it should be as if the HW doesn't
support it. So I think it should not be too unexpected, in situations
where it gets passed this check.

> 
> > @@ -283,9 +335,29 @@ static int __init ibt_setup(char *str)
> >  }
> >  
> >  __setup("ibt=", ibt_setup);
> > -
> > +#else
> > +static void do_kernel_control_protection_fault(struct pt_regs
> > *regs)
> > +{
> > +	WARN_ONCE(1, "Kernel-mode control protection fault with IBT
> > disabled\n");
> > +}
> >  #endif /* CONFIG_X86_KERNEL_IBT */
> >  
> > +#if defined(CONFIG_X86_KERNEL_IBT) ||
> > defined(CONFIG_X86_SHADOW_STACK)
> > +DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
> > +{
> > +	if (!cpu_feature_enabled(X86_FEATURE_IBT) &&
> > +	    !cpu_feature_enabled(X86_FEATURE_SHSTK)) {
> > +		pr_err("Unexpected #CP\n");
> 
> Do some future poor sole a favour and render the numeric error code
> too.  Without it, the error is ambiguous between SHSTK and IBT when
> %rip
> points at a call/ret instruction.
> 

This was from the original kernel IBT handler. Yes, all these messages
should probably be unified too. Thanks.
Edgecombe, Rick P Oct. 5, 2022, 10:45 p.m. UTC | #11
On Wed, 2022-10-05 at 11:39 +0200, Peter Zijlstra wrote:
> On Thu, Sep 29, 2022 at 03:29:04PM -0700, Rick Edgecombe wrote:
> 
> > diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
> > index d62b2cb85cea..b7dde8730236 100644
> > --- a/arch/x86/kernel/traps.c
> > +++ b/arch/x86/kernel/traps.c
> > @@ -229,16 +223,74 @@ enum cp_error_code {
> >  	CP_ENCL	     = 1 << 15,
> >  };
> >  
> > -DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
> > +#ifdef CONFIG_X86_SHADOW_STACK
> > +static const char * const control_protection_err[] = {
> > +	"unknown",
> > +	"near-ret",
> > +	"far-ret/iret",
> > +	"endbranch",
> > +	"rstorssp",
> > +	"setssbsy",
> > +};
> > +
> > +static DEFINE_RATELIMIT_STATE(cpf_rate,
> > DEFAULT_RATELIMIT_INTERVAL,
> > +			      DEFAULT_RATELIMIT_BURST);
> > +
> > +static void do_user_control_protection_fault(struct pt_regs *regs,
> > +					     unsigned long error_code)
> >  {
> > -	if (!cpu_feature_enabled(X86_FEATURE_IBT)) {
> > -		pr_err("Unexpected #CP\n");
> > -		BUG();
> > +	struct task_struct *tsk;
> > +	unsigned long ssp;
> > +
> > +	/* Read SSP before enabling interrupts. */
> > +	rdmsrl(MSR_IA32_PL3_SSP, ssp);
> > +
> > +	cond_local_irq_enable(regs);
> > +
> > +	if (!cpu_feature_enabled(X86_FEATURE_SHSTK))
> > +		WARN_ONCE(1, "User-mode control protection fault with
> > shadow support disabled\n");
> > +
> > +	tsk = current;
> > +	tsk->thread.error_code = error_code;
> > +	tsk->thread.trap_nr = X86_TRAP_CP;
> > +
> > +	/* Ratelimit to prevent log spamming. */
> > +	if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
> > +	    __ratelimit(&cpf_rate)) {
> > +		unsigned int cpec;
> > +
> > +		cpec = error_code & CP_EC;
> > +		if (cpec >= ARRAY_SIZE(control_protection_err))
> > +			cpec = 0;
> > +
> > +		pr_emerg("%s[%d] control protection ip:%lx sp:%lx
> > ssp:%lx error:%lx(%s)%s",
> > +			 tsk->comm, task_pid_nr(tsk),
> > +			 regs->ip, regs->sp, ssp, error_code,
> > +			 control_protection_err[cpec],
> > +			 error_code & CP_ENCL ? " in enclave" : "");
> > +		print_vma_addr(KERN_CONT " in ", regs->ip);
> > +		pr_cont("\n");
> >  	}
> >  
> > -	if (WARN_ON_ONCE(user_mode(regs) || (error_code & CP_EC) !=
> > CP_ENDBR))
> > -		return;
> 
> Why are you removing the (error_code & CP_EC) != CP_ENDBR check from
> the
> kernel handler?

Argh. It was accidentally removed with the user_mode() check. I'll fix
it.

> 
> > +	force_sig_fault(SIGSEGV, SEGV_CPERR, (void __user *)0);
> > +	cond_local_irq_disable(regs);
> > +}
> > +#else
> > +static void do_user_control_protection_fault(struct pt_regs *regs,
> > +					     unsigned long error_code)
> > +{
> > +	WARN_ONCE(1, "User-mode control protection fault with shadow
> > support disabled\n");
> > +}
> > +#endif
> > +
> > +#ifdef CONFIG_X86_KERNEL_IBT
> > +
> > +static __ro_after_init bool ibt_fatal = true;
> > +
> > +extern void ibt_selftest_ip(void); /* code label defined in asm
> > below */
> >  
> > +static void do_kernel_control_protection_fault(struct pt_regs
> > *regs)
> > +{
> >  	if (unlikely(regs->ip == (unsigned long)&ibt_selftest_ip)) {
> >  		regs->ax = 0;
> >  		return;
> > @@ -283,9 +335,29 @@ static int __init ibt_setup(char *str)
> >  }
> >  
> >  __setup("ibt=", ibt_setup);
> > -
> > +#else
> > +static void do_kernel_control_protection_fault(struct pt_regs
> > *regs)
> > +{
> > +	WARN_ONCE(1, "Kernel-mode control protection fault with IBT
> > disabled\n");
> > +}
> >  #endif /* CONFIG_X86_KERNEL_IBT */
> >  
> > +#if defined(CONFIG_X86_KERNEL_IBT) ||
> > defined(CONFIG_X86_SHADOW_STACK)
> > +DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
> > +{
> > +	if (!cpu_feature_enabled(X86_FEATURE_IBT) &&
> > +	    !cpu_feature_enabled(X86_FEATURE_SHSTK)) {
> > +		pr_err("Unexpected #CP\n");
> > +		BUG();
> > +	}
> > +
> > +	if (user_mode(regs))
> > +		do_user_control_protection_fault(regs, error_code);
> > +	else
> > +		do_kernel_control_protection_fault(regs);
> 
> These function names are weirdly long, surely they can do without the
> _fault part at the very least. And as stated above, I would really
> like
> the kernel thing to retain the error_code argument.
> 

I can shorten them. Thanks.

> > +}
> > +#endif /* defined(CONFIG_X86_KERNEL_IBT) ||
> > defined(CONFIG_X86_SHADOW_STACK) */
> 
>
diff mbox series

Patch

diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
index ea128e32e8ca..fa47b8754624 100644
--- a/arch/arm/kernel/signal.c
+++ b/arch/arm/kernel/signal.c
@@ -681,7 +681,7 @@  asmlinkage void do_rseq_syscall(struct pt_regs *regs)
  */
 static_assert(NSIGILL	== 11);
 static_assert(NSIGFPE	== 15);
-static_assert(NSIGSEGV	== 9);
+static_assert(NSIGSEGV	== 10);
 static_assert(NSIGBUS	== 5);
 static_assert(NSIGTRAP	== 6);
 static_assert(NSIGCHLD	== 6);
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 9ad911f1647c..81b13a21046e 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -1166,7 +1166,7 @@  void __init minsigstksz_setup(void)
  */
 static_assert(NSIGILL	== 11);
 static_assert(NSIGFPE	== 15);
-static_assert(NSIGSEGV	== 9);
+static_assert(NSIGSEGV	== 10);
 static_assert(NSIGBUS	== 5);
 static_assert(NSIGTRAP	== 6);
 static_assert(NSIGCHLD	== 6);
diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c
index 4700f8522d27..bbd542704730 100644
--- a/arch/arm64/kernel/signal32.c
+++ b/arch/arm64/kernel/signal32.c
@@ -460,7 +460,7 @@  void compat_setup_restart_syscall(struct pt_regs *regs)
  */
 static_assert(NSIGILL	== 11);
 static_assert(NSIGFPE	== 15);
-static_assert(NSIGSEGV	== 9);
+static_assert(NSIGSEGV	== 10);
 static_assert(NSIGBUS	== 5);
 static_assert(NSIGTRAP	== 6);
 static_assert(NSIGCHLD	== 6);
diff --git a/arch/sparc/kernel/signal32.c b/arch/sparc/kernel/signal32.c
index dad38960d1a8..82da8a2d769d 100644
--- a/arch/sparc/kernel/signal32.c
+++ b/arch/sparc/kernel/signal32.c
@@ -751,7 +751,7 @@  asmlinkage int do_sys32_sigstack(u32 u_ssptr, u32 u_ossptr, unsigned long sp)
  */
 static_assert(NSIGILL	== 11);
 static_assert(NSIGFPE	== 15);
-static_assert(NSIGSEGV	== 9);
+static_assert(NSIGSEGV	== 10);
 static_assert(NSIGBUS	== 5);
 static_assert(NSIGTRAP	== 6);
 static_assert(NSIGCHLD	== 6);
diff --git a/arch/sparc/kernel/signal_64.c b/arch/sparc/kernel/signal_64.c
index 570e43e6fda5..b4e410976e0d 100644
--- a/arch/sparc/kernel/signal_64.c
+++ b/arch/sparc/kernel/signal_64.c
@@ -562,7 +562,7 @@  void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, unsigned long
  */
 static_assert(NSIGILL	== 11);
 static_assert(NSIGFPE	== 15);
-static_assert(NSIGSEGV	== 9);
+static_assert(NSIGSEGV	== 10);
 static_assert(NSIGBUS	== 5);
 static_assert(NSIGTRAP	== 6);
 static_assert(NSIGCHLD	== 6);
diff --git a/arch/x86/include/asm/idtentry.h b/arch/x86/include/asm/idtentry.h
index 72184b0b2219..6768c9d4468c 100644
--- a/arch/x86/include/asm/idtentry.h
+++ b/arch/x86/include/asm/idtentry.h
@@ -618,7 +618,7 @@  DECLARE_IDTENTRY_RAW_ERRORCODE(X86_TRAP_DF,	xenpv_exc_double_fault);
 #endif
 
 /* #CP */
-#ifdef CONFIG_X86_KERNEL_IBT
+#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)
 DECLARE_IDTENTRY_ERRORCODE(X86_TRAP_CP,	exc_control_protection);
 #endif
 
diff --git a/arch/x86/kernel/idt.c b/arch/x86/kernel/idt.c
index a58c6bc1cd68..90cce3614ead 100644
--- a/arch/x86/kernel/idt.c
+++ b/arch/x86/kernel/idt.c
@@ -107,7 +107,7 @@  static const __initconst struct idt_data def_idts[] = {
 	ISTG(X86_TRAP_MC,		asm_exc_machine_check, IST_INDEX_MCE),
 #endif
 
-#ifdef CONFIG_X86_KERNEL_IBT
+#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)
 	INTG(X86_TRAP_CP,		asm_exc_control_protection),
 #endif
 
diff --git a/arch/x86/kernel/signal_compat.c b/arch/x86/kernel/signal_compat.c
index 879ef8c72f5c..d441804443d5 100644
--- a/arch/x86/kernel/signal_compat.c
+++ b/arch/x86/kernel/signal_compat.c
@@ -27,7 +27,7 @@  static inline void signal_compat_build_tests(void)
 	 */
 	BUILD_BUG_ON(NSIGILL  != 11);
 	BUILD_BUG_ON(NSIGFPE  != 15);
-	BUILD_BUG_ON(NSIGSEGV != 9);
+	BUILD_BUG_ON(NSIGSEGV != 10);
 	BUILD_BUG_ON(NSIGBUS  != 5);
 	BUILD_BUG_ON(NSIGTRAP != 6);
 	BUILD_BUG_ON(NSIGCHLD != 6);
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index d62b2cb85cea..b7dde8730236 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -211,12 +211,6 @@  DEFINE_IDTENTRY(exc_overflow)
 	do_error_trap(regs, 0, "overflow", X86_TRAP_OF, SIGSEGV, 0, NULL);
 }
 
-#ifdef CONFIG_X86_KERNEL_IBT
-
-static __ro_after_init bool ibt_fatal = true;
-
-extern void ibt_selftest_ip(void); /* code label defined in asm below */
-
 enum cp_error_code {
 	CP_EC        = (1 << 15) - 1,
 
@@ -229,16 +223,74 @@  enum cp_error_code {
 	CP_ENCL	     = 1 << 15,
 };
 
-DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
+#ifdef CONFIG_X86_SHADOW_STACK
+static const char * const control_protection_err[] = {
+	"unknown",
+	"near-ret",
+	"far-ret/iret",
+	"endbranch",
+	"rstorssp",
+	"setssbsy",
+};
+
+static DEFINE_RATELIMIT_STATE(cpf_rate, DEFAULT_RATELIMIT_INTERVAL,
+			      DEFAULT_RATELIMIT_BURST);
+
+static void do_user_control_protection_fault(struct pt_regs *regs,
+					     unsigned long error_code)
 {
-	if (!cpu_feature_enabled(X86_FEATURE_IBT)) {
-		pr_err("Unexpected #CP\n");
-		BUG();
+	struct task_struct *tsk;
+	unsigned long ssp;
+
+	/* Read SSP before enabling interrupts. */
+	rdmsrl(MSR_IA32_PL3_SSP, ssp);
+
+	cond_local_irq_enable(regs);
+
+	if (!cpu_feature_enabled(X86_FEATURE_SHSTK))
+		WARN_ONCE(1, "User-mode control protection fault with shadow support disabled\n");
+
+	tsk = current;
+	tsk->thread.error_code = error_code;
+	tsk->thread.trap_nr = X86_TRAP_CP;
+
+	/* Ratelimit to prevent log spamming. */
+	if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
+	    __ratelimit(&cpf_rate)) {
+		unsigned int cpec;
+
+		cpec = error_code & CP_EC;
+		if (cpec >= ARRAY_SIZE(control_protection_err))
+			cpec = 0;
+
+		pr_emerg("%s[%d] control protection ip:%lx sp:%lx ssp:%lx error:%lx(%s)%s",
+			 tsk->comm, task_pid_nr(tsk),
+			 regs->ip, regs->sp, ssp, error_code,
+			 control_protection_err[cpec],
+			 error_code & CP_ENCL ? " in enclave" : "");
+		print_vma_addr(KERN_CONT " in ", regs->ip);
+		pr_cont("\n");
 	}
 
-	if (WARN_ON_ONCE(user_mode(regs) || (error_code & CP_EC) != CP_ENDBR))
-		return;
+	force_sig_fault(SIGSEGV, SEGV_CPERR, (void __user *)0);
+	cond_local_irq_disable(regs);
+}
+#else
+static void do_user_control_protection_fault(struct pt_regs *regs,
+					     unsigned long error_code)
+{
+	WARN_ONCE(1, "User-mode control protection fault with shadow support disabled\n");
+}
+#endif
+
+#ifdef CONFIG_X86_KERNEL_IBT
+
+static __ro_after_init bool ibt_fatal = true;
+
+extern void ibt_selftest_ip(void); /* code label defined in asm below */
 
+static void do_kernel_control_protection_fault(struct pt_regs *regs)
+{
 	if (unlikely(regs->ip == (unsigned long)&ibt_selftest_ip)) {
 		regs->ax = 0;
 		return;
@@ -283,9 +335,29 @@  static int __init ibt_setup(char *str)
 }
 
 __setup("ibt=", ibt_setup);
-
+#else
+static void do_kernel_control_protection_fault(struct pt_regs *regs)
+{
+	WARN_ONCE(1, "Kernel-mode control protection fault with IBT disabled\n");
+}
 #endif /* CONFIG_X86_KERNEL_IBT */
 
+#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)
+DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
+{
+	if (!cpu_feature_enabled(X86_FEATURE_IBT) &&
+	    !cpu_feature_enabled(X86_FEATURE_SHSTK)) {
+		pr_err("Unexpected #CP\n");
+		BUG();
+	}
+
+	if (user_mode(regs))
+		do_user_control_protection_fault(regs, error_code);
+	else
+		do_kernel_control_protection_fault(regs);
+}
+#endif /* defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK) */
+
 #ifdef CONFIG_X86_F00F_BUG
 void handle_invalid_op(struct pt_regs *regs)
 #else
diff --git a/arch/x86/xen/enlighten_pv.c b/arch/x86/xen/enlighten_pv.c
index 0ed2e487a693..57faa287163f 100644
--- a/arch/x86/xen/enlighten_pv.c
+++ b/arch/x86/xen/enlighten_pv.c
@@ -628,7 +628,7 @@  static struct trap_array_entry trap_array[] = {
 	TRAP_ENTRY(exc_coprocessor_error,		false ),
 	TRAP_ENTRY(exc_alignment_check,			false ),
 	TRAP_ENTRY(exc_simd_coprocessor_error,		false ),
-#ifdef CONFIG_X86_KERNEL_IBT
+#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)
 	TRAP_ENTRY(exc_control_protection,		false ),
 #endif
 };
diff --git a/arch/x86/xen/xen-asm.S b/arch/x86/xen/xen-asm.S
index 6b4fdf6b9542..e45ff6300c7d 100644
--- a/arch/x86/xen/xen-asm.S
+++ b/arch/x86/xen/xen-asm.S
@@ -148,7 +148,7 @@  xen_pv_trap asm_exc_page_fault
 xen_pv_trap asm_exc_spurious_interrupt_bug
 xen_pv_trap asm_exc_coprocessor_error
 xen_pv_trap asm_exc_alignment_check
-#ifdef CONFIG_X86_KERNEL_IBT
+#if defined(CONFIG_X86_KERNEL_IBT) || defined(CONFIG_X86_SHADOW_STACK)
 xen_pv_trap asm_exc_control_protection
 #endif
 #ifdef CONFIG_X86_MCE
diff --git a/include/uapi/asm-generic/siginfo.h b/include/uapi/asm-generic/siginfo.h
index ffbe4cec9f32..0f52d0ac47c5 100644
--- a/include/uapi/asm-generic/siginfo.h
+++ b/include/uapi/asm-generic/siginfo.h
@@ -242,7 +242,8 @@  typedef struct siginfo {
 #define SEGV_ADIPERR	7	/* Precise MCD exception */
 #define SEGV_MTEAERR	8	/* Asynchronous ARM MTE error */
 #define SEGV_MTESERR	9	/* Synchronous ARM MTE exception */
-#define NSIGSEGV	9
+#define SEGV_CPERR	10	/* Control protection fault */
+#define NSIGSEGV	10
 
 /*
  * SIGBUS si_codes