1 | /* Copyright (C) 2001-2024 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) any later version. |
8 | |
9 | The GNU C Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; if not, see |
16 | <https://www.gnu.org/licenses/>. */ |
17 | |
18 | #include <sysdep.h> |
19 | #include <pointer_guard.h> |
20 | #include <jmpbuf-offsets.h> |
21 | #include <jmp_buf-ssp.h> |
22 | #include <asm-syntax.h> |
23 | #include <stap-probe.h> |
24 | |
25 | /* Don't restore shadow stack register if shadow stack isn't enabled. */ |
26 | #if !SHSTK_ENABLED || defined DO_NOT_RESTORE_SHADOW_STACK |
27 | # undef SHADOW_STACK_POINTER_OFFSET |
28 | #endif |
29 | |
30 | #ifndef CHECK_INVALID_LONGJMP |
31 | # define CHECK_INVALID_LONGJMP |
32 | #endif |
33 | |
34 | /* Jump to the position specified by ENV, causing the |
35 | setjmp call there to return VAL, or 1 if VAL is 0. |
36 | void __longjmp (__jmp_buf env, int val). */ |
37 | .text |
38 | ENTRY(__longjmp) |
39 | /* Restore registers. */ |
40 | mov (JB_RSP*8)(%rdi),%R8_LP |
41 | mov (JB_RBP*8)(%rdi),%R9_LP |
42 | mov (JB_PC*8)(%rdi),%RDX_LP |
43 | #ifdef PTR_DEMANGLE |
44 | PTR_DEMANGLE (%R8_LP) |
45 | PTR_DEMANGLE (%R9_LP) |
46 | PTR_DEMANGLE (%RDX_LP) |
47 | # ifdef __ILP32__ |
48 | /* We ignored the high bits of the %rbp value because only the low |
49 | bits are mangled. But we cannot presume that %rbp is being used |
50 | as a pointer and truncate it, so recover the high bits. */ |
51 | movl (JB_RBP*8 + 4)(%rdi), %eax |
52 | shlq $32, %rax |
53 | orq %rax, %r9 |
54 | # endif |
55 | #endif |
56 | |
57 | CHECK_INVALID_LONGJMP |
58 | |
59 | #ifdef SHADOW_STACK_POINTER_OFFSET |
60 | # if IS_IN (libc) && defined SHARED && defined FEATURE_1_OFFSET |
61 | /* Check if Shadow Stack is enabled. */ |
62 | testl $X86_FEATURE_1_SHSTK, %fs:FEATURE_1_OFFSET |
63 | jz L(skip_ssp) |
64 | # else |
65 | xorl %eax, %eax |
66 | # endif |
67 | /* Check and adjust the Shadow-Stack-Pointer. */ |
68 | /* Get the current ssp. */ |
69 | rdsspq %rax |
70 | /* Save the current ssp. */ |
71 | movq %rax, %r10 |
72 | /* And compare it with the saved ssp value. */ |
73 | movq SHADOW_STACK_POINTER_OFFSET(%rdi), %rcx |
74 | subq %rcx, %rax |
75 | je L(skip_ssp) |
76 | |
77 | /* Save the target ssp. */ |
78 | movq %rcx, %r11 |
79 | |
80 | L(find_restore_token_loop): |
81 | /* Look for a restore token. */ |
82 | movq -8(%rcx), %rbx |
83 | andq $-8, %rbx |
84 | cmpq %rcx, %rbx |
85 | /* Find the restore token. */ |
86 | je L(restore_shadow_stack) |
87 | |
88 | /* Try the next slot. */ |
89 | subq $8, %rcx |
90 | /* Stop if the current ssp is found. */ |
91 | cmpq %rcx, %r10 |
92 | jne L(find_restore_token_loop) |
93 | jmp L(no_shadow_stack_token) |
94 | |
95 | L(restore_shadow_stack): |
96 | /* Restore the target shadow stack. */ |
97 | rstorssp -8(%rcx) |
98 | /* Save the restore token on the old shadow stack. */ |
99 | saveprevssp |
100 | rdsspq %rax |
101 | subq %r11, %rax |
102 | |
103 | L(no_shadow_stack_token): |
104 | /* Count the number of frames to adjust and adjust it |
105 | with incssp instruction. The instruction can adjust |
106 | the ssp by [0..255] value only thus use a loop if |
107 | the number of frames is bigger than 255. */ |
108 | negq %rax |
109 | shrq $3, %rax |
110 | /* NB: We saved Shadow-Stack-Pointer of setjmp. Since we are |
111 | restoring Shadow-Stack-Pointer of setjmp's caller, we |
112 | need to unwind shadow stack by one more frame. */ |
113 | addq $1, %rax |
114 | |
115 | movl $255, %ebx |
116 | L(loop): |
117 | cmpq %rbx, %rax |
118 | cmovb %rax, %rbx |
119 | incsspq %rbx |
120 | subq %rbx, %rax |
121 | ja L(loop) |
122 | |
123 | L(skip_ssp): |
124 | #endif |
125 | LIBC_PROBE (longjmp, 3, LP_SIZE@%RDI_LP, -4@%esi, LP_SIZE@%RDX_LP) |
126 | /* We add unwind information for the target here. */ |
127 | cfi_def_cfa(%rdi, 0) |
128 | cfi_register(%rsp,%r8) |
129 | cfi_register(%rbp,%r9) |
130 | cfi_register(%rip,%rdx) |
131 | cfi_offset(%rbx,JB_RBX*8) |
132 | cfi_offset(%r12,JB_R12*8) |
133 | cfi_offset(%r13,JB_R13*8) |
134 | cfi_offset(%r14,JB_R14*8) |
135 | cfi_offset(%r15,JB_R15*8) |
136 | movq (JB_RBX*8)(%rdi),%rbx |
137 | movq (JB_R12*8)(%rdi),%r12 |
138 | movq (JB_R13*8)(%rdi),%r13 |
139 | movq (JB_R14*8)(%rdi),%r14 |
140 | movq (JB_R15*8)(%rdi),%r15 |
141 | /* Set return value for setjmp. */ |
142 | mov %esi, %eax |
143 | mov %R8_LP,%RSP_LP |
144 | movq %r9,%rbp |
145 | LIBC_PROBE (longjmp_target, 3, |
146 | LP_SIZE@%RDI_LP, -4@%eax, LP_SIZE@%RDX_LP) |
147 | jmpq *%rdx |
148 | END (__longjmp) |
149 | |