summaryrefslogtreecommitdiff
path: root/sysdeps/unix/sysv/linux/x86_64/swapcontext.S
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/unix/sysv/linux/x86_64/swapcontext.S')
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/swapcontext.S129
1 files changed, 129 insertions, 0 deletions
diff --git a/sysdeps/unix/sysv/linux/x86_64/swapcontext.S b/sysdeps/unix/sysv/linux/x86_64/swapcontext.S
index 1110c479fa..92d3371807 100644
--- a/sysdeps/unix/sysv/linux/x86_64/swapcontext.S
+++ b/sysdeps/unix/sysv/linux/x86_64/swapcontext.S
@@ -18,6 +18,7 @@
<http://www.gnu.org/licenses/>. */
#include <sysdep.h>
+#include <asm/prctl.h>
#include "ucontext_i.h"
@@ -67,6 +68,7 @@ ENTRY(__swapcontext)
/* The syscall destroys some registers, save them. */
movq %rsi, %r12
+ movq %rdi, %r9
/* Save the current signal mask and install the new one with
rt_sigprocmask (SIG_BLOCK, newset, oldset,_NSIG/8). */
@@ -99,6 +101,133 @@ ENTRY(__swapcontext)
movq oR14(%rdx), %r14
movq oR15(%rdx), %r15
+#if SHSTK_ENABLED
+ /* Check if shadow stack is enabled. */
+ testl $X86_FEATURE_1_SHSTK, %fs:FEATURE_1_OFFSET
+ jz L(no_shstk)
+
+ xorl %eax, %eax
+ cmpq %fs:SSP_BASE_OFFSET, %rax
+ jnz L(shadow_stack_bound_recorded)
+
+ /* Get the base address and size of the default shadow stack
+ which must be the current shadow stack since nothing has
+ been recorded yet. */
+ sub $24, %RSP_LP
+ mov %RSP_LP, %RSI_LP
+ movl $ARCH_CET_STATUS, %edi
+ movl $__NR_arch_prctl, %eax
+ syscall
+ testq %rax, %rax
+ jz L(continue_no_err)
+
+ /* This should never happen. */
+ hlt
+
+L(continue_no_err):
+ /* Record the base of the current shadow stack. */
+ movq 8(%rsp), %rax
+ movq %rax, %fs:SSP_BASE_OFFSET
+ add $24, %RSP_LP
+
+L(shadow_stack_bound_recorded):
+ /* If we unwind the stack, we can't undo stack unwinding. Just
+ save the target shadow stack pointer as the current shadow
+ stack pointer. */
+ movq oSSP(%rdx), %rcx
+ movq %rcx, oSSP(%r9)
+
+ /* Save the base of the current shadow stack. */
+ movq %fs:SSP_BASE_OFFSET, %rax
+ movq %rax, (oSSP + 8)(%r9)
+
+ /* If the base of the target shadow stack is the same as the
+ base of the current shadow stack, we unwind the shadow
+ stack. Otherwise it is a stack switch and we look for a
+ restore token. */
+ movq oSSP(%rdx), %rsi
+ movq %rsi, %rdi
+
+ /* Get the base of the target shadow stack. */
+ movq (oSSP + 8)(%rdx), %rcx
+ cmpq %fs:SSP_BASE_OFFSET, %rcx
+ je L(unwind_shadow_stack)
+
+L(find_restore_token_loop):
+ /* Look for a restore token. */
+ movq -8(%rsi), %rax
+ andq $-8, %rax
+ cmpq %rsi, %rax
+ je L(restore_shadow_stack)
+
+ /* Try the next slot. */
+ subq $8, %rsi
+ jmp L(find_restore_token_loop)
+
+L(restore_shadow_stack):
+ /* The target shadow stack will be restored. Save the current
+ shadow stack pointer. */
+ rdsspq %rcx
+ movq %rcx, oSSP(%r9)
+
+ /* Restore the target shadow stack. */
+ rstorssp -8(%rsi)
+
+ /* Save the restore token on the old shadow stack. NB: This
+ restore token may be checked by setcontext or swapcontext
+ later. */
+ saveprevssp
+
+ /* Record the new shadow stack base that was switched to. */
+ movq (oSSP + 8)(%rdx), %rax
+ movq %rax, %fs:SSP_BASE_OFFSET
+
+L(unwind_shadow_stack):
+ rdsspq %rcx
+ subq %rdi, %rcx
+ je L(skip_unwind_shadow_stack)
+ negq %rcx
+ shrq $3, %rcx
+ movl $255, %esi
+L(loop):
+ cmpq %rsi, %rcx
+ cmovb %rcx, %rsi
+ incsspq %rsi
+ subq %rsi, %rcx
+ ja L(loop)
+
+L(skip_unwind_shadow_stack):
+ /* Setup registers used for passing args. */
+ movq oRDI(%rdx), %rdi
+ movq oRSI(%rdx), %rsi
+ movq oRCX(%rdx), %rcx
+ movq oR8(%rdx), %r8
+ movq oR9(%rdx), %r9
+
+ /* Get the return address set with getcontext. */
+ movq oRIP(%rdx), %r10
+
+ /* Setup finally %rdx. */
+ movq oRDX(%rdx), %rdx
+
+ /* Check if return address is valid for the case when setcontext
+ is invoked from __start_context with linked context. */
+ rdsspq %rax
+ cmpq (%rax), %r10
+ /* Clear rax to indicate success. NB: Don't use xorl to keep
+ EFLAGS for jne. */
+ movl $0, %eax
+ jne L(jmp)
+ /* Return to the new context if return address valid. */
+ pushq %r10
+ ret
+
+L(jmp):
+ /* Jump to the new context directly. */
+ jmp *%r10
+
+L(no_shstk):
+#endif
/* The following ret should return to the address set with
getcontext. Therefore push the address on the stack. */
movq oRIP(%rdx), %rcx