|
|
Message-ID: <3928cd66075c0f2b730c73f8f0bf8dc6@bu.edu>
Date: Sun, 20 Sep 2026 19:33:17 +0000
From: Khosro Moeini <khosro@...edu>
To: musl@...ts.openwall.com
Cc: khosro@...edu
Subject: [PATCH v3] x86_64: add CET shadow stack support
Enable CET shadow stack on x86_64 when the executable and all loaded
shared objects have the GNU_PROPERTY_X86_FEATURE_1_SHSTK bit set in
their .note.gnu.property note. If shadow stack is enabled for a process
dlopen of an object without the shadow stack property note fails.
Unlike glibc, this implementation does not check environment variables.
All the changes are guarded by SHSTK_ENABLED which is set through
the --enable-cet configuration option.
Signed-off-by: Khosro Moeini <khosro@...edu>
---
>> Thanks for the feedback. Regarding the concerns discussed in the older
>> thread:
>>
>> sigaltstack: The main program and its signal handlers use the same
>> shadow stack, so there won't be resource problems. Please see:
>> https://docs.kernel.org/next/x86/shstk.html#signal
>
> That is exactly the problem. It breaks the property that overflow of
> the normal stack cannot prevent the signal handler from running.
The size of the shadow stack is MIN(RLIMIT_STACK, 4 GB) and the shadow
stack only stores return addresses. Given the 16-byte stack alignment
in the x86_64 calling convention, in the extreme case where no extra
stack space is used, the shadow stack would be half empty when the
normal stack is overflowing. The Linux doc says:
"Because the shadow stack stores only return addresses, a large shadow
stack covers the condition that both the program stack and the signal
alternate stack run out."
>> ucontext: removed from POSIX and not supported in musl
>
> That just means we don't presently have it in musl; distros are using
> libucontext.
This shouldn't be an issue. If the library supports shadow stack and
has the shadow stack note, then shadow stack can be safely activated.
If the library does not support shadow stack it wouldn't have the
shadow stack note and thus shadow stack won't be activated for programs
using this library.
>> This patch is entirely hand-written.
>
> This is not the statement I asked for and is not reassuring.
No AI was used in the production of the code.
Since the last version, I simplified the vfork and sigsetjmp changes.
The shadow stack pointer is now stored in __ss[15] during setjmp and
sigsetjmp. This allows a simpler implementation for sigsetjmp relying
on the fact that __ss[15] is untouched when restoring the signal mask.
I ran libc-test with and without --enable-cet and also against the
unpatched version. The set of failing tests was the same in all cases.
Please CC on reply. Thanks.
Makefile | 11 +-
arch/generic/shstk_arch.h | 35 ++++++
arch/x86_64/note.s | 12 ++
arch/x86_64/shstk_arch.h | 40 ++++++
configure | 29 +++++
ldso/dynlink.c | 25 ++++
src/env/__libc_start_main.c | 12 ++
src/internal/shstk.h | 30 +++++
src/ldso/shstk.c | 119 ++++++++++++++++++
src/process/x86_64/{vfork.s => vfork.S} | 6 +
src/setjmp/x86_64/{longjmp.s => longjmp.S} | 18 +++
src/setjmp/x86_64/{setjmp.s => setjmp.S} | 4 +
.../x86_64/{sigsetjmp.s => sigsetjmp.S} | 21 ++++
13 files changed, 358 insertions(+), 4 deletions(-)
create mode 100644 arch/generic/shstk_arch.h
create mode 100644 arch/x86_64/note.s
create mode 100644 arch/x86_64/shstk_arch.h
create mode 100644 src/internal/shstk.h
create mode 100644 src/ldso/shstk.c
rename src/process/x86_64/{vfork.s => vfork.S} (51%)
rename src/setjmp/x86_64/{longjmp.s => longjmp.S} (49%)
rename src/setjmp/x86_64/{setjmp.s => setjmp.S} (85%)
rename src/signal/x86_64/{sigsetjmp.s => sigsetjmp.S} (39%)
diff --git a/Makefile b/Makefile
index 3ad88b3..0aa23b3 100644
--- a/Makefile
+++ b/Makefile
@@ -45,6 +45,7 @@ CPPFLAGS =
CFLAGS =
CFLAGS_AUTO = -Os -pipe
CFLAGS_C99FSE = -std=c99 -ffreestanding -nostdinc
+ASM_NOTE =
CFLAGS_ALL = $(CFLAGS_C99FSE)
CFLAGS_ALL += -D_XOPEN_SOURCE=700 -I$(srcdir)/arch/$(ARCH) -I$(srcdir)/arch/generic -Iobj/src/internal -I$(srcdir)/src/include -I$(srcdir)/src/internal -Iobj/include -I$(srcdir)/include
@@ -135,16 +136,18 @@ CC_CMD = $(CC) $(CFLAGS_ALL) -c -o $@ $<
# Choose invocation of assembler to be used
ifeq ($(ADD_CFI),yes)
- AS_CMD = LC_ALL=C awk -f $(srcdir)/tools/add-cfi.common.awk -f $(srcdir)/tools/add-cfi.$(ARCH).awk $< | $(CC) $(CFLAGS_ALL) -x assembler -c -o $@ -
+ AS_CMD = LC_ALL=C awk -f $(srcdir)/tools/add-cfi.common.awk -f $(srcdir)/tools/add-cfi.$(ARCH).awk $< $(ASM_NOTE) | $(CC) $(CFLAGS_ALL) -x assembler -c -o $@ -
else
- AS_CMD = $(CC_CMD)
+ AS_CMD = cat $< $(ASM_NOTE) | $(CC) $(CFLAGS_ALL) -x assembler -c -o $@ -
endif
+ASCPP_CMD = cat $< $(ASM_NOTE) | $(CC) $(CFLAGS_ALL) -iquote $(dir $<) -x assembler-with-cpp -c -o $@ -
+
obj/%.o: $(srcdir)/%.s
$(AS_CMD)
obj/%.o: $(srcdir)/%.S
- $(CC_CMD)
+ $(ASCPP_CMD)
obj/%.o: $(srcdir)/%.c $(GENH) $(IMPH)
$(CC_CMD)
@@ -153,7 +156,7 @@ obj/%.lo: $(srcdir)/%.s
$(AS_CMD)
obj/%.lo: $(srcdir)/%.S
- $(CC_CMD)
+ $(ASCPP_CMD)
obj/%.lo: $(srcdir)/%.c $(GENH) $(IMPH)
$(CC_CMD)
diff --git a/arch/generic/shstk_arch.h b/arch/generic/shstk_arch.h
new file mode 100644
index 0000000..3c28dd3
--- /dev/null
+++ b/arch/generic/shstk_arch.h
@@ -0,0 +1,35 @@
+#ifndef SHSTK_ARCH_H
+#define SHSTK_ARCH_H
+
+#if SHSTK_ENABLED
+
+#include <errno.h>
+#include <features.h>
+
+#define GNU_PROPERTY_FEATURE_1_AND 0xc0000002
+
+#define GNU_PROPERTY_FEATURE_1_SHSTK (1U << 1)
+
+#define ARCH_SHSTK_ENABLE 0x5001
+#define ARCH_SHSTK_LOCK 0x5003
+#define ARCH_SHSTK_STATUS 0x5005
+
+#define ARCH_SHSTK_SHSTK 1
+
+extern hidden unsigned long __shstk_status;
+
+static inline int __init_shstk_status(void)
+{
+ return -ENOTSUP;
+}
+
+static inline int __lock_shstk(void)
+{
+ return -ENOTSUP;
+}
+
+#define SHSTK_ENABLE() do { } while (0)
+
+#endif
+
+#endif
diff --git a/arch/x86_64/note.s b/arch/x86_64/note.s
new file mode 100644
index 0000000..cb03e5a
--- /dev/null
+++ b/arch/x86_64/note.s
@@ -0,0 +1,12 @@
+/* .note.gnu.property is 8-byte aligned in 64-bit objects */
+.section .note.gnu.property,"a"
+.balign 8
+.long 4 /* n_namesz: sizeof "GNU" */
+.long 16 /* n_descsz: one 8-byte-padded property */
+.long 5 /* n_type: NT_GNU_PROPERTY_TYPE_0 */
+.asciz "GNU"
+.long 0xc0000002 /* pr_type: GNU_PROPERTY_X86_FEATURE_1_AND */
+.long 4 /* pr_datasz */
+.long 2 /* GNU_PROPERTY_X86_FEATURE_1_SHSTK */
+.long 0 /* padding to 8-byte alignment */
+.previous
diff --git a/arch/x86_64/shstk_arch.h b/arch/x86_64/shstk_arch.h
new file mode 100644
index 0000000..c9c3561
--- /dev/null
+++ b/arch/x86_64/shstk_arch.h
@@ -0,0 +1,40 @@
+#ifndef SHSTK_ARCH_H
+#define SHSTK_ARCH_H
+
+#if SHSTK_ENABLED
+
+#include "syscall.h"
+
+#define GNU_PROPERTY_FEATURE_1_AND 0xc0000002
+
+#define GNU_PROPERTY_FEATURE_1_SHSTK (1U << 1)
+
+#define ARCH_SHSTK_ENABLE 0x5001
+#define ARCH_SHSTK_LOCK 0x5003
+#define ARCH_SHSTK_STATUS 0x5005
+
+#define ARCH_SHSTK_SHSTK 1
+
+extern hidden unsigned long __shstk_status;
+
+static inline int __init_shstk_status(void)
+{
+ return syscall(SYS_arch_prctl, ARCH_SHSTK_STATUS, &__shstk_status);
+}
+
+static inline int __lock_shstk(void)
+{
+ return syscall(SYS_arch_prctl, ARCH_SHSTK_LOCK, -1UL);
+}
+
+#define SHSTK_ENABLE() do { \
+ unsigned long ret; \
+ __asm__ __volatile__ ( "syscall" \
+ : "=a"(ret) \
+ : "a"(SYS_arch_prctl), "D"(ARCH_SHSTK_ENABLE), "S"(ARCH_SHSTK_SHSTK) \
+ : "rcx", "r11", "memory" ); \
+ } while (0)
+
+#endif
+
+#endif
diff --git a/configure b/configure
index bc9fbe4..a278c81 100755
--- a/configure
+++ b/configure
@@ -34,6 +34,7 @@ Optional features:
--enable-wrapper=... build given musl toolchain wrapper [auto]
--disable-shared inhibit building shared library [enabled]
--disable-static inhibit building static library [enabled]
+ --enable-cet build with CET shadow stack support [disabled]
Optional packages:
--with-malloc=... choose malloc implementation [mallocng]
@@ -142,6 +143,7 @@ static=yes
wrapper=auto
gcc_wrapper=no
clang_wrapper=no
+cet=no
malloc_dir=mallocng
for arg ; do
@@ -172,6 +174,8 @@ case "$arg" in
--disable-wrapper|--enable-wrapper=no) wrapper=no ;;
--enable-gcc-wrapper|--enable-gcc-wrapper=yes) wrapper=yes ; gcc_wrapper=yes ;;
--disable-gcc-wrapper|--enable-gcc-wrapper=no) wrapper=no ;;
+--enable-cet|--enable-cet=yes) cet=yes ;;
+--disable-cet|--enable-cet=no) cet=no ;;
--with-malloc=*) malloc_dir=${arg#*=} ;;
--enable-*|--disable-*|--with-*|--without-*|--*dir=*) ;;
--host=*|--target=*) target=${arg#*=} ;;
@@ -759,6 +763,30 @@ fi
test "$SUBARCH" \
&& printf "configured for %s variant: %s\n" "$ARCH" "$ARCH$SUBARCH"
+ASM_NOTE=
+if test "$cet" = yes ; then
+
+if test "$ARCH" != "x86_64" ; then
+fail "$0: error: --enable-cet is only supported on x86_64"
+fi
+
+tryflag CFLAGS_AUTO -fcf-protection=return \
+|| fail "$0: error: --enable-cet requires compiler support for -fcf-protection"
+
+printf "checking whether assembler supports rdssp/incssp instructions... "
+echo "__asm__(\"xor %eax,%eax ; rdsspq %rax ; incsspq %rax\");" > "$tmpc"
+if $CC -c -o /dev/null "$tmpc" >/dev/null 2>&1 ; then
+printf "yes\n"
+else
+printf "no\n"
+fail "$0: error: --enable-cet requires assembler support for rdssp/incssp"
+fi
+
+CFLAGS_AUTO="$CFLAGS_AUTO -DSHSTK_ENABLED=1"
+
+ASM_NOTE="arch/$ARCH/note.s"
+fi
+
#
# Some archs (powerpc) have different possible long double formats
# that the compiler can be configured for. The logic for whether this
@@ -820,6 +848,7 @@ CFLAGS_AUTO = $CFLAGS_AUTO
CFLAGS_C99FSE = $CFLAGS_C99FSE
CFLAGS_MEMOPS = $CFLAGS_MEMOPS
CFLAGS_NOSSP = $CFLAGS_NOSSP
+ASM_NOTE = $ASM_NOTE
CPPFLAGS = $CPPFLAGS
LDFLAGS = $LDFLAGS
LDFLAGS_AUTO = $LDFLAGS_AUTO
diff --git a/ldso/dynlink.c b/ldso/dynlink.c
index 10471b2..61c989a 100644
--- a/ldso/dynlink.c
+++ b/ldso/dynlink.c
@@ -23,6 +23,7 @@
#include "fork_impl.h"
#include "libc.h"
#include "dynlink.h"
+#include "shstk.h"
static size_t ldso_page_size;
/* libc.h may have defined a macro for dynamic PAGE_SIZE already, but
@@ -702,6 +703,9 @@ static void *map_library(int fd, struct dso *dso)
size_t dyn=0;
size_t tls_image=0;
size_t i;
+#if SHSTK_ENABLED
+ Phdr *gnu_prop_ph = NULL;
+#endif
ssize_t l = read(fd, buf, sizeof buf);
eh = buf;
@@ -741,6 +745,10 @@ static void *map_library(int fd, struct dso *dso)
ph->p_memsz < DEFAULT_STACK_MAX ?
ph->p_memsz : DEFAULT_STACK_MAX;
}
+#if SHSTK_ENABLED
+ } else if (ph->p_type == PT_GNU_PROPERTY) {
+ gnu_prop_ph = ph;
+#endif
}
if (ph->p_type != PT_LOAD) continue;
nsegs++;
@@ -862,6 +870,13 @@ done_mapping:
dso->base = base;
dso->dynv = laddr(dso, dyn);
if (dso->tls.size) dso->tls.image = laddr(dso, tls_image);
+#if SHSTK_ENABLED
+ int ret = __try_update_feature_1_and((size_t)base, gnu_prop_ph);
+ if (ret) {
+ errno = ret;
+ goto error;
+ }
+#endif
free(allocated_buf);
return map;
noexec:
@@ -1455,6 +1470,10 @@ static void kernel_mapped_dso(struct dso *p)
ph->p_memsz < DEFAULT_STACK_MAX ?
ph->p_memsz : DEFAULT_STACK_MAX;
}
+#if SHSTK_ENABLED
+ } else if (ph->p_type == PT_GNU_PROPERTY) {
+ __update_feature_1_and((size_t)p->base, ph);
+#endif
}
if (ph->p_type != PT_LOAD) continue;
if (ph->p_vaddr < min_addr)
@@ -1637,6 +1656,12 @@ void __init_tls(size_t *auxv)
{
}
+#if SHSTK_ENABLED
+void __init_feature_1_and(void)
+{
+}
+#endif
+
static void update_tls_size()
{
libc.tls_cnt = tls_cnt;
diff --git a/src/env/__libc_start_main.c b/src/env/__libc_start_main.c
index c5b277b..7a1e960 100644
--- a/src/env/__libc_start_main.c
+++ b/src/env/__libc_start_main.c
@@ -6,6 +6,8 @@
#include "syscall.h"
#include "atomic.h"
#include "libc.h"
+#include "shstk.h"
+#include "shstk_arch.h"
static void dummy(void) {}
weak_alias(dummy, _init);
@@ -89,6 +91,16 @@ int __libc_start_main(int (*main)(int,char **,char **), int argc, char **argv,
static int libc_start_main_stage2(int (*main)(int,char **,char **), int argc, char **argv)
{
char **envp = argv+argc+1;
+
+#if SHSTK_ENABLED
+ __init_feature_1_and();
+ if (__feature_1_and & GNU_PROPERTY_FEATURE_1_SHSTK) {
+ SHSTK_ENABLE();
+ __lock_shstk();
+ }
+ __init_shstk_status();
+#endif
+
__libc_start_init();
/* Pass control to the application */
diff --git a/src/internal/shstk.h b/src/internal/shstk.h
new file mode 100644
index 0000000..8d97c4a
--- /dev/null
+++ b/src/internal/shstk.h
@@ -0,0 +1,30 @@
+#ifndef SHSTK_H
+#define SHSTK_H
+
+#if SHSTK_ENABLED
+
+#include <elf.h>
+#include <stddef.h>
+#include <features.h>
+
+#if ULONG_MAX == 0xffffffff
+typedef Elf32_Addr Addr;
+typedef Elf32_Phdr Phdr;
+typedef Elf32_Nhdr Nhdr;
+#else
+typedef Elf64_Addr Addr;
+typedef Elf64_Phdr Phdr;
+typedef Elf64_Nhdr Nhdr;
+#endif
+
+extern hidden unsigned __feature_1_and;
+
+hidden void __init_feature_1_and(void);
+
+hidden void __update_feature_1_and(size_t base, Phdr *ph);
+
+hidden int __try_update_feature_1_and(size_t base, Phdr *ph);
+
+#endif
+
+#endif
diff --git a/src/ldso/shstk.c b/src/ldso/shstk.c
new file mode 100644
index 0000000..7185770
--- /dev/null
+++ b/src/ldso/shstk.c
@@ -0,0 +1,119 @@
+#if SHSTK_ENABLED
+
+#include <errno.h>
+#include <string.h>
+#include <stddef.h>
+#include "libc.h"
+#include "shstk.h"
+#include "shstk_arch.h"
+
+hidden unsigned __feature_1_and = -1;
+hidden unsigned long __shstk_status = 0;
+
+#define ALIGNMENT sizeof(Addr)
+#define ALIGN_UP(x,y) ((x)+(y)-1 & -(y))
+#define DESC_HDR_SIZE 8
+
+/* gnu properties are a sequence of type/size/value */
+static unsigned get_feature_from_note(size_t p, size_t len)
+{
+ size_t end = p + len;
+ while (p + DESC_HDR_SIZE <= end) {
+ uint32_t type, datasz;
+ type = *(uint32_t *)p;
+ p += sizeof(uint32_t);
+ datasz = *(uint32_t *)p;
+ p += sizeof(uint32_t);
+ if (p + datasz > end) return 0;
+ if (type == GNU_PROPERTY_FEATURE_1_AND) {
+ uint32_t feature;
+ if (datasz != 4) return 0;
+ feature = *(uint32_t *)p;
+ return feature;
+ }
+ /* kernel v6.18 /fs/bifmt_efl.c:758 says: */
+ /* Properties are supposed to be unique and sorted on pr_type: */
+ if (type > GNU_PROPERTY_FEATURE_1_AND) return 0;
+ p += ALIGN_UP(datasz, ALIGNMENT);
+ }
+ return 0;
+}
+
+static unsigned get_feature_from_header(size_t base, Phdr *ph)
+{
+ if (!ph || ph->p_align != ALIGNMENT) return 0;
+ size_t end = base + ph->p_vaddr + ph->p_memsz;
+ size_t p = base + ph->p_vaddr;
+ while (p + sizeof(Nhdr) <= end) {
+ Nhdr *nh = (Nhdr *)p;
+ size_t desc_offset = ALIGN_UP(sizeof(Nhdr) + nh->n_namesz, ALIGNMENT);
+ if (nh->n_namesz == 4
+ && nh->n_type == NT_GNU_PROPERTY_TYPE_0
+ && p + desc_offset + nh->n_descsz <= end
+ && !memcmp((void *)(p + sizeof(Nhdr)), "GNU", 4)) {
+ return get_feature_from_note(p + desc_offset, nh->n_descsz);
+ }
+ p += desc_offset;
+ p += ALIGN_UP(nh->n_descsz, ALIGNMENT);
+ }
+ return 0;
+}
+
+/*
+ * four cases here:
+ * (1) feature not set, shstk active -> impossible
+ * (2) feature not set, shstk not active -> no update needed
+ * (3) feature set, shstk not active -> initial loading, should update feature
+ * (4) feature set, shstk active -> dlopen, should fail if no shstk note
+ */
+hidden int __try_update_feature_1_and(size_t base, Phdr *ph)
+{
+ if (!(__feature_1_and & GNU_PROPERTY_FEATURE_1_SHSTK))
+ return 0;
+ /* feature set */
+ unsigned long dso_features = get_feature_from_header(base, ph);
+ if (!(__shstk_status & ARCH_SHSTK_SHSTK)) {
+ __feature_1_and &= dso_features;
+ return 0;
+ }
+ /* feature set, shstk active -> dlopen */
+ if (dso_features & GNU_PROPERTY_FEATURE_1_SHSTK)
+ return 0;
+ return ENOTSUP;
+}
+
+hidden void __update_feature_1_and(size_t base, Phdr *ph)
+{
+ __feature_1_and &= get_feature_from_header(base, ph);
+}
+
+#define AUX_CNT 38
+extern weak hidden const size_t _DYNAMIC[];
+
+static void static_init_feature_1_and(void)
+{
+ unsigned char *p;
+ Phdr *phdr, *gnu_prop_ph = NULL;
+ size_t base = 0;
+ size_t n;
+ size_t i, aux[AUX_CNT] = { 0 };
+
+ for (i=0; libc.auxv[i]; i+=2)
+ if (libc.auxv[i]<AUX_CNT) aux[libc.auxv[i]] = libc.auxv[i+1];
+
+ for (p=(void *)aux[AT_PHDR],n=aux[AT_PHNUM]; n; n--,p+=aux[AT_PHENT]) {
+ phdr = (void *)p;
+ if (phdr->p_type == PT_PHDR)
+ base = aux[AT_PHDR] - phdr->p_vaddr;
+ if (phdr->p_type == PT_DYNAMIC && _DYNAMIC)
+ base = (size_t)_DYNAMIC - phdr->p_vaddr;
+ if (phdr->p_type == PT_GNU_PROPERTY)
+ gnu_prop_ph = phdr;
+ }
+
+ __feature_1_and = get_feature_from_header(base, gnu_prop_ph);
+}
+
+weak_alias(static_init_feature_1_and, __init_feature_1_and);
+
+#endif
diff --git a/src/process/x86_64/vfork.s b/src/process/x86_64/vfork.S
similarity index 51%
rename from src/process/x86_64/vfork.s
rename to src/process/x86_64/vfork.S
index 9114439..8901fce 100644
--- a/src/process/x86_64/vfork.s
+++ b/src/process/x86_64/vfork.S
@@ -4,6 +4,12 @@ vfork:
pop %rdx
mov $58,%eax
syscall
+#if SHSTK_ENABLED
+ test %eax,%eax
+ jnz 1f /* return normally if in parent */
+ jmp *%rdx /* no error when in child so jmp */
+1:
+#endif
push %rdx
mov %rax,%rdi
.hidden __syscall_ret
diff --git a/src/setjmp/x86_64/longjmp.s b/src/setjmp/x86_64/longjmp.S
similarity index 49%
rename from src/setjmp/x86_64/longjmp.s
rename to src/setjmp/x86_64/longjmp.S
index 1b2661c..8eb4239 100644
--- a/src/setjmp/x86_64/longjmp.s
+++ b/src/setjmp/x86_64/longjmp.S
@@ -5,6 +5,24 @@
.type longjmp,@function
_longjmp:
longjmp:
+#if SHSTK_ENABLED
+ xor %ecx,%ecx
+ rdsspq %rcx
+ test %rcx,%rcx
+ jz 2f /* jmp if shstk is not active */
+ mov 72+120(%rdi),%rdx /* read target ssp from __ss[15] */
+ sub %rcx,%rdx
+ jb 2f /* jmp if target ssp is below current ssp */
+ shr $3,%rdx /* slots between current and target */
+ add $1,%rdx /* plus the slot setjmp itself returned to */
+1: mov $255,%rcx /* incssp adds at most 255 slots at a time */
+ cmp %rcx,%rdx
+ cmovb %rdx,%rcx
+ incsspq %rcx
+ sub %rcx,%rdx
+ ja 1b
+2:
+#endif
xor %eax,%eax
cmp $1,%esi /* CF = val ? 0 : 1 */
adc %esi,%eax /* eax = val + !val */
diff --git a/src/setjmp/x86_64/setjmp.s b/src/setjmp/x86_64/setjmp.S
similarity index 85%
rename from src/setjmp/x86_64/setjmp.s
rename to src/setjmp/x86_64/setjmp.S
index d95e485..2b43edb 100644
--- a/src/setjmp/x86_64/setjmp.s
+++ b/src/setjmp/x86_64/setjmp.S
@@ -18,5 +18,9 @@ setjmp:
mov %rdx,48(%rdi)
mov (%rsp),%rdx /* save return addr ptr for new rip */
mov %rdx,56(%rdi)
+#if SHSTK_ENABLED
+ rdsspq %rax
+ mov %rax,72+120(%rdi) /* store ssp in __ss[15] = (72 + 120) */
+#endif
xor %eax,%eax /* always return 0 */
ret
diff --git a/src/signal/x86_64/sigsetjmp.s b/src/signal/x86_64/sigsetjmp.S
similarity index 39%
rename from src/signal/x86_64/sigsetjmp.s
rename to src/signal/x86_64/sigsetjmp.S
index 9a7695f..d300738 100644
--- a/src/signal/x86_64/sigsetjmp.s
+++ b/src/signal/x86_64/sigsetjmp.S
@@ -18,7 +18,28 @@ __sigsetjmp:
mov %eax,%esi
mov 72+8(%rbx),%rbx
+#if SHSTK_ENABLED
+ /*
+ * ssp is read and stored by sigsetjmp in __ss[15].
+ * longjmp restores ssp to __ss[15] + 8, which is
+ * the ssp in the caller of setjmp. This works since
+ * the caller of setjmp must not return before longjmp
+ * is called. However, sigsetjmp does not follow this
+ * rule and breaks things. The following code makes
+ * sure sigsetjmp does not return before longjmp.
+ */
+ test %eax,%eax
+ jz 2f
+#endif
.hidden __sigsetjmp_tail
jmp __sigsetjmp_tail
+#if SHSTK_ENABLED
+2: subq $8,%rsp
+ call __sigsetjmp_tail
+ addq $8,%rsp
+ pop %rdx
+ jmp *%rdx
+#endif
+
1: jmp setjmp@PLT
--
2.49.1
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.