public inbox for glibc-cvs@sourceware.org
help / color / mirror / Atom feed
From: H.J. Lu <hjl@sourceware.org>
To: glibc-cvs@sourceware.org
Subject: [glibc] x86-64/cet: Check the restore token in longjmp
Date: Thu,  4 Jan 2024 21:39:07 +0000 (GMT)	[thread overview]
Message-ID: <20240104213907.447833858D32@sourceware.org> (raw)

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=35694d3416b273ac19d67ffa49b7969f36684ae1

commit 35694d3416b273ac19d67ffa49b7969f36684ae1
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Tue Jan 2 07:03:29 2024 -0800

    x86-64/cet: Check the restore token in longjmp
    
    setcontext and swapcontext put a restore token on the old shadow stack
    which is used to restore the target shadow stack when switching user
    contexts.  When longjmp from a user context, the target shadow stack
    can be different from the current shadow stack and INCSSP can't be
    used to restore the shadow stack pointer to the target shadow stack.
    
    Update longjmp to search for a restore token.  If found, use the token
    to restore the shadow stack pointer before using INCSSP to pop the
    shadow stack.  Stop the token search and use INCSSP if the shadow stack
    entry value is the same as the current shadow stack pointer.
    
    It is a user error if there is a shadow stack switch without leaving a
    restore token on the old shadow stack.
    
    The only difference between __longjmp.S and __longjmp_chk.S is that
    __longjmp_chk.S has a check for invalid longjmp usages.  Merge
    __longjmp.S and __longjmp_chk.S by adding the CHECK_INVALID_LONGJMP
    macro.
    Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>

Diff:
---
 sysdeps/unix/sysv/linux/x86_64/____longjmp_chk.S | 179 +++++------------------
 sysdeps/x86/__longjmp_cancel.S                   |   3 +
 sysdeps/x86_64/__longjmp.S                       |  47 +++++-
 3 files changed, 84 insertions(+), 145 deletions(-)

diff --git a/sysdeps/unix/sysv/linux/x86_64/____longjmp_chk.S b/sysdeps/unix/sysv/linux/x86_64/____longjmp_chk.S
index deb6398f43..9aa24620b9 100644
--- a/sysdeps/unix/sysv/linux/x86_64/____longjmp_chk.S
+++ b/sysdeps/unix/sysv/linux/x86_64/____longjmp_chk.S
@@ -15,18 +15,7 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
-#include <sysdep.h>
-#include <pointer_guard.h>
-#include <jmpbuf-offsets.h>
-#include <asm-syntax.h>
-#include <stap-probe.h>
 #include <sigaltstack-offsets.h>
-#include <jmp_buf-ssp.h>
-
-/* Don't restore shadow stack register if shadow stack isn't enabled.  */
-#if !SHSTK_ENABLED
-# undef SHADOW_STACK_POINTER_OFFSET
-#endif
 
 	.section .rodata.str1.1,"aMS",@progbits,1
 	.type	longjmp_msg,@object
@@ -34,136 +23,48 @@ longjmp_msg:
 	.string "longjmp causes uninitialized stack frame"
 	.size	longjmp_msg, .-longjmp_msg
 
-
-//#define __longjmp ____longjmp_chk
-
 #ifdef PIC
-# define CALL_FAIL	sub	$8, %RSP_LP;				      \
-			cfi_remember_state;				      \
-			cfi_def_cfa_offset(16);				      \
-			lea	longjmp_msg(%rip), %RDI_LP;		      \
-			call	HIDDEN_JUMPTARGET(__fortify_fail);	      \
-			nop;						      \
-			cfi_restore_state
+# define LOAD_MSG	lea	longjmp_msg(%rip), %RDI_LP
 #else
-# define CALL_FAIL	sub	$8, %RSP_LP;				      \
-			cfi_remember_state;				      \
-			cfi_def_cfa_offset(16);				      \
-			mov	$longjmp_msg, %RDI_LP;			      \
-			call	HIDDEN_JUMPTARGET(__fortify_fail);	      \
-			nop;						      \
-			cfi_restore_state
+# define LOAD_MSG	mov	$longjmp_msg, %RDI_LP
 #endif
 
-/* Jump to the position specified by ENV, causing the
-   setjmp call there to return VAL, or 1 if VAL is 0.
-   void __longjmp (__jmp_buf env, int val).  */
-	.text
-ENTRY(____longjmp_chk)
-	/* Restore registers.  */
-	mov	(JB_RSP*8)(%rdi), %R8_LP
-	mov	(JB_RBP*8)(%rdi),%R9_LP
-	mov	(JB_PC*8)(%rdi), %RDX_LP
-#ifdef PTR_DEMANGLE
-	PTR_DEMANGLE (%R8_LP)
-	PTR_DEMANGLE (%R9_LP)
-	PTR_DEMANGLE (%RDX_LP)
-# ifdef __ILP32__
-	/* We ignored the high bits of the %rbp value because only the low
-	   bits are mangled.  But we cannot presume that %rbp is being used
-	   as a pointer and truncate it, so recover the high bits.  */
-	movl (JB_RBP*8 + 4)(%rdi), %eax
-	shlq $32, %rax
-	orq %rax, %r9
-# endif
-#endif
-
-	cmp	%R8_LP, %RSP_LP
-	jbe	.Lok
-
-	/* Save function parameters.  */
-	movq	%rdi, %r10
-	cfi_register (%rdi, %r10)
-	movl	%esi, %ebx
-	cfi_register (%rsi, %rbx)
-
-	xorl	%edi, %edi
-	lea	-sizeSS(%rsp), %RSI_LP
-	movl	$__NR_sigaltstack, %eax
-	syscall
-	/* Without working sigaltstack we cannot perform the test.  */
-	testl	%eax, %eax
-	jne	.Lok2
-	testl	$1, (-sizeSS + oSS_FLAGS)(%rsp)
-	jz	.Lfail
-
-	mov	(-sizeSS + oSS_SP)(%rsp), %RAX_LP
-	add	(-sizeSS + oSS_SIZE)(%rsp), %RAX_LP
-	sub	%R8_LP, %RAX_LP
-	cmp	(-sizeSS + oSS_SIZE)(%rsp), %RAX_LP
-	jae	.Lok2
-
-.Lfail:	CALL_FAIL
-
-.Lok2:	movq	%r10, %rdi
-	cfi_restore (%rdi)
-	movl	%ebx, %esi
-	cfi_restore (%rsi)
-
+#define CHECK_INVALID_LONGJMP \
+	cmp	%R8_LP, %RSP_LP;					\
+	jbe	.Lok;							\
+	/* Save function parameters.  */				\
+	movq	%rdi, %r10;						\
+	cfi_register (%rdi, %r10);					\
+	movl	%esi, %ebx;						\
+	cfi_register (%rsi, %rbx);					\
+	xorl	%edi, %edi;						\
+	lea	-sizeSS(%rsp), %RSI_LP;					\
+	movl	$__NR_sigaltstack, %eax;				\
+	syscall;							\
+	/* Without working sigaltstack we cannot perform the test.  */	\
+	testl	%eax, %eax;						\
+	jne	.Lok2;							\
+	testl	$1, (-sizeSS + oSS_FLAGS)(%rsp);			\
+	jz	.Lfail;							\
+	mov	(-sizeSS + oSS_SP)(%rsp), %RAX_LP;			\
+	add	(-sizeSS + oSS_SIZE)(%rsp), %RAX_LP;			\
+	sub	%R8_LP, %RAX_LP;					\
+	cmp	(-sizeSS + oSS_SIZE)(%rsp), %RAX_LP;			\
+	jae	.Lok2;							\
+.Lfail:									\
+	sub	$8, %RSP_LP;						\
+	cfi_remember_state;						\
+	cfi_def_cfa_offset(16);						\
+	LOAD_MSG;							\
+	call	HIDDEN_JUMPTARGET(__fortify_fail);			\
+	nop;								\
+	cfi_restore_state;						\
+.Lok2:									\
+	movq	%r10, %rdi;						\
+	cfi_restore (%rdi);						\
+	movl	%ebx, %esi;						\
+	cfi_restore (%rsi);						\
 .Lok:
-#ifdef SHADOW_STACK_POINTER_OFFSET
-# if IS_IN (libc) && defined SHARED && defined FEATURE_1_OFFSET
-	/* Check if Shadow Stack is enabled.  */
-	testl	$X86_FEATURE_1_SHSTK, %fs:FEATURE_1_OFFSET
-	jz	L(skip_ssp)
-# else
-	xorl	%eax, %eax
-# endif
-	/* Check and adjust the Shadow-Stack-Pointer.  */
-	rdsspq	%rax
-	/* And compare it with the saved ssp value.  */
-	subq	SHADOW_STACK_POINTER_OFFSET(%rdi), %rax
-	je	L(skip_ssp)
-	/* Count the number of frames to adjust and adjust it
-	   with incssp instruction.  The instruction can adjust
-	   the ssp by [0..255] value only thus use a loop if
-	   the number of frames is bigger than 255.  */
-	negq	%rax
-	shrq	$3, %rax
-	/* NB: We saved Shadow-Stack-Pointer of setjmp.  Since we are
-	       restoring Shadow-Stack-Pointer of setjmp's caller, we
-	       need to unwind shadow stack by one more frame.  */
-	addq	$1, %rax
-	movl	$255, %ebx
-L(loop):
-	cmpq	%rbx, %rax
-	cmovb	%rax, %rbx
-	incsspq	%rbx
-	subq	%rbx, %rax
-	ja	L(loop)
-L(skip_ssp):
-#endif
-	LIBC_PROBE (longjmp, 3, LP_SIZE@%RDI_LP, -4@%esi, LP_SIZE@%RDX_LP)
-	/* We add unwind information for the target here.  */
-	cfi_def_cfa(%rdi, 0)
-	cfi_register(%rsp,%r8)
-	cfi_register(%rbp,%r9)
-	cfi_register(%rip,%rdx)
-	cfi_offset(%rbx,JB_RBX*8)
-	cfi_offset(%r12,JB_R12*8)
-	cfi_offset(%r13,JB_R13*8)
-	cfi_offset(%r14,JB_R14*8)
-	cfi_offset(%r15,JB_R15*8)
-	movq	(JB_RBX*8)(%rdi), %rbx
-	movq	(JB_R12*8)(%rdi), %r12
-	movq	(JB_R13*8)(%rdi), %r13
-	movq	(JB_R14*8)(%rdi), %r14
-	movq	(JB_R15*8)(%rdi), %r15
-	/* Set return value for setjmp.  */
-	movl	%esi, %eax
-	mov	%R8_LP, %RSP_LP
-	movq	%r9,%rbp
-	LIBC_PROBE (longjmp_target, 3,
-		    LP_SIZE@%RDI_LP, -4@%eax, LP_SIZE@%RDX_LP)
-	jmpq	*%rdx
-END (____longjmp_chk)
+
+#define __longjmp ____longjmp_chk
+#include <__longjmp.S>
diff --git a/sysdeps/x86/__longjmp_cancel.S b/sysdeps/x86/__longjmp_cancel.S
index e71b304257..b03f52b308 100644
--- a/sysdeps/x86/__longjmp_cancel.S
+++ b/sysdeps/x86/__longjmp_cancel.S
@@ -16,5 +16,8 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
+/* Don't restore shadow stack register for __longjmp_cancel.  */
+#define DO_NOT_RESTORE_SHADOW_STACK
+
 #define __longjmp __longjmp_cancel
 #include <__longjmp.S>
diff --git a/sysdeps/x86_64/__longjmp.S b/sysdeps/x86_64/__longjmp.S
index c9f70f8e2a..22fedc4997 100644
--- a/sysdeps/x86_64/__longjmp.S
+++ b/sysdeps/x86_64/__longjmp.S
@@ -22,14 +22,15 @@
 #include <asm-syntax.h>
 #include <stap-probe.h>
 
-/* Don't restore shadow stack register if
-   1. Shadow stack isn't enabled.  Or
-   2. __longjmp is defined for __longjmp_cancel.
- */
-#if !SHSTK_ENABLED || defined __longjmp
+/* Don't restore shadow stack register if shadow stack isn't enabled.  */
+#if !SHSTK_ENABLED || defined DO_NOT_RESTORE_SHADOW_STACK
 # undef SHADOW_STACK_POINTER_OFFSET
 #endif
 
+#ifndef CHECK_INVALID_LONGJMP
+# define CHECK_INVALID_LONGJMP
+#endif
+
 /* Jump to the position specified by ENV, causing the
    setjmp call there to return VAL, or 1 if VAL is 0.
    void __longjmp (__jmp_buf env, int val).  */
@@ -52,6 +53,9 @@ ENTRY(__longjmp)
 	orq %rax, %r9
 # endif
 #endif
+
+	CHECK_INVALID_LONGJMP
+
 #ifdef SHADOW_STACK_POINTER_OFFSET
 # if IS_IN (libc) && defined SHARED && defined FEATURE_1_OFFSET
 	/* Check if Shadow Stack is enabled.  */
@@ -63,9 +67,40 @@ ENTRY(__longjmp)
 	/* Check and adjust the Shadow-Stack-Pointer.  */
 	/* Get the current ssp.  */
 	rdsspq %rax
+	/* Save the current ssp.  */
+	movq %rax, %r10
 	/* And compare it with the saved ssp value.  */
-	subq SHADOW_STACK_POINTER_OFFSET(%rdi), %rax
+	movq SHADOW_STACK_POINTER_OFFSET(%rdi), %rcx
+	subq %rcx, %rax
 	je L(skip_ssp)
+
+	/* Save the target ssp.  */
+	movq %rcx, %r11
+
+L(find_restore_token_loop):
+	/* Look for a restore token.  */
+	movq -8(%rcx), %rbx
+	andq $-8, %rbx
+	cmpq %rcx, %rbx
+	/* Find the restore token.  */
+	je L(restore_shadow_stack)
+
+	/* Try the next slot.  */
+	subq $8, %rcx
+	/* Stop if the current ssp is found.  */
+	cmpq %rcx, %r10
+	jne L(find_restore_token_loop)
+	jmp L(no_shadow_stack_token)
+
+L(restore_shadow_stack):
+	/* Restore the target shadow stack.  */
+	rstorssp -8(%rcx)
+	/* Save the restore token on the old shadow stack.  */
+	saveprevssp
+	rdsspq %rax
+	subq %r11, %rax
+
+L(no_shadow_stack_token):
 	/* Count the number of frames to adjust and adjust it
 	   with incssp instruction.  The instruction can adjust
 	   the ssp by [0..255] value only thus use a loop if

                 reply	other threads:[~2024-01-04 21:39 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240104213907.447833858D32@sourceware.org \
    --to=hjl@sourceware.org \
    --cc=glibc-cvs@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).