mirror of
https://sourceware.org/git/glibc.git
synced 2024-12-22 19:00:07 +00:00
x86: Make a space in jmpbuf for shadow stack pointer
To support Shadow Stack (SHSTK) in Intel Control-flow Enforcement Technology (CET) in setjmp/longjmp, we need to save shadow stack pointer in jmp_buf. The __saved_mask field in jmp_buf has type of __sigset_t. On Linux, __sigset_t is defined as #define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int))) typedef struct { unsigned long int __val[_SIGSET_NWORDS]; } __sigset_t; which is much bigger than expected by the __sigprocmask system call, which has typedef struct { unsigned long sig[_NSIG_WORDS]; } sigset_t; For Linux/x86, we can shrink __sigset_t used by __saved_mask in jmp_buf to add paddings for shadow stack pointer. As long as the new __sigset_t is not smaller than sigset_t expected by the __sigprocmask system call, it should work correctly. This patch adds an internal header file, <setjmpP.h>, to define __jmp_buf_sigset_t for __saved_mask in jmp_buf for Linux/x86 with a space to store shadow stack pointer. It verifies __jmp_buf_sigset_t has the suitable size for the __sigprocmask system call. A run-time test, tst-saved_mask-1.c, is added to verify that size of __jmp_buf_sigset_t is sufficient. If its size is too small, the test fails with rt_sigprocmask(SIG_SETMASK, strace: umoven: short read (4 < 8) @0x7fa8aa28effc 0x7fa8aa28effc, NULL, 8) = -1 EFAULT (Bad address) rt_sigprocmask(SIG_SETMASK, strace: umoven: short read (4 < 8) @0x7fa8aa28effc 0x7fa8aa28effc, NULL, 8) = -1 EFAULT (Bad address) rt_sigprocmask(SIG_SETMASK, NULL, 0x7fa8aa28effc, 8) = -1 EFAULT (Bad address) exit_group(1) = ? Tested with build-many-glibcs.py. * debug/longjmp_chk.c: Include <setjmpP.h> instead of <setjmp.h>. * setjmp/longjmp.c: Include <setjmpP.h> instead of <setjmp.h>. (__libc_siglongjmp): Cast &env[0].__saved_mask to "sigset_t *". * setjmp/sigjmp.c: Include <setjmpP.h> instead of <setjmp.h>. (__sigjmp_save): Cast &env[0].__saved_mask to "sigset_t *". * sysdeps/generic/setjmpP.h: New file. * sysdeps/unix/sysv/linux/x86/jmp_buf-ssp.sym: Likewise. * sysdeps/unix/sysv/linux/x86/setjmpP.h: Likewise. * sysdeps/unix/sysv/linux/x86/tst-saved_mask-1.c: Likewise. * sysdeps/unix/sysv/linux/x86/Makefile (gen-as-const-headers): Add jmp_buf-ssp.sym. (tests): Add tst-saved_mask-1.
This commit is contained in:
parent
34697694e8
commit
f33632ccd1
16
ChangeLog
16
ChangeLog
@ -1,3 +1,19 @@
|
||||
2017-11-30 H.J. Lu <hongjiu.lu@intel.com>
|
||||
|
||||
* debug/longjmp_chk.c: Include <setjmpP.h> instead of
|
||||
<setjmp.h>.
|
||||
* setjmp/longjmp.c: Include <setjmpP.h> instead of <setjmp.h>.
|
||||
(__libc_siglongjmp): Cast &env[0].__saved_mask to "sigset_t *".
|
||||
* setjmp/sigjmp.c: Include <setjmpP.h> instead of <setjmp.h>.
|
||||
(__sigjmp_save): Cast &env[0].__saved_mask to "sigset_t *".
|
||||
* sysdeps/generic/setjmpP.h: New file.
|
||||
* sysdeps/unix/sysv/linux/x86/jmp_buf-ssp.sym: Likewise.
|
||||
* sysdeps/unix/sysv/linux/x86/setjmpP.h: Likewise.
|
||||
* sysdeps/unix/sysv/linux/x86/tst-saved_mask-1.c: Likewise.
|
||||
* sysdeps/unix/sysv/linux/x86/Makefile (gen-as-const-headers):
|
||||
Add jmp_buf-ssp.sym.
|
||||
(tests): Add tst-saved_mask-1.
|
||||
|
||||
2017-11-30 Arjun Shankar <arjun@redhat.com>
|
||||
|
||||
[BZ #22375]
|
||||
|
@ -15,7 +15,7 @@
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <setjmpP.h>
|
||||
|
||||
#define __longjmp ____longjmp_chk
|
||||
#define __libc_siglongjmp __longjmp_chk
|
||||
|
@ -16,7 +16,7 @@
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <setjmpP.h>
|
||||
#include <signal.h>
|
||||
|
||||
|
||||
@ -31,7 +31,8 @@ __libc_siglongjmp (sigjmp_buf env, int val)
|
||||
|
||||
if (env[0].__mask_was_saved)
|
||||
/* Restore the saved signal mask. */
|
||||
(void) __sigprocmask (SIG_SETMASK, &env[0].__saved_mask,
|
||||
(void) __sigprocmask (SIG_SETMASK,
|
||||
(sigset_t *) &env[0].__saved_mask,
|
||||
(sigset_t *) NULL);
|
||||
|
||||
/* Call the machine-dependent function to restore machine state. */
|
||||
|
@ -16,7 +16,7 @@
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <setjmpP.h>
|
||||
#include <signal.h>
|
||||
|
||||
/* This function is called by the `sigsetjmp' macro
|
||||
@ -28,7 +28,7 @@ __sigjmp_save (sigjmp_buf env, int savemask)
|
||||
{
|
||||
env[0].__mask_was_saved = (savemask &&
|
||||
__sigprocmask (SIG_BLOCK, (sigset_t *) NULL,
|
||||
&env[0].__saved_mask) == 0);
|
||||
(sigset_t *) &env[0].__saved_mask) == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
24
sysdeps/generic/setjmpP.h
Normal file
24
sysdeps/generic/setjmpP.h
Normal file
@ -0,0 +1,24 @@
|
||||
/* Internal header file for <setjmp.h>. Generic version.
|
||||
Copyright (C) 2017 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/>. */
|
||||
|
||||
#ifndef _SETJMPP_H
|
||||
#define _SETJMPP_H 1
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
#endif /* setjmpP.h */
|
@ -19,3 +19,8 @@ endif
|
||||
ifeq ($(subdir),elf)
|
||||
sysdep_routines += dl-vdso
|
||||
endif
|
||||
|
||||
ifeq ($(subdir),setjmp)
|
||||
gen-as-const-headers += jmp_buf-ssp.sym
|
||||
tests += tst-saved_mask-1
|
||||
endif
|
||||
|
5
sysdeps/unix/sysv/linux/x86/jmp_buf-ssp.sym
Normal file
5
sysdeps/unix/sysv/linux/x86/jmp_buf-ssp.sym
Normal file
@ -0,0 +1,5 @@
|
||||
#include <setjmpP.h>
|
||||
#undef __saved_mask
|
||||
|
||||
--
|
||||
SHADOW_STACK_POINTER_OFFSET offsetof(struct __jmp_buf_tag, __saved_mask.__saved.__shadow_stack_pointer)
|
67
sysdeps/unix/sysv/linux/x86/setjmpP.h
Normal file
67
sysdeps/unix/sysv/linux/x86/setjmpP.h
Normal file
@ -0,0 +1,67 @@
|
||||
/* Internal header file for <setjmp.h>. Linux/x86 version.
|
||||
Copyright (C) 2017 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/>. */
|
||||
|
||||
#ifndef _SETJMPP_H
|
||||
#define _SETJMPP_H 1
|
||||
|
||||
#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
|
||||
/* Number of longs to hold all signals. */
|
||||
#define _JUMP_BUF_SIGSET_NWORDS \
|
||||
((_JUMP_BUF_SIGSET_NSIG - 1 + 7) / (8 * sizeof (unsigned long int)))
|
||||
|
||||
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;
|
||||
|
||||
#undef __sigset_t
|
||||
#define __sigset_t __jmpbuf_arch_t
|
||||
#include <setjmp.h>
|
||||
#undef __saved_mask
|
||||
#define __saved_mask __saved_mask.__saved.__saved_mask
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#define _SIGPROCMASK_NSIG_WORDS (_NSIG / (8 * sizeof (unsigned long int)))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned long int __val[_SIGPROCMASK_NSIG_WORDS];
|
||||
} __sigprocmask_sigset_t;
|
||||
|
||||
extern jmp_buf ___buf;
|
||||
extern __typeof (___buf[0].__saved_mask) ___saved_mask;
|
||||
_Static_assert (sizeof (___saved_mask) >= sizeof (__sigprocmask_sigset_t),
|
||||
"size of ___saved_mask < size of __sigprocmask_sigset_t");
|
||||
|
||||
#endif /* setjmpP.h */
|
55
sysdeps/unix/sysv/linux/x86/tst-saved_mask-1.c
Normal file
55
sysdeps/unix/sysv/linux/x86/tst-saved_mask-1.c
Normal file
@ -0,0 +1,55 @@
|
||||
/* Test that sigprocmask does not read from the unused part of jmpbuf.
|
||||
Copyright (C) 2017 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 <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <setjmpP.h>
|
||||
#include <support/next_to_fault.h>
|
||||
|
||||
#define SIZEOF_SIGSET_T sizeof (__jmp_buf_sigset_t)
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
sigjmp_buf sj;
|
||||
struct support_next_to_fault sigset_t_buf
|
||||
= support_next_to_fault_allocate (SIZEOF_SIGSET_T);
|
||||
sigset_t *m_p = (sigset_t *) sigset_t_buf.buffer;
|
||||
sigset_t m;
|
||||
|
||||
sigemptyset (&m);
|
||||
memcpy (m_p, &m, SIZEOF_SIGSET_T);
|
||||
sigprocmask (SIG_SETMASK, m_p, NULL);
|
||||
memcpy (&m, m_p, SIZEOF_SIGSET_T);
|
||||
if (sigsetjmp (sj, 0) == 0)
|
||||
{
|
||||
sigaddset (&m, SIGUSR1);
|
||||
memcpy (m_p, &m, SIZEOF_SIGSET_T);
|
||||
sigprocmask (SIG_SETMASK, m_p, NULL);
|
||||
memcpy (&m, m_p, SIZEOF_SIGSET_T);
|
||||
siglongjmp (sj, 1);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
sigprocmask (SIG_SETMASK, NULL, m_p);
|
||||
memcpy (&m, m_p, SIZEOF_SIGSET_T);
|
||||
return sigismember (&m, SIGUSR1) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
Loading…
Reference in New Issue
Block a user