Skip to content

Commit bd78c55

Browse files
committed
Return stack support
1 parent 23158b0 commit bd78c55

22 files changed

+744
-0
lines changed

debug/Versions

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ libc {
4848
}
4949
GLIBC_2.11 {
5050
__longjmp_chk;
51+
__safe_longjmp_chk;
5152
}
5253
GLIBC_2.15 {
5354
__fdelt_chk; __fdelt_warn;

debug/longjmp_chk.c

+5
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,9 @@
2020
#define __longjmp ____longjmp_chk
2121
#define __libc_siglongjmp __longjmp_chk
2222

23+
#ifdef WITH_RETURN_STACK_SUPPORT
24+
#define __safe_longjmp ____safe_longjmp_chk
25+
#define __libc_safe_siglongjmp __safe_longjmp_chk
26+
#endif
27+
2328
#include <setjmp/longjmp.c>

include/setjmp.h

+21
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,17 @@
77
/* Internal machine-dependent function to restore context sans signal mask. */
88
extern void __longjmp (__jmp_buf __env, int __val)
99
__attribute__ ((__noreturn__)) attribute_hidden;
10+
#ifdef WITH_RETURN_STACK_SUPPORT
11+
extern void __safe_longjmp (__jmp_buf __env, int __val)
12+
__attribute__ ((__noreturn__)) attribute_hidden;
13+
#endif
1014

1115
extern void ____longjmp_chk (__jmp_buf __env, int __val)
1216
__attribute__ ((__noreturn__)) attribute_hidden;
17+
#ifdef WITH_RETURN_STACK_SUPPORT
18+
extern void ____safe_longjmp_chk (__jmp_buf __env, int __val)
19+
__attribute__ ((__noreturn__));
20+
#endif
1321

1422
/* Internal function to possibly save the current mask of blocked signals
1523
in ENV, and always set the flag saying whether or not it was saved.
@@ -21,12 +29,25 @@ extern void _longjmp_unwind (jmp_buf env, int val);
2129

2230
extern void __libc_siglongjmp (sigjmp_buf env, int val)
2331
__attribute__ ((noreturn));
32+
#ifdef WITH_RETURN_STACK_SUPPORT
33+
extern void __libc_safe_siglongjmp (sigjmp_buf env, int val)
34+
__attribute__ ((noreturn));
35+
#endif
2436
extern void __libc_longjmp (sigjmp_buf env, int val)
2537
__attribute__ ((noreturn));
2638
libc_hidden_proto (__libc_longjmp)
39+
#ifdef WITH_RETURN_STACK_SUPPORT
40+
extern void __libc_safe_longjmp (sigjmp_buf env, int val)
41+
__attribute__ ((noreturn));
42+
libc_hidden_proto (__libc_safe_longjmp)
43+
#endif
2744

2845
libc_hidden_proto (_setjmp)
2946
libc_hidden_proto (__sigsetjmp)
47+
#ifdef WITH_RETURN_STACK_SUPPORT
48+
libc_hidden_proto (_safe_setjmp)
49+
libc_hidden_proto (__safe_sigsetjmp)
50+
#endif
3051

3152
# if IS_IN (rtld) && !defined NO_RTLD_HIDDEN
3253
extern __typeof (__sigsetjmp) __sigsetjmp attribute_hidden;

setjmp/Versions

+3
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ libc {
22
GLIBC_2.0 {
33
# functions with special/multiple interfaces
44
_longjmp; __sigsetjmp; _setjmp;
5+
_safe_longjmp; __safe_sigsetjmp; _safe_setjmp;
56

67
# l*
78
longjmp;
9+
safe_longjmp;
810

911
# s*
1012
setjmp;
13+
safe_setjmp;
1114
}
1215
GLIBC_PRIVATE {
1316
# helper functions

setjmp/longjmp.c

+25
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,35 @@ __libc_siglongjmp (sigjmp_buf env, int val)
3939
__longjmp (env[0].__jmpbuf, val ?: 1);
4040
}
4141

42+
#ifdef WITH_RETURN_STACK_SUPPORT
43+
void
44+
__libc_safe_siglongjmp (sigjmp_buf env, int val)
45+
{
46+
/* Perform any cleanups needed by the frames being unwound. */
47+
_longjmp_unwind (env, val);
48+
49+
if (env[0].__mask_was_saved)
50+
/* Restore the saved signal mask. */
51+
(void) __sigprocmask (SIG_SETMASK, &env[0].__saved_mask,
52+
(sigset_t *) NULL);
53+
54+
/* Call the safe version of longjmp to restore the machine state. */
55+
__safe_longjmp (env[0].__jmpbuf, val ?: 1);
56+
}
57+
#endif
58+
4259
#ifndef __libc_siglongjmp
4360
strong_alias (__libc_siglongjmp, __libc_longjmp)
4461
libc_hidden_def (__libc_longjmp)
62+
# ifdef WITH_RETURN_STACK_SUPPORT
63+
strong_alias (__libc_safe_siglongjmp, __libc_safe_longjmp)
64+
# endif
4565
weak_alias (__libc_siglongjmp, _longjmp)
4666
weak_alias (__libc_siglongjmp, longjmp)
4767
weak_alias (__libc_siglongjmp, siglongjmp)
68+
# ifdef WITH_RETURN_STACK_SUPPORT
69+
weak_alias (__libc_safe_siglongjmp, _safe_longjmp)
70+
weak_alias (__libc_safe_siglongjmp, safe_longjmp)
71+
weak_alias (__libc_safe_siglongjmp, safe_siglongjmp)
72+
# endif
4873
#endif

setjmp/setjmp.c

+4
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,7 @@ __libc_sigsetjmp (jmp_buf env, int savemask)
3333

3434
weak_alias (__libc_sigsetjmp, __sigsetjmp)
3535
stub_warning (__sigsetjmp)
36+
#ifdef WITH_RETURN_STACK_SUPPORT
37+
weak_alias (__libc_sigsetjmp, __safe_sigsetjmp)
38+
stub_warning (__safe_sigsetjmp)
39+
#endif

setjmp/setjmp.h

+49
Original file line numberDiff line numberDiff line change
@@ -48,31 +48,66 @@ typedef struct __jmp_buf_tag jmp_buf[1];
4848
Return 0. */
4949
extern int setjmp (jmp_buf __env) __THROWNL;
5050

51+
#ifdef WITH_RETURN_STACK_SUPPORT
52+
/* Safe version of `setjmp'. Instead of the return stack pointer,
53+
saves the marker into ENV. */
54+
extern int safe_setjmp (jmp_buf __env) __THROWNL;
55+
#endif
56+
5157
/* Store the calling environment in ENV, also saving the
5258
signal mask if SAVEMASK is nonzero. Return 0.
5359
This is the internal name for `sigsetjmp'. */
5460
extern int __sigsetjmp (struct __jmp_buf_tag __env[1], int __savemask) __THROWNL;
5561

62+
#ifdef WITH_RETURN_STACK_SUPPORT
63+
/* Safe version of `__sigsetjmp'. Instead of the return stack pointer,
64+
saves the marker into ENV. */
65+
extern int __safe_sigsetjmp (struct __jmp_buf_tag __env[1], int __savemask) __THROWNL;
66+
#endif
67+
5668
/* Store the calling environment in ENV, not saving the signal mask.
5769
Return 0. */
5870
extern int _setjmp (struct __jmp_buf_tag __env[1]) __THROWNL;
5971

72+
#ifdef WITH_RETURN_STACK_SUPPORT
73+
/* Safe version of `_setjmp'. Instead of the return stack pointer,
74+
saves the marker into ENV. */
75+
extern int _safe_setjmp (struct __jmp_buf_tag __env[1]) __THROWNL;
76+
#endif
77+
6078
/* Do not save the signal mask. This is equivalent to the `_setjmp'
6179
BSD function. */
6280
#define setjmp(env) _setjmp (env)
81+
#ifdef WITH_RETURN_STACK_SUPPORT
82+
# define safe_setjmp(env) _safe_setjmp (env)
83+
#endif
6384

6485

6586
/* Jump to the environment saved in ENV, making the
6687
`setjmp' call there return VAL, or 1 if VAL is 0. */
6788
extern void longjmp (struct __jmp_buf_tag __env[1], int __val)
6889
__THROWNL __attribute__ ((__noreturn__));
6990

91+
#ifdef WITH_RETURN_STACK_SUPPORT
92+
/* Safe version of `longjmp'. Restores the return stack pointer by
93+
unwinding the return stack until the marker is reached. */
94+
extern void safe_longjmp (struct __jmp_buf_tag __env[1], int __val)
95+
__THROWNL __attribute__ ((__noreturn__));
96+
#endif
97+
7098
#if defined __USE_MISC || defined __USE_XOPEN
7199
/* Same. Usually `_longjmp' is used with `_setjmp', which does not save
72100
the signal mask. But it is how ENV was saved that determines whether
73101
`longjmp' restores the mask; `_longjmp' is just an alias. */
74102
extern void _longjmp (struct __jmp_buf_tag __env[1], int __val)
75103
__THROWNL __attribute__ ((__noreturn__));
104+
105+
# ifdef WITH_RETURN_STACK_SUPPORT
106+
/* Safe version of `_longjmp'. Restores the return stack pointer by
107+
unwinding the return stack until the marker is reached. */
108+
extern void _safe_longjmp (struct __jmp_buf_tag __env[1], int __val)
109+
__THROWNL __attribute__ ((__noreturn__));
110+
# endif
76111
#endif
77112

78113

@@ -86,12 +121,26 @@ typedef struct __jmp_buf_tag sigjmp_buf[1];
86121
signal mask if SAVEMASK is nonzero. Return 0. */
87122
# define sigsetjmp(env, savemask) __sigsetjmp (env, savemask)
88123

124+
# ifdef WITH_RETURN_STACK_SUPPORT
125+
/* Safe version of `sigsetjmp'. Identical to `sigsetjmp', but instead
126+
of the return stack pointer, saves the marker into ENV. */
127+
# define safe_sigsetjmp(env, savemask) __safe_sigsetjmp (env, savemask)
128+
# endif
129+
89130
/* Jump to the environment saved in ENV, making the
90131
sigsetjmp call there return VAL, or 1 if VAL is 0.
91132
Restore the signal mask if that sigsetjmp call saved it.
92133
This is just an alias `longjmp'. */
93134
extern void siglongjmp (sigjmp_buf __env, int __val)
94135
__THROWNL __attribute__ ((__noreturn__));
136+
137+
# ifdef WITH_RETURN_STACK_SUPPORT
138+
/* Safe version of `siglongjmp'. Identical to `siglongjmp', but
139+
restores the return stack pointer by unwinding the return stack
140+
until the marker is reached. */
141+
extern void safe_siglongjmp (sigjmp_buf __env, int __val)
142+
__THROWNL __attribute__ ((__noreturn__));
143+
# endif
95144
#endif /* Use POSIX. */
96145

97146

signal/Versions

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ libc {
2828
sigaction; sigaddset; sigaltstack; sigandset; sigblock; sigdelset;
2929
sigemptyset; sigfillset; siggetmask; siginterrupt; sigisemptyset;
3030
sigismember; siglongjmp; signal; sigorset; sigpause; sigpending;
31+
safe_siglongjmp;
3132
sigprocmask; sigreturn; sigsetmask; sigstack; sigsuspend; sigvec;
3233
sigwait; ssignal;
3334
}

sysdeps/aarch64/Makefile

+10
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,13 @@ endif
1212
ifeq ($(subdir),gmon)
1313
CFLAGS-mcount.c += -mgeneral-regs-only
1414
endif
15+
16+
ifeq ($(subdir),debug)
17+
defines += -DWITH_RETURN_STACK_SUPPORT
18+
sysdep_routines += ____safe_longjmp_chk
19+
endif
20+
21+
ifeq ($(subdir),setjmp)
22+
defines += -DWITH_RETURN_STACK_SUPPORT
23+
sysdep_routines += safe_setjmp __safe_longjmp
24+
endif

sysdeps/aarch64/__safe_longjmp.S

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/* Return stack-safe version of longjmp for ARM64.
2+
3+
Copyright (C) 1997-2018 Free Software Foundation, Inc.
4+
5+
Return stack support added by
6+
Philipp Zieris <[email protected]>
7+
Copyright (C) 2018 Fraunhofer AISEC.
8+
9+
This program is free software; you can redistribute it and/or
10+
modify it under the terms of the GNU Lesser General Public License as
11+
published by the Free Software Foundation; either version 2.1 of the
12+
License, or (at your option) any later version.
13+
14+
This program is distributed in the hope that it will be useful,
15+
but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17+
Lesser General Public License for more details.
18+
19+
You should have received a copy of the GNU Lesser General Public
20+
License along with the GNU C Library. If not, see
21+
<http://www.gnu.org/licenses/>. */
22+
23+
#include <sysdep.h>
24+
#include <jmpbuf-offsets.h>
25+
#include <stap-probe.h>
26+
27+
/* Safe version of __longjmp. Restores the return stack pointer
28+
by unwinding the return stack until the marker is reached.
29+
__safe_longjmp(jmpbuf, val) */
30+
31+
ENTRY (__safe_longjmp)
32+
cfi_def_cfa(x0, 0)
33+
cfi_offset(x19, JB_X19<<3)
34+
cfi_offset(x20, JB_X20<<3)
35+
cfi_offset(x21, JB_X21<<3)
36+
cfi_offset(x22, JB_X22<<3)
37+
cfi_offset(x23, JB_X23<<3)
38+
cfi_offset(x24, JB_X24<<3)
39+
cfi_offset(x25, JB_X25<<3)
40+
cfi_offset(x26, JB_X26<<3)
41+
cfi_offset(x27, JB_X27<<3)
42+
cfi_offset(x29, JB_X29<<3)
43+
cfi_offset(x30, JB_LR<<3)
44+
45+
cfi_offset( d8, JB_D8<<3)
46+
cfi_offset( d9, JB_D9<<3)
47+
cfi_offset(d10, JB_D10<<3)
48+
cfi_offset(d11, JB_D11<<3)
49+
cfi_offset(d12, JB_D12<<3)
50+
cfi_offset(d13, JB_D13<<3)
51+
cfi_offset(d14, JB_D14<<3)
52+
cfi_offset(d15, JB_D15<<3)
53+
54+
DELOUSE (0)
55+
56+
ldp x19, x20, [x0, #JB_X19<<3]
57+
ldp x21, x22, [x0, #JB_X21<<3]
58+
ldp x23, x24, [x0, #JB_X23<<3]
59+
ldp x25, x26, [x0, #JB_X25<<3]
60+
ldr x27, [x0, #JB_X27<<3]
61+
#ifdef PTR_DEMANGLE
62+
ldp x29, x4, [x0, #JB_X29<<3]
63+
PTR_DEMANGLE (30, 4, 3, 2)
64+
#else
65+
ldp x29, x30, [x0, #JB_X29<<3]
66+
#endif
67+
/* longjmp probe takes 3 arguments, address of jump buffer as
68+
first argument (8@x0), return value as second argument (-4@x1),
69+
and target address (8@x30), respectively. */
70+
LIBC_PROBE (longjmp, 3, 8@x0, -4@x1, 8@x30)
71+
ldp d8, d9, [x0, #JB_D8<<3]
72+
ldp d10, d11, [x0, #JB_D10<<3]
73+
ldp d12, d13, [x0, #JB_D12<<3]
74+
ldp d14, d15, [x0, #JB_D14<<3]
75+
76+
/* Originally this was implemented with a series of
77+
.cfi_restore() directives.
78+
79+
The theory was that cfi_restore should revert to previous
80+
frame value is the same as the current value. In practice
81+
this doesn't work, even after cfi_restore() gdb continues
82+
to try to recover a previous frame value offset from x0,
83+
which gets stuffed after a few more instructions. The
84+
cfi_same_value() mechanism appears to work fine. */
85+
86+
cfi_same_value(x19)
87+
cfi_same_value(x20)
88+
cfi_same_value(x21)
89+
cfi_same_value(x22)
90+
cfi_same_value(x23)
91+
cfi_same_value(x24)
92+
cfi_same_value(x25)
93+
cfi_same_value(x26)
94+
cfi_same_value(x27)
95+
cfi_same_value(x29)
96+
cfi_same_value(x30)
97+
cfi_same_value(d8)
98+
cfi_same_value(d9)
99+
cfi_same_value(d10)
100+
cfi_same_value(d11)
101+
cfi_same_value(d12)
102+
cfi_same_value(d13)
103+
cfi_same_value(d14)
104+
cfi_same_value(d15)
105+
#ifdef PTR_DEMANGLE
106+
ldr x4, [x0, #JB_SP<<3]
107+
PTR_DEMANGLE (5, 4, 3, 2)
108+
#else
109+
ldr x5, [x0, #JB_SP<<3]
110+
#endif
111+
mov sp, x5
112+
/* Unwind return stack. */
113+
ldr x4, [x0, #JB_MARKER<<3]
114+
.Lrs_unwind:
115+
ldr x5, [x28, #-8]!
116+
cmp x4, x5
117+
bne .Lrs_unwind
118+
add x28, x28, #8
119+
120+
/* longjmp_target probe takes 3 arguments, address of jump buffer
121+
as first argument (8@x0), return value as second argument (-4@x1),
122+
and target address (8@x30), respectively. */
123+
LIBC_PROBE (longjmp_target, 3, 8@x0, -4@x1, 8@x30)
124+
cmp x1, #0
125+
mov x0, #1
126+
csel x0, x1, x0, ne
127+
/* Use br instead of ret because ret is guaranteed to mispredict */
128+
br x30
129+
END (__safe_longjmp)

sysdeps/aarch64/jmpbuf-offsets.h

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
#define JB_X26 7
2727
#define JB_X27 8
2828
#define JB_X28 9
29+
/* The safe versions of setjmp and longjmp don't need to save X28. They
30+
store the marker at this position instead. */
31+
#define JB_MARKER 9
2932
#define JB_X29 10
3033
#define JB_LR 11
3134
#define JB_SP 13

0 commit comments

Comments
 (0)