summaryrefslogtreecommitdiff
path: root/sysdeps/unix/sysv/linux/x86_64/setcontext.S
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/unix/sysv/linux/x86_64/setcontext.S')
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/setcontext.S92
1 files changed, 92 insertions, 0 deletions
diff --git a/sysdeps/unix/sysv/linux/x86_64/setcontext.S b/sysdeps/unix/sysv/linux/x86_64/setcontext.S
index b42af8e291..0afdf8ce0a 100644
--- a/sysdeps/unix/sysv/linux/x86_64/setcontext.S
+++ b/sysdeps/unix/sysv/linux/x86_64/setcontext.S
@@ -18,6 +18,7 @@
<http://www.gnu.org/licenses/>. */
#include <sysdep.h>
+#include <asm/prctl.h>
#include "ucontext_i.h"
@@ -79,6 +80,97 @@ ENTRY(__setcontext)
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)
+
+ /* 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):
+ /* Pop return address from the shadow stack since setcontext
+ will not return. */
+ movq $1, %rax
+ incsspq %rax
+
+ /* Use the restore stoken to 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):
+ movq oRSI(%rdx), %rsi
+ movq oRDI(%rdx), %rdi
+ 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