Submitter | H.J. Lu |
---|---|
Date | March 30, 2018, 5:41 p.m. |
Message ID | <CAMe9rOpj6nYdP33emmepZ+KWFG2JDVNsHn1bve3oo5WQyFmduA@mail.gmail.com> |
Download | mbox | patch |
Permalink | /patch/26516/ |
State | New |
Headers | show |
Comments
On 03/30/2018 12:41 PM, H.J. Lu wrote: > On Thu, Mar 29, 2018 at 1:20 PM, Florian Weimer <fw@deneb.enyo.de> wrote: >> * H. J. Lu: >> >>> On Thu, Mar 29, 2018 at 1:15 PM, Florian Weimer <fw@deneb.enyo.de> wrote: >>>> * H. J. Lu: >>>> >>>>> You need to make a choice. You either don't introduce a new symbol >>>>> version or don't save shadow stack for thread cancellation. You >>>>> can't have both. >>>> I don't understand. We have room to save the shadow stack pointer in >>>> the existing struct. >>> No, we don't have room in struct pthread_unwind_buf: >>> >>> Note: There is an unused pointer space in pthread_unwind_buf_data. But >>> it isn't suitable for saving and restoring shadow stack register since >>> x32 is a 64-bit process with 32-bit software pointer and kernel may >>> place x32 shadow stack above 4GB. We need to save and restore 64-bit >>> shadow stack register for x32. >> We have for void * fields. They are subsequently overwritten by >> __pthread_register_cancel. But __sigsetjmp can write to them first >> without causing any harm. We just need a private __longjmp_cancel >> that doesn't restore the shadow stack pointer. > Here is the patch which does that. Any comments? OK, so I have reviewed https://github.com/hjl-tools/glibc/ hjl/cet/master, please confirm that this is the correct branch of the required implementation for CET. It really helps to review the rest of the patch set, you should be preparing this as a patch set instead of having it reviewed one-at-a-time. This issue was already raised in the thread with Zack. Architecture: * We avoid a "flag day" with feature_1 TCB flag to switch to a new ABI, which we have discussed is a fragile process which should be avoided if a supportable alternative solution exists. * We avoid versioned symbols, this makes CET backportable, and this has a bigger benefit for long-term stable distributions. * A key design problem has been that cancellation jump buffers within glibc are truncated to optimize on-stack size, and this means that setjmp could write beyond the structure because setjmp now tries to save the shadowstack pointer into space that the cancellation jump buffer did not allocate. For the record this optimization seems premature and I'm sad we did it, this is a lesson we should learn from. * We have all agreed to the following concepts: * The cancellation process, in particular the unwinder, never returns from any of the functions we call, it just keeps calling into the unwinder to jump to the next unwound cancellation function all the way to the thread start routine. Therefore because we never return from one of these functions we never need to restore the shadow stack, and consequently wherever it is stored in the cancellation jump buffer can be overwritten if we need the space (it's a dead store). * The corollary to this is that function calls made from cancellation handlers will continue to advance the shadowstack from the deepest point at which cancellation is initiated from. This means that the depth of the shadowstack doesn't match the depth of the real stack while we are unwinding. I don't know if this will have consequences on analysis tooling or not, or debug tools during unwinding. It's a fairly advanced situation and corner case, and restoring the shadowstack is not useful becuase we don't need it and simplifies the implementation. * The cancellation jump buffer has private data used for chaining the cancel jump buffers together such that the custom unwinder can follow them and call them in sequence. This space constitutes 4 void *'s which is space that setjmp can write to, because we will just overwrite it when we register the cancel handler. * If the new shadowstack-enabled setjmp stores the shadowstack pointer into the space taken by the 4 void*'s then we won't overflow the stack, and we don't need to change the layout of the cancellation jump buffer. The 4 void*'s are sufficient, even for x32 to write a 64-bit shadow stack address. * After fixing the cancellation jump buffers the following work needs to be reviewed: * Add feature_1 in tcb head to track CET status and make it easily available to runtime for checking. * Save and restore shadowstack in setjmp/longjmp. * Add CET support to ld.so et. al. and track runtime status. * Adjust vfork for shadow stack usage. * Add ENDBR or NOTRACK where required in assembly. * CET and makecontext incompatible. - Probably need to discuss which default is appropriate. - Should the user get CET automatically disabled in makecontext() et. al. silently? - Should your current solution, which is to error out during the build, and require flag changes, be the default? This forces the user to review the security for their application. * prctl for CET. * The work to review after this patch appears to be less contentious in terms of the kinds of changes that are required. Most of the changes are internal details of enabling CET and not ABI details, with the exception of the possible pain we might cause with makecontext() being unsupported and what default position to take there. Design: * Overall the implementation looks exactly how I might expect it to look, but some of the math that places the shadowstack pointer appears to need either commenting or fixing because I don't understand it. You need to make it easy for me to see that we have placed the shadowstack pointer into the 4 pad words. Details: * One comment needs filling out a bit more, noted below. > 0001-x86-Use-pad-in-pthread_unwind_buf-to-preserve-shadow.patch > > > From f222537447f5ec879427f318b7c0396362b7453a Mon Sep 17 00:00:00 2001 > From: "H.J. Lu" <hjl.tools@gmail.com> > Date: Sat, 24 Feb 2018 17:23:54 -0800 > Subject: [PATCH] x86: Use pad in pthread_unwind_buf to preserve shadow stack > register > > The pad array in struct pthread_unwind_buf is used by setjmp to save > shadow stack register. We assert that size of struct pthread_unwind_buf > is no less than offset of shadow stack pointer + shadow stack pointer > size. > OK. > Since functions, like LIBC_START_MAIN, START_THREAD_DEFN as well as > these with thread cancellation, call setjmp, but never return after > __libc_unwind_longjmp, __libc_unwind_longjmp, which is defined as > __libc_longjmp on x86, doesn't need to restore shadow stack register. OK. > __libc_longjmp, which is a private interface for thread cancellation > implementation in libpthread, is changed to call __longjmp_cancel, > instead of __longjmp. __longjmp_cancel is a new internal function > in libc, which is similar to __longjmp, but doesn't restore shadow > stack register. OK. Good. I like the use of a __longjmp_cancel name to call out what's going on in the API (clear semantics). > > The compatibility longjmp and siglongjmp in libpthread.so are changed > to call __libc_siglongjmp, instead of __libc_longjmp, so that they will > restore shadow stack register. OK. > > Tested with build-many-glibcs.py. > > * nptl/pthread_create.c (START_THREAD_DEFN): Clear previous > handlers after setjmp. > * setjmp/longjmp.c (__libc_longjmp): Don't define alias if > defined. > * sysdeps/unix/sysv/linux/x86/setjmpP.h (_JUMP_BUF_SIGSET_NSIG): > Changed to 97. > * sysdeps/x86/Makefile (sysdep_routines): Add __longjmp_cancel. > * sysdeps/x86/__longjmp_cancel.S: New file. > * sysdeps/x86/longjmp.c: Likewise. > * sysdeps/x86/nptl/pt-longjmp.c: Likewise. This looks much better. > --- > nptl/pthread_create.c | 9 ++-- > setjmp/longjmp.c | 2 + > sysdeps/unix/sysv/linux/x86/setjmpP.h | 4 +- > sysdeps/x86/Makefile | 4 ++ > sysdeps/x86/__longjmp_cancel.S | 20 ++++++++ > sysdeps/x86/longjmp.c | 45 ++++++++++++++++ > sysdeps/x86/nptl/pt-longjmp.c | 97 +++++++++++++++++++++++++++++++++++ > 7 files changed, 176 insertions(+), 5 deletions(-) > create mode 100644 sysdeps/x86/__longjmp_cancel.S > create mode 100644 sysdeps/x86/longjmp.c > create mode 100644 sysdeps/x86/nptl/pt-longjmp.c > > diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c > index caaf07c134..1c5b3780c6 100644 > --- a/nptl/pthread_create.c > +++ b/nptl/pthread_create.c > @@ -427,12 +427,15 @@ START_THREAD_DEFN > compilers without that support we do use setjmp. */ > struct pthread_unwind_buf unwind_buf; > > - /* No previous handlers. */ > + int not_first_call; > + not_first_call = setjmp ((struct __jmp_buf_tag *) unwind_buf.cancel_jmp_buf); > + > + /* No previous handlers. NB: This must be done after setjmp since > + the same space may be used by setjmp to store extra data which > + should never be used by __libc_unwind_longjmp. */ Suggest: ~~~ No previous handlers. NB: This must be done after setjmp since the private space in the unwind jump buffer may overlap space used by setjmp to store extra architecture-specific information which is never be used by the cancellation-specific __libc_unwind_longjmp. The private space is allowed to overlap because the unwinder never has to return through any of the jumped-to call frames, and thus only a minimum amount of saved data need be stored, and for example, need not include the process signal mask information. This is all an optimization to reduce stack usage when pushing cancellation handlers. ~~~ > unwind_buf.priv.data.prev = NULL; > unwind_buf.priv.data.cleanup = NULL; > > - int not_first_call; > - not_first_call = setjmp ((struct __jmp_buf_tag *) unwind_buf.cancel_jmp_buf); OK. > if (__glibc_likely (! not_first_call)) > { > /* Store the new cleanup handler info. */ > diff --git a/setjmp/longjmp.c b/setjmp/longjmp.c > index a2a7065a85..453889e103 100644 > --- a/setjmp/longjmp.c > +++ b/setjmp/longjmp.c > @@ -40,9 +40,11 @@ __libc_siglongjmp (sigjmp_buf env, int val) > } > > #ifndef __libc_siglongjmp > +# ifndef __libc_longjmp > /* __libc_longjmp is a private interface for cancellation implementation > in libpthread. */ > strong_alias (__libc_siglongjmp, __libc_longjmp) > +# endif OK. > weak_alias (__libc_siglongjmp, _longjmp) > weak_alias (__libc_siglongjmp, longjmp) > weak_alias (__libc_siglongjmp, siglongjmp) > diff --git a/sysdeps/unix/sysv/linux/x86/setjmpP.h b/sysdeps/unix/sysv/linux/x86/setjmpP.h > index c0ed767a0d..90a6bbcf32 100644 > --- a/sysdeps/unix/sysv/linux/x86/setjmpP.h > +++ b/sysdeps/unix/sysv/linux/x86/setjmpP.h > @@ -22,8 +22,8 @@ > #include <bits/types/__sigset_t.h> > > /* The biggest signal number + 1. As of kernel 4.14, x86 _NSIG is 64. > - Define it to 513 to leave some rooms for future use. */ > -#define _JUMP_BUF_SIGSET_NSIG 513 > + Define it to 97 to leave some rooms for future use. */ OK. > +#define _JUMP_BUF_SIGSET_NSIG 97 Please provide proof in the way of a comment or rewriting this constant to show that it places the shadow stack pointer on both x86_64 and x32 into the range of the private pad. Also, from commit f33632ccd1dec3217583fcfdd965afb62954203c, where did this math come from? ((_JUMP_BUF_SIGSET_NSIG - 1 + 7) / (8 * sizeof (unsigned long int))) Why the +7? > /* Number of longs to hold all signals. */ > #define _JUMP_BUF_SIGSET_NWORDS \ > ((_JUMP_BUF_SIGSET_NSIG - 1 + 7) / (8 * sizeof (unsigned long int))) > diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile > index 0d0326c21a..d25d6f0ae4 100644 > --- a/sysdeps/x86/Makefile > +++ b/sysdeps/x86/Makefile > @@ -8,3 +8,7 @@ sysdep-dl-routines += dl-get-cpu-features > tests += tst-get-cpu-features > tests-static += tst-get-cpu-features-static > endif > + > +ifeq ($(subdir),setjmp) > +sysdep_routines += __longjmp_cancel > +endif OK. > diff --git a/sysdeps/x86/__longjmp_cancel.S b/sysdeps/x86/__longjmp_cancel.S > new file mode 100644 > index 0000000000..b57dbfa376 > --- /dev/null > +++ b/sysdeps/x86/__longjmp_cancel.S > @@ -0,0 +1,20 @@ > +/* __longjmp_cancel for x86. > + Copyright (C) 2018 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <http://www.gnu.org/licenses/>. */ > + > +#define __longjmp __longjmp_cancel > +#include <__longjmp.S> OK. > diff --git a/sysdeps/x86/longjmp.c b/sysdeps/x86/longjmp.c > new file mode 100644 > index 0000000000..a53f31e1dd > --- /dev/null > +++ b/sysdeps/x86/longjmp.c > @@ -0,0 +1,45 @@ > +/* __libc_siglongjmp for x86. > + Copyright (C) 2018 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <http://www.gnu.org/licenses/>. */ > + > +#define __libc_longjmp __redirect___libc_longjmp > +#include <setjmp/longjmp.c> > +#undef __libc_longjmp > + > +extern void __longjmp_cancel (__jmp_buf __env, int __val) > + __attribute__ ((__noreturn__)) attribute_hidden; > + > +/* Since __libc_longjmp is a private interface for cancellation > + implementation in libpthread, there is no need to restore shadow > + stack register. */ > + > +void > +__libc_longjmp (sigjmp_buf env, int val) > +{ > + /* Perform any cleanups needed by the frames being unwound. */ > + _longjmp_unwind (env, val); OK. > + > + if (env[0].__mask_was_saved) > + /* Restore the saved signal mask. */ > + (void) __sigprocmask (SIG_SETMASK, > + (sigset_t *) &env[0].__saved_mask, > + (sigset_t *) NULL); OK. > + > + /* Call the machine-dependent function to restore machine state > + without shadow stack. */ > + __longjmp_cancel (env[0].__jmpbuf, val ?: 1); OK. > +} > diff --git a/sysdeps/x86/nptl/pt-longjmp.c b/sysdeps/x86/nptl/pt-longjmp.c > new file mode 100644 > index 0000000000..7eb8651cfe > --- /dev/null > +++ b/sysdeps/x86/nptl/pt-longjmp.c > @@ -0,0 +1,97 @@ > +/* ABI compatibility for 'longjmp' and 'siglongjmp' symbols in libpthread ABI. > + X86 version. > + Copyright (C) 18 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <http://www.gnu.org/licenses/>. */ > + > +/* <nptl/descr.h> has > + > +struct pthread_unwind_buf > +{ > + struct > + { > + __jmp_buf jmp_buf; > + int mask_was_saved; > + } cancel_jmp_buf[1]; > + > + union > + { > + void *pad[4]; > + struct > + { > + struct pthread_unwind_buf *prev; > + struct _pthread_cleanup_buffer *cleanup; > + int canceltype; > + } data; > + } priv; > +}; > + > + The pad array in struct pthread_unwind_buf is used by setjmp to save This appears to be SHADOW_STACK_POINTER_OFFSET in subsequent patches. However on your hjl/cet/master branch it appears that this offset is not defined to be *just after* the mask_was_saved? > + shadow stack register. Assert that size of struct pthread_unwind_buf > + is no less than offset of shadow stack pointer plus shadow stack > + pointer size. > + > + NB: setjmp is called in libpthread to save shadow stack register. But > + __libc_unwind_longjmp doesn't restore shadow stack register since they > + never return after longjmp. */ > + > +#include <pthreadP.h> > +#include <jmp_buf-ssp.h> > + > +#ifdef __x86_64__ > +# define SHADOW_STACK_POINTER_SIZE 8 > +#else > +# define SHADOW_STACK_POINTER_SIZE 4 > +#endif > + > +_Static_assert ((sizeof (struct pthread_unwind_buf) > + >= (SHADOW_STACK_POINTER_OFFSET > + + SHADOW_STACK_POINTER_SIZE)), > + "size of struct pthread_unwind_buf < " > + "(SHADOW_STACK_POINTER_OFFSET + SHADOW_STACK_POINTER_SIZE)"); OK. > + > +#include <shlib-compat.h> > + > +/* libpthread once had its own longjmp (and siglongjmp alias), though there > + was no apparent reason for it. There is no use in having a separate > + symbol in libpthread, but the historical ABI requires it. For static > + linking, there is no need to provide anything here--the libc version > + will be linked in. For shared library ABI compatibility, there must be > + longjmp and siglongjmp symbols in libpthread.so. > + > + With an IFUNC resolver, it would be possible to avoid the indirection, > + but the IFUNC resolver might run before the __libc_longjmp symbol has > + been relocated, in which case the IFUNC resolver would not be able to > + provide the correct address. */ > + > +#if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_22) > + > +static void __attribute__ ((noreturn, used)) > +longjmp_compat (jmp_buf env, int val) > +{ > + /* NB: We call __libc_siglongjmp, instead of __libc_longjmp, since > + __libc_longjmp is a private interface for cancellation which > + doesn't restore shadow stack register. */ > + __libc_siglongjmp (env, val); OK. > +} > + > +strong_alias (longjmp_compat, longjmp_alias) > +compat_symbol (libpthread, longjmp_alias, longjmp, GLIBC_2_0); > + > +strong_alias (longjmp_alias, siglongjmp_alias) > +compat_symbol (libpthread, siglongjmp_alias, siglongjmp, GLIBC_2_0); > + > +#endif > -- 2.14.3
On Thu, Apr 5, 2018 at 9:46 PM, Carlos O'Donell <carlos@redhat.com> wrote: > On 03/30/2018 12:41 PM, H.J. Lu wrote: >> On Thu, Mar 29, 2018 at 1:20 PM, Florian Weimer <fw@deneb.enyo.de> wrote: >>> * H. J. Lu: >>> >>>> On Thu, Mar 29, 2018 at 1:15 PM, Florian Weimer <fw@deneb.enyo.de> wrote: >>>>> * H. J. Lu: >>>>> >>>>>> You need to make a choice. You either don't introduce a new symbol >>>>>> version or don't save shadow stack for thread cancellation. You >>>>>> can't have both. >>>>> I don't understand. We have room to save the shadow stack pointer in >>>>> the existing struct. >>>> No, we don't have room in struct pthread_unwind_buf: >>>> >>>> Note: There is an unused pointer space in pthread_unwind_buf_data. But >>>> it isn't suitable for saving and restoring shadow stack register since >>>> x32 is a 64-bit process with 32-bit software pointer and kernel may >>>> place x32 shadow stack above 4GB. We need to save and restore 64-bit >>>> shadow stack register for x32. >>> We have for void * fields. They are subsequently overwritten by >>> __pthread_register_cancel. But __sigsetjmp can write to them first >>> without causing any harm. We just need a private __longjmp_cancel >>> that doesn't restore the shadow stack pointer. >> Here is the patch which does that. Any comments? > > OK, so I have reviewed https://github.com/hjl-tools/glibc/ hjl/cet/master, > please confirm that this is the correct branch of the required implementation > for CET. It really helps to review the rest of the patch set, you should be > preparing this as a patch set instead of having it reviewed one-at-a-time. > This issue was already raised in the thread with Zack. Thanks for your feedbacks. > Architecture: > > * We avoid a "flag day" with feature_1 TCB flag to switch to a new ABI, > which we have discussed is a fragile process which should be avoided if > a supportable alternative solution exists. > > * We avoid versioned symbols, this makes CET backportable, and this has a > bigger benefit for long-term stable distributions. > > * A key design problem has been that cancellation jump buffers within glibc > are truncated to optimize on-stack size, and this means that setjmp could > write beyond the structure because setjmp now tries to save the shadowstack > pointer into space that the cancellation jump buffer did not allocate. > For the record this optimization seems premature and I'm sad we did it, this > is a lesson we should learn from. > > * We have all agreed to the following concepts: > > * The cancellation process, in particular the unwinder, never returns from > any of the functions we call, it just keeps calling into the unwinder to > jump to the next unwound cancellation function all the way to the thread > start routine. Therefore because we never return from one of these functions > we never need to restore the shadow stack, and consequently wherever it is > stored in the cancellation jump buffer can be overwritten if we need the > space (it's a dead store). > > * The corollary to this is that function calls made from cancellation handlers > will continue to advance the shadowstack from the deepest point at which > cancellation is initiated from. This means that the depth of the shadowstack > doesn't match the depth of the real stack while we are unwinding. I don't > know if this will have consequences on analysis tooling or not, or debug > tools during unwinding. It's a fairly advanced situation and corner case, > and restoring the shadowstack is not useful becuase we don't need it and > simplifies the implementation. > > * The cancellation jump buffer has private data used for chaining the cancel > jump buffers together such that the custom unwinder can follow them and > call them in sequence. This space constitutes 4 void *'s which is space > that setjmp can write to, because we will just overwrite it when we register > the cancel handler. > > * If the new shadowstack-enabled setjmp stores the shadowstack pointer into > the space taken by the 4 void*'s then we won't overflow the stack, and we > don't need to change the layout of the cancellation jump buffer. The 4 void*'s > are sufficient, even for x32 to write a 64-bit shadow stack address. > > * After fixing the cancellation jump buffers the following work needs to be reviewed: > > * Add feature_1 in tcb head to track CET status and make it easily available > to runtime for checking. > > * Save and restore shadowstack in setjmp/longjmp. > > * Add CET support to ld.so et. al. and track runtime status. > > * Adjust vfork for shadow stack usage. > > * Add ENDBR or NOTRACK where required in assembly. > > * CET and makecontext incompatible. > - Probably need to discuss which default is appropriate. > - Should the user get CET automatically disabled in makecontext() et. al. silently? > - Should your current solution, which is to error out during the build, and require > flag changes, be the default? This forces the user to review the security for their > application. I'd like to reserve 4 slots in ucontext for shadow stack: https://github.com/hjl-tools/glibc/commit/9bf6aefa8fb45f8df140d42ce9cf890bb24076e1 It should be binary backward compatible. I will investigate if there is a way to support shadow stack with existing API. Otherwise, we need to add a new API for ucontext functions with shadow stack. > * prctl for CET. We have been experimenting different approaches to get the best implementation. I am expecting that this patch may change as we collect more data. > * The work to review after this patch appears to be less contentious in terms of > the kinds of changes that are required. Most of the changes are internal details > of enabling CET and not ABI details, with the exception of the possible pain we > might cause with makecontext() being unsupported and what default position to take > there. > > Design: > > * Overall the implementation looks exactly how I might expect it to look, but some > of the math that places the shadowstack pointer appears to need either commenting > or fixing because I don't understand it. You need to make it easy for me to see > that we have placed the shadowstack pointer into the 4 pad words. > > Details: > > * One comment needs filling out a bit more, noted below. > >> 0001-x86-Use-pad-in-pthread_unwind_buf-to-preserve-shadow.patch >> >> >> From f222537447f5ec879427f318b7c0396362b7453a Mon Sep 17 00:00:00 2001 >> From: "H.J. Lu" <hjl.tools@gmail.com> >> Date: Sat, 24 Feb 2018 17:23:54 -0800 >> Subject: [PATCH] x86: Use pad in pthread_unwind_buf to preserve shadow stack >> register >> >> The pad array in struct pthread_unwind_buf is used by setjmp to save >> shadow stack register. We assert that size of struct pthread_unwind_buf >> is no less than offset of shadow stack pointer + shadow stack pointer >> size. >> > > OK. > >> Since functions, like LIBC_START_MAIN, START_THREAD_DEFN as well as >> these with thread cancellation, call setjmp, but never return after >> __libc_unwind_longjmp, __libc_unwind_longjmp, which is defined as >> __libc_longjmp on x86, doesn't need to restore shadow stack register. > > OK. > >> __libc_longjmp, which is a private interface for thread cancellation >> implementation in libpthread, is changed to call __longjmp_cancel, >> instead of __longjmp. __longjmp_cancel is a new internal function >> in libc, which is similar to __longjmp, but doesn't restore shadow >> stack register. > > OK. Good. I like the use of a __longjmp_cancel name to call out what's > going on in the API (clear semantics). > >> >> The compatibility longjmp and siglongjmp in libpthread.so are changed >> to call __libc_siglongjmp, instead of __libc_longjmp, so that they will >> restore shadow stack register. > > OK. > >> >> Tested with build-many-glibcs.py. >> >> * nptl/pthread_create.c (START_THREAD_DEFN): Clear previous >> handlers after setjmp. >> * setjmp/longjmp.c (__libc_longjmp): Don't define alias if >> defined. >> * sysdeps/unix/sysv/linux/x86/setjmpP.h (_JUMP_BUF_SIGSET_NSIG): >> Changed to 97. >> * sysdeps/x86/Makefile (sysdep_routines): Add __longjmp_cancel. >> * sysdeps/x86/__longjmp_cancel.S: New file. >> * sysdeps/x86/longjmp.c: Likewise. >> * sysdeps/x86/nptl/pt-longjmp.c: Likewise. > > This looks much better. > >> --- >> nptl/pthread_create.c | 9 ++-- >> setjmp/longjmp.c | 2 + >> sysdeps/unix/sysv/linux/x86/setjmpP.h | 4 +- >> sysdeps/x86/Makefile | 4 ++ >> sysdeps/x86/__longjmp_cancel.S | 20 ++++++++ >> sysdeps/x86/longjmp.c | 45 ++++++++++++++++ >> sysdeps/x86/nptl/pt-longjmp.c | 97 +++++++++++++++++++++++++++++++++++ >> 7 files changed, 176 insertions(+), 5 deletions(-) >> create mode 100644 sysdeps/x86/__longjmp_cancel.S >> create mode 100644 sysdeps/x86/longjmp.c >> create mode 100644 sysdeps/x86/nptl/pt-longjmp.c >> >> diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c >> index caaf07c134..1c5b3780c6 100644 >> --- a/nptl/pthread_create.c >> +++ b/nptl/pthread_create.c >> @@ -427,12 +427,15 @@ START_THREAD_DEFN >> compilers without that support we do use setjmp. */ >> struct pthread_unwind_buf unwind_buf; >> >> - /* No previous handlers. */ >> + int not_first_call; >> + not_first_call = setjmp ((struct __jmp_buf_tag *) unwind_buf.cancel_jmp_buf); >> + >> + /* No previous handlers. NB: This must be done after setjmp since >> + the same space may be used by setjmp to store extra data which >> + should never be used by __libc_unwind_longjmp. */ > > Suggest: > ~~~ > No previous handlers. NB: This must be done after setjmp since > the private space in the unwind jump buffer may overlap space > used by setjmp to store extra architecture-specific information > which is never be used by the cancellation-specific > __libc_unwind_longjmp. > > The private space is allowed to overlap because the unwinder never > has to return through any of the jumped-to call frames, and thus > only a minimum amount of saved data need be stored, and for example, > need not include the process signal mask information. This is all > an optimization to reduce stack usage when pushing cancellation > handlers. > ~~~ Will fix it. >> unwind_buf.priv.data.prev = NULL; >> unwind_buf.priv.data.cleanup = NULL; >> >> - int not_first_call; >> - not_first_call = setjmp ((struct __jmp_buf_tag *) unwind_buf.cancel_jmp_buf); > > OK. > >> if (__glibc_likely (! not_first_call)) >> { >> /* Store the new cleanup handler info. */ >> diff --git a/setjmp/longjmp.c b/setjmp/longjmp.c >> index a2a7065a85..453889e103 100644 >> --- a/setjmp/longjmp.c >> +++ b/setjmp/longjmp.c >> @@ -40,9 +40,11 @@ __libc_siglongjmp (sigjmp_buf env, int val) >> } >> >> #ifndef __libc_siglongjmp >> +# ifndef __libc_longjmp >> /* __libc_longjmp is a private interface for cancellation implementation >> in libpthread. */ >> strong_alias (__libc_siglongjmp, __libc_longjmp) >> +# endif > > OK. > >> weak_alias (__libc_siglongjmp, _longjmp) >> weak_alias (__libc_siglongjmp, longjmp) >> weak_alias (__libc_siglongjmp, siglongjmp) >> diff --git a/sysdeps/unix/sysv/linux/x86/setjmpP.h b/sysdeps/unix/sysv/linux/x86/setjmpP.h >> index c0ed767a0d..90a6bbcf32 100644 >> --- a/sysdeps/unix/sysv/linux/x86/setjmpP.h >> +++ b/sysdeps/unix/sysv/linux/x86/setjmpP.h >> @@ -22,8 +22,8 @@ >> #include <bits/types/__sigset_t.h> >> >> /* The biggest signal number + 1. As of kernel 4.14, x86 _NSIG is 64. >> - Define it to 513 to leave some rooms for future use. */ >> -#define _JUMP_BUF_SIGSET_NSIG 513 >> + Define it to 97 to leave some rooms for future use. */ > > OK. > >> +#define _JUMP_BUF_SIGSET_NSIG 97 > > Please provide proof in the way of a comment or rewriting this constant > to show that it places the shadow stack pointer on both x86_64 and x32 > into the range of the private pad. sysdeps/x86/nptl/pt-longjmp.c has _Static_assert ((sizeof (struct pthread_unwind_buf) >= (SHADOW_STACK_POINTER_OFFSET + SHADOW_STACK_POINTER_SIZE)), "size of struct pthread_unwind_buf < " "(SHADOW_STACK_POINTER_OFFSET + SHADOW_STACK_POINTER_SIZE)"); If shadow stack pointer is saved in the offset bigger than the size of struct pthread_unwind_buf, assert will trigger at compile-time. > Also, from commit f33632ccd1dec3217583fcfdd965afb62954203c, > where did this math come from? > > ((_JUMP_BUF_SIGSET_NSIG - 1 + 7) / (8 * sizeof (unsigned long int))) > > Why the +7? _JUMP_BUF_SIGSET_NSIG is the biggest signal number + 1. _JUMP_BUF_SIGSET_NSIG - 1 gives the biggest signal number. _JUMP_BUF_SIGSET_NSIG - 1 + 7 rounds up to the number of bytes which are needed to store the biggest signal number. >> /* Number of longs to hold all signals. */ >> #define _JUMP_BUF_SIGSET_NWORDS \ >> ((_JUMP_BUF_SIGSET_NSIG - 1 + 7) / (8 * sizeof (unsigned long int))) >> diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile >> index 0d0326c21a..d25d6f0ae4 100644 >> --- a/sysdeps/x86/Makefile >> +++ b/sysdeps/x86/Makefile >> @@ -8,3 +8,7 @@ sysdep-dl-routines += dl-get-cpu-features >> tests += tst-get-cpu-features >> tests-static += tst-get-cpu-features-static >> endif >> + >> +ifeq ($(subdir),setjmp) >> +sysdep_routines += __longjmp_cancel >> +endif > > OK. > >> diff --git a/sysdeps/x86/__longjmp_cancel.S b/sysdeps/x86/__longjmp_cancel.S >> new file mode 100644 >> index 0000000000..b57dbfa376 >> --- /dev/null >> +++ b/sysdeps/x86/__longjmp_cancel.S >> @@ -0,0 +1,20 @@ >> +/* __longjmp_cancel for x86. >> + Copyright (C) 2018 Free Software Foundation, Inc. >> + This file is part of the GNU C Library. >> + >> + The GNU C Library is free software; you can redistribute it and/or >> + modify it under the terms of the GNU Lesser General Public >> + License as published by the Free Software Foundation; either >> + version 2.1 of the License, or (at your option) any later version. >> + >> + The GNU C Library is distributed in the hope that it will be useful, >> + but WITHOUT ANY WARRANTY; without even the implied warranty of >> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + Lesser General Public License for more details. >> + >> + You should have received a copy of the GNU Lesser General Public >> + License along with the GNU C Library; if not, see >> + <http://www.gnu.org/licenses/>. */ >> + >> +#define __longjmp __longjmp_cancel >> +#include <__longjmp.S> > > OK. > >> diff --git a/sysdeps/x86/longjmp.c b/sysdeps/x86/longjmp.c >> new file mode 100644 >> index 0000000000..a53f31e1dd >> --- /dev/null >> +++ b/sysdeps/x86/longjmp.c >> @@ -0,0 +1,45 @@ >> +/* __libc_siglongjmp for x86. >> + Copyright (C) 2018 Free Software Foundation, Inc. >> + This file is part of the GNU C Library. >> + >> + The GNU C Library is free software; you can redistribute it and/or >> + modify it under the terms of the GNU Lesser General Public >> + License as published by the Free Software Foundation; either >> + version 2.1 of the License, or (at your option) any later version. >> + >> + The GNU C Library is distributed in the hope that it will be useful, >> + but WITHOUT ANY WARRANTY; without even the implied warranty of >> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + Lesser General Public License for more details. >> + >> + You should have received a copy of the GNU Lesser General Public >> + License along with the GNU C Library; if not, see >> + <http://www.gnu.org/licenses/>. */ >> + >> +#define __libc_longjmp __redirect___libc_longjmp >> +#include <setjmp/longjmp.c> >> +#undef __libc_longjmp >> + >> +extern void __longjmp_cancel (__jmp_buf __env, int __val) >> + __attribute__ ((__noreturn__)) attribute_hidden; >> + >> +/* Since __libc_longjmp is a private interface for cancellation >> + implementation in libpthread, there is no need to restore shadow >> + stack register. */ >> + >> +void >> +__libc_longjmp (sigjmp_buf env, int val) >> +{ >> + /* Perform any cleanups needed by the frames being unwound. */ >> + _longjmp_unwind (env, val); > > OK. > >> + >> + if (env[0].__mask_was_saved) >> + /* Restore the saved signal mask. */ >> + (void) __sigprocmask (SIG_SETMASK, >> + (sigset_t *) &env[0].__saved_mask, >> + (sigset_t *) NULL); > > OK. > >> + >> + /* Call the machine-dependent function to restore machine state >> + without shadow stack. */ >> + __longjmp_cancel (env[0].__jmpbuf, val ?: 1); > > OK. > >> +} >> diff --git a/sysdeps/x86/nptl/pt-longjmp.c b/sysdeps/x86/nptl/pt-longjmp.c >> new file mode 100644 >> index 0000000000..7eb8651cfe >> --- /dev/null >> +++ b/sysdeps/x86/nptl/pt-longjmp.c >> @@ -0,0 +1,97 @@ >> +/* ABI compatibility for 'longjmp' and 'siglongjmp' symbols in libpthread ABI. >> + X86 version. >> + Copyright (C) 18 Free Software Foundation, Inc. >> + This file is part of the GNU C Library. >> + >> + The GNU C Library is free software; you can redistribute it and/or >> + modify it under the terms of the GNU Lesser General Public >> + License as published by the Free Software Foundation; either >> + version 2.1 of the License, or (at your option) any later version. >> + >> + The GNU C Library is distributed in the hope that it will be useful, >> + but WITHOUT ANY WARRANTY; without even the implied warranty of >> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + Lesser General Public License for more details. >> + >> + You should have received a copy of the GNU Lesser General Public >> + License along with the GNU C Library; if not, see >> + <http://www.gnu.org/licenses/>. */ >> + >> +/* <nptl/descr.h> has >> + >> +struct pthread_unwind_buf >> +{ >> + struct >> + { >> + __jmp_buf jmp_buf; >> + int mask_was_saved; >> + } cancel_jmp_buf[1]; >> + >> + union >> + { >> + void *pad[4]; >> + struct >> + { >> + struct pthread_unwind_buf *prev; >> + struct _pthread_cleanup_buffer *cleanup; >> + int canceltype; >> + } data; >> + } priv; >> +}; >> + >> + The pad array in struct pthread_unwind_buf is used by setjmp to save > > This appears to be SHADOW_STACK_POINTER_OFFSET in subsequent patches. > > However on your hjl/cet/master branch it appears that this offset is not > defined to be *just after* the mask_was_saved? sysdeps/unix/sysv/linux/x86/setjmpP.h has typedef struct { unsigned long int __val[_JUMP_BUF_SIGSET_NWORDS]; } __jmp_buf_sigset_t; typedef union { __sigset_t __saved_mask_compat; struct { __jmp_buf_sigset_t __saved_mask; /* Used for shadow stack pointer. */ unsigned long int __shadow_stack_pointer; } __saved; } __jmpbuf_arch_t; __shadow_stack_pointer is placed after __saved_mask, aka mask_was_saved. >> + shadow stack register. Assert that size of struct pthread_unwind_buf >> + is no less than offset of shadow stack pointer plus shadow stack >> + pointer size. >> + >> + NB: setjmp is called in libpthread to save shadow stack register. But >> + __libc_unwind_longjmp doesn't restore shadow stack register since they >> + never return after longjmp. */ >> + >> +#include <pthreadP.h> >> +#include <jmp_buf-ssp.h> >> + >> +#ifdef __x86_64__ >> +# define SHADOW_STACK_POINTER_SIZE 8 >> +#else >> +# define SHADOW_STACK_POINTER_SIZE 4 >> +#endif >> + >> +_Static_assert ((sizeof (struct pthread_unwind_buf) >> + >= (SHADOW_STACK_POINTER_OFFSET >> + + SHADOW_STACK_POINTER_SIZE)), >> + "size of struct pthread_unwind_buf < " >> + "(SHADOW_STACK_POINTER_OFFSET + SHADOW_STACK_POINTER_SIZE)"); > > OK. > >> + >> +#include <shlib-compat.h> >> + >> +/* libpthread once had its own longjmp (and siglongjmp alias), though there >> + was no apparent reason for it. There is no use in having a separate >> + symbol in libpthread, but the historical ABI requires it. For static >> + linking, there is no need to provide anything here--the libc version >> + will be linked in. For shared library ABI compatibility, there must be >> + longjmp and siglongjmp symbols in libpthread.so. >> + >> + With an IFUNC resolver, it would be possible to avoid the indirection, >> + but the IFUNC resolver might run before the __libc_longjmp symbol has >> + been relocated, in which case the IFUNC resolver would not be able to >> + provide the correct address. */ >> + >> +#if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_22) >> + >> +static void __attribute__ ((noreturn, used)) >> +longjmp_compat (jmp_buf env, int val) >> +{ >> + /* NB: We call __libc_siglongjmp, instead of __libc_longjmp, since >> + __libc_longjmp is a private interface for cancellation which >> + doesn't restore shadow stack register. */ >> + __libc_siglongjmp (env, val); > > OK. > >> +} >> + >> +strong_alias (longjmp_compat, longjmp_alias) >> +compat_symbol (libpthread, longjmp_alias, longjmp, GLIBC_2_0); >> + >> +strong_alias (longjmp_alias, siglongjmp_alias) >> +compat_symbol (libpthread, siglongjmp_alias, siglongjmp, GLIBC_2_0); >> + >> +#endif >> -- 2.14.3 > > > -- > Cheers, > Carlos.
On Fri, 6 Apr 2018, H.J. Lu wrote: > https://github.com/hjl-tools/glibc/commit/9bf6aefa8fb45f8df140d42ce9cf890bb24076e1 > > It should be binary backward compatible. I will investigate if there is a way Increasing the size of a public type is always dangerous, because you can end up with one part of a program expecting the new, larger size but another part only allocating the old, smaller size. It might in some cases be compatible to the extent that existing linked programs and shared libraries work with new glibc, if new glibc will never try to write into the unallocated part of such objects allocated by an existing linked program or shared library. However, any such change would need a careful analysis of how the type gets written to, and to what extent external libraries have interfaces that depend on the size of the type, and would need a NEWS entry explaining the change and discussing the compatibility issues with it.
On Tue, Apr 17, 2018 at 1:02 PM, Joseph Myers <joseph@codesourcery.com> wrote: > On Fri, 6 Apr 2018, H.J. Lu wrote: > >> https://github.com/hjl-tools/glibc/commit/9bf6aefa8fb45f8df140d42ce9cf890bb24076e1 >> >> It should be binary backward compatible. I will investigate if there is a way > > Increasing the size of a public type is always dangerous, because you can > end up with one part of a program expecting the new, larger size but > another part only allocating the old, smaller size. That is true. The allocated ucontext size must be no less than the size expected by ucontext consumer. > It might in some cases be compatible to the extent that existing linked > programs and shared libraries work with new glibc, if new glibc will never This is done by checking CET properties. Both linker and dynamic linker clear CET property bits if any module doesn't have CET bits set. Glibc should access new extended fields only if CET bits are set, which means the new ucontext is used in all .o files. That is why I want to extend ucontext before we have found a solution so that if an object file has CET bits set, it must use the new ucontext. > try to write into the unallocated part of such objects allocated by an > existing linked program or shared library. However, any such change would > need a careful analysis of how the type gets written to, and to what > extent external libraries have interfaces that depend on the size of the > type, and would need a NEWS entry explaining the change and discussing the > compatibility issues with it. > Agreed,
Patch
From f222537447f5ec879427f318b7c0396362b7453a Mon Sep 17 00:00:00 2001 From: "H.J. Lu" <hjl.tools@gmail.com> Date: Sat, 24 Feb 2018 17:23:54 -0800 Subject: [PATCH] x86: Use pad in pthread_unwind_buf to preserve shadow stack register The pad array in struct pthread_unwind_buf is used by setjmp to save shadow stack register. We assert that size of struct pthread_unwind_buf is no less than offset of shadow stack pointer + shadow stack pointer size. Since functions, like LIBC_START_MAIN, START_THREAD_DEFN as well as these with thread cancellation, call setjmp, but never return after __libc_unwind_longjmp, __libc_unwind_longjmp, which is defined as __libc_longjmp on x86, doesn't need to restore shadow stack register. __libc_longjmp, which is a private interface for thread cancellation implementation in libpthread, is changed to call __longjmp_cancel, instead of __longjmp. __longjmp_cancel is a new internal function in libc, which is similar to __longjmp, but doesn't restore shadow stack register. The compatibility longjmp and siglongjmp in libpthread.so are changed to call __libc_siglongjmp, instead of __libc_longjmp, so that they will restore shadow stack register. Tested with build-many-glibcs.py. * nptl/pthread_create.c (START_THREAD_DEFN): Clear previous handlers after setjmp. * setjmp/longjmp.c (__libc_longjmp): Don't define alias if defined. * sysdeps/unix/sysv/linux/x86/setjmpP.h (_JUMP_BUF_SIGSET_NSIG): Changed to 97. * sysdeps/x86/Makefile (sysdep_routines): Add __longjmp_cancel. * sysdeps/x86/__longjmp_cancel.S: New file. * sysdeps/x86/longjmp.c: Likewise. * sysdeps/x86/nptl/pt-longjmp.c: Likewise. --- nptl/pthread_create.c | 9 ++-- setjmp/longjmp.c | 2 + sysdeps/unix/sysv/linux/x86/setjmpP.h | 4 +- sysdeps/x86/Makefile | 4 ++ sysdeps/x86/__longjmp_cancel.S | 20 ++++++++ sysdeps/x86/longjmp.c | 45 ++++++++++++++++ sysdeps/x86/nptl/pt-longjmp.c | 97 +++++++++++++++++++++++++++++++++++ 7 files changed, 176 insertions(+), 5 deletions(-) create mode 100644 sysdeps/x86/__longjmp_cancel.S create mode 100644 sysdeps/x86/longjmp.c create mode 100644 sysdeps/x86/nptl/pt-longjmp.c diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c index caaf07c134..1c5b3780c6 100644 --- a/nptl/pthread_create.c +++ b/nptl/pthread_create.c @@ -427,12 +427,15 @@ START_THREAD_DEFN compilers without that support we do use setjmp. */ struct pthread_unwind_buf unwind_buf; - /* No previous handlers. */ + int not_first_call; + not_first_call = setjmp ((struct __jmp_buf_tag *) unwind_buf.cancel_jmp_buf); + + /* No previous handlers. NB: This must be done after setjmp since + the same space may be used by setjmp to store extra data which + should never be used by __libc_unwind_longjmp. */ unwind_buf.priv.data.prev = NULL; unwind_buf.priv.data.cleanup = NULL; - int not_first_call; - not_first_call = setjmp ((struct __jmp_buf_tag *) unwind_buf.cancel_jmp_buf); if (__glibc_likely (! not_first_call)) { /* Store the new cleanup handler info. */ diff --git a/setjmp/longjmp.c b/setjmp/longjmp.c index a2a7065a85..453889e103 100644 --- a/setjmp/longjmp.c +++ b/setjmp/longjmp.c @@ -40,9 +40,11 @@ __libc_siglongjmp (sigjmp_buf env, int val) } #ifndef __libc_siglongjmp +# ifndef __libc_longjmp /* __libc_longjmp is a private interface for cancellation implementation in libpthread. */ strong_alias (__libc_siglongjmp, __libc_longjmp) +# endif weak_alias (__libc_siglongjmp, _longjmp) weak_alias (__libc_siglongjmp, longjmp) weak_alias (__libc_siglongjmp, siglongjmp) diff --git a/sysdeps/unix/sysv/linux/x86/setjmpP.h b/sysdeps/unix/sysv/linux/x86/setjmpP.h index c0ed767a0d..90a6bbcf32 100644 --- a/sysdeps/unix/sysv/linux/x86/setjmpP.h +++ b/sysdeps/unix/sysv/linux/x86/setjmpP.h @@ -22,8 +22,8 @@ #include <bits/types/__sigset_t.h> /* The biggest signal number + 1. As of kernel 4.14, x86 _NSIG is 64. - Define it to 513 to leave some rooms for future use. */ -#define _JUMP_BUF_SIGSET_NSIG 513 + Define it to 97 to leave some rooms for future use. */ +#define _JUMP_BUF_SIGSET_NSIG 97 /* Number of longs to hold all signals. */ #define _JUMP_BUF_SIGSET_NWORDS \ ((_JUMP_BUF_SIGSET_NSIG - 1 + 7) / (8 * sizeof (unsigned long int))) diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile index 0d0326c21a..d25d6f0ae4 100644 --- a/sysdeps/x86/Makefile +++ b/sysdeps/x86/Makefile @@ -8,3 +8,7 @@ sysdep-dl-routines += dl-get-cpu-features tests += tst-get-cpu-features tests-static += tst-get-cpu-features-static endif + +ifeq ($(subdir),setjmp) +sysdep_routines += __longjmp_cancel +endif diff --git a/sysdeps/x86/__longjmp_cancel.S b/sysdeps/x86/__longjmp_cancel.S new file mode 100644 index 0000000000..b57dbfa376 --- /dev/null +++ b/sysdeps/x86/__longjmp_cancel.S @@ -0,0 +1,20 @@ +/* __longjmp_cancel for x86. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#define __longjmp __longjmp_cancel +#include <__longjmp.S> diff --git a/sysdeps/x86/longjmp.c b/sysdeps/x86/longjmp.c new file mode 100644 index 0000000000..a53f31e1dd --- /dev/null +++ b/sysdeps/x86/longjmp.c @@ -0,0 +1,45 @@ +/* __libc_siglongjmp for x86. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#define __libc_longjmp __redirect___libc_longjmp +#include <setjmp/longjmp.c> +#undef __libc_longjmp + +extern void __longjmp_cancel (__jmp_buf __env, int __val) + __attribute__ ((__noreturn__)) attribute_hidden; + +/* Since __libc_longjmp is a private interface for cancellation + implementation in libpthread, there is no need to restore shadow + stack register. */ + +void +__libc_longjmp (sigjmp_buf env, int val) +{ + /* Perform any cleanups needed by the frames being unwound. */ + _longjmp_unwind (env, val); + + if (env[0].__mask_was_saved) + /* Restore the saved signal mask. */ + (void) __sigprocmask (SIG_SETMASK, + (sigset_t *) &env[0].__saved_mask, + (sigset_t *) NULL); + + /* Call the machine-dependent function to restore machine state + without shadow stack. */ + __longjmp_cancel (env[0].__jmpbuf, val ?: 1); +} diff --git a/sysdeps/x86/nptl/pt-longjmp.c b/sysdeps/x86/nptl/pt-longjmp.c new file mode 100644 index 0000000000..7eb8651cfe --- /dev/null +++ b/sysdeps/x86/nptl/pt-longjmp.c @@ -0,0 +1,97 @@ +/* ABI compatibility for 'longjmp' and 'siglongjmp' symbols in libpthread ABI. + X86 version. + Copyright (C) 18 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* <nptl/descr.h> has + +struct pthread_unwind_buf +{ + struct + { + __jmp_buf jmp_buf; + int mask_was_saved; + } cancel_jmp_buf[1]; + + union + { + void *pad[4]; + struct + { + struct pthread_unwind_buf *prev; + struct _pthread_cleanup_buffer *cleanup; + int canceltype; + } data; + } priv; +}; + + The pad array in struct pthread_unwind_buf is used by setjmp to save + shadow stack register. Assert that size of struct pthread_unwind_buf + is no less than offset of shadow stack pointer plus shadow stack + pointer size. + + NB: setjmp is called in libpthread to save shadow stack register. But + __libc_unwind_longjmp doesn't restore shadow stack register since they + never return after longjmp. */ + +#include <pthreadP.h> +#include <jmp_buf-ssp.h> + +#ifdef __x86_64__ +# define SHADOW_STACK_POINTER_SIZE 8 +#else +# define SHADOW_STACK_POINTER_SIZE 4 +#endif + +_Static_assert ((sizeof (struct pthread_unwind_buf) + >= (SHADOW_STACK_POINTER_OFFSET + + SHADOW_STACK_POINTER_SIZE)), + "size of struct pthread_unwind_buf < " + "(SHADOW_STACK_POINTER_OFFSET + SHADOW_STACK_POINTER_SIZE)"); + +#include <shlib-compat.h> + +/* libpthread once had its own longjmp (and siglongjmp alias), though there + was no apparent reason for it. There is no use in having a separate + symbol in libpthread, but the historical ABI requires it. For static + linking, there is no need to provide anything here--the libc version + will be linked in. For shared library ABI compatibility, there must be + longjmp and siglongjmp symbols in libpthread.so. + + With an IFUNC resolver, it would be possible to avoid the indirection, + but the IFUNC resolver might run before the __libc_longjmp symbol has + been relocated, in which case the IFUNC resolver would not be able to + provide the correct address. */ + +#if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_22) + +static void __attribute__ ((noreturn, used)) +longjmp_compat (jmp_buf env, int val) +{ + /* NB: We call __libc_siglongjmp, instead of __libc_longjmp, since + __libc_longjmp is a private interface for cancellation which + doesn't restore shadow stack register. */ + __libc_siglongjmp (env, val); +} + +strong_alias (longjmp_compat, longjmp_alias) +compat_symbol (libpthread, longjmp_alias, longjmp, GLIBC_2_0); + +strong_alias (longjmp_alias, siglongjmp_alias) +compat_symbol (libpthread, siglongjmp_alias, siglongjmp, GLIBC_2_0); + +#endif -- 2.14.3