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.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>

	* 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: Include
	<libc-pointer-arith.h>.
	(_JUMP_BUF_SIGSET_BITS_PER_WORD): New.
	(_JUMP_BUF_SIGSET_NSIG): Changed to 96.
	(_JUMP_BUF_SIGSET_NWORDS): Changed to use ALIGN_UP and
	_JUMP_BUF_SIGSET_BITS_PER_WORD.
	* 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 commit is contained in:
H.J. Lu 2018-05-02 06:17:20 -07:00
parent b109fbfe4d
commit d6cc1829aa
8 changed files with 239 additions and 8 deletions

View File

@ -1,3 +1,20 @@
2018-05-02 H.J. Lu <hongjiu.lu@intel.com>
* 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: Include
<libc-pointer-arith.h>.
(_JUMP_BUF_SIGSET_BITS_PER_WORD): New.
(_JUMP_BUF_SIGSET_NSIG): Changed to 96.
(_JUMP_BUF_SIGSET_NWORDS): Changed to use ALIGN_UP and
_JUMP_BUF_SIGSET_BITS_PER_WORD.
* 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.
2018-05-02 Adhemerval Zanella <adhemerval.zanella@linaro.org>
* NEWS: Add ustat.h deprecation entry.

View File

@ -427,12 +427,23 @@ 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
private space in the unwind jump buffer may overlap space used by
setjmp to store extra architecture-specific information which is
never 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);
if (__glibc_likely (! not_first_call))
{
/* Store the new cleanup handler info. */

View File

@ -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)

View File

@ -20,13 +20,72 @@
#define _SETJMPP_H 1
#include <bits/types/__sigset_t.h>
#include <libc-pointer-arith.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
/* <setjmp/setjmp.h> has
struct __jmp_buf_tag
{
__jmp_buf __jmpbuf;
int __mask_was_saved;
__sigset_t __saved_mask;
};
struct __jmp_buf_tag is 32 bits aligned on i386 and is 64 bits
aligned on x32 and x86-64. __saved_mask is aligned to 32 bits
on i386/x32 without padding and is aligned to 64 bits on x86-64
with 32 bit padding.
and <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;
};
struct pthread_unwind_buf is 32 bits aligned on i386 and 64 bits
aligned on x32/x86-64. cancel_jmp_buf is aligned to 32 bits on
i386 and is aligned to 64 bits on x32/x86-64.
The pad array in struct pthread_unwind_buf is used by setjmp to save
shadow stack register. The usable space in __saved_mask for sigset
and shadow stack pointer:
1. i386: The 4x4 byte pad array which can be used for 4 byte shadow
stack pointer and maximum 12 byte sigset.
2. x32: 4 byte padding + the 4x4 byte pad array which can be used
for 8 byte shadow stack pointer and maximum 12 byte sigset.
3. x86-64: The 4x8 byte pad array which can be used for 8 byte
shadow stack pointer and maximum 24 byte sigset.
NB: We use setjmp in thread cancellation and this saves the shadow
stack register, but __libc_unwind_longjmp doesn't restore the shadow
stack register since cancellation never returns after longjmp. */
/* Number of bits per long. */
#define _JUMP_BUF_SIGSET_BITS_PER_WORD (8 * sizeof (unsigned long int))
/* The biggest signal number. As of kernel 4.14, x86 _NSIG is 64. The
common maximum sigset for i386, x32 and x86-64 is 12 bytes (96 bits).
Define it to 96 to leave some rooms for future use. */
#define _JUMP_BUF_SIGSET_NSIG 96
/* Number of longs to hold all signals. */
#define _JUMP_BUF_SIGSET_NWORDS \
((_JUMP_BUF_SIGSET_NSIG - 1 + 7) / (8 * sizeof (unsigned long int)))
(ALIGN_UP (_JUMP_BUF_SIGSET_NSIG, _JUMP_BUF_SIGSET_BITS_PER_WORD) \
/ _JUMP_BUF_SIGSET_BITS_PER_WORD)
typedef struct
{
@ -39,7 +98,9 @@ typedef union
struct
{
__jmp_buf_sigset_t __saved_mask;
/* Used for shadow stack pointer. */
/* Used for shadow stack pointer. NB: Shadow stack pointer
must have the same alignment as __saved_mask. Otherwise
offset of __saved_mask will be changed. */
unsigned long int __shadow_stack_pointer;
} __saved;
} __jmpbuf_arch_t;

View File

@ -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

View File

@ -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>

45
sysdeps/x86/longjmp.c Normal file
View File

@ -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);
}

View File

@ -0,0 +1,71 @@
/* 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/>. */
#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
/* Assert that the priv field in struct pthread_unwind_buf has space
to store shadow stack pointer. */
_Static_assert ((offsetof (struct pthread_unwind_buf, priv)
<= SHADOW_STACK_POINTER_OFFSET)
&& ((offsetof (struct pthread_unwind_buf, priv)
+ sizeof (((struct pthread_unwind_buf *) 0)->priv))
>= (SHADOW_STACK_POINTER_OFFSET
+ SHADOW_STACK_POINTER_SIZE)),
"Shadow stack pointer is not within private storage "
"of pthread_unwind_buf.");
#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