/* * We need to use inline assembly instead of glibc's syscall because glibc's * syscall will attempt to access the PLT in order to call a library function * which is protected by MPK 0 which we don't have access to.
*/ staticinline __always_inline long syscall_raw(long n, long a1, long a2, long a3, long a4, long a5, long a6)
{ unsignedlong ret; #ifdef __x86_64__ registerlong r10 asm("r10") = a4; registerlong r8 asm("r8") = a5; registerlong r9 asm("r9") = a6; asmvolatile ("syscall"
: "=a"(ret)
: "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8), "r"(r9)
: "rcx", "r11", "memory"); #elifdefined __i386__ asmvolatile ("int $0x80"
: "=a"(ret)
: "a"(n), "b"(a1), "c"(a2), "d"(a3), "S"(a4), "D"(a5)
: "memory"); #elifdefined __aarch64__ registerlong x0 asm("x0") = a1; registerlong x1 asm("x1") = a2; registerlong x2 asm("x2") = a3; registerlong x3 asm("x3") = a4; registerlong x4 asm("x4") = a5; registerlong x5 asm("x5") = a6; registerlong x8 asm("x8") = n; asmvolatile ("svc #0"
: "=r"(x0)
: "r"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5), "r"(x8)
: "memory");
ret = x0; #else # error syscall_raw() not implemented #endif return ret;
}
staticinlinelong clone_raw(unsignedlong flags, void *stack, int *parent_tid, int *child_tid)
{ long a1 = flags; long a2 = (long)stack; long a3 = (long)parent_tid; #ifdefined(__x86_64__) || defined(__i386) long a4 = (long)child_tid; long a5 = 0; #elifdefined(__aarch64__) long a4 = 0; long a5 = (long)child_tid; #else # error clone_raw() not implemented #endif
/* * Returns the most restrictive pkey register value that can be used by the * tests.
*/ staticinline u64 pkey_reg_restrictive_default(void)
{ /* * Disallow everything except execution on pkey 0, so that each caller * doesn't need to enable it explicitly (the selftest code runs with * its code mapped with pkey 0).
*/ return set_pkey_bits(PKEY_REG_ALLOW_NONE, 0, PKEY_DISABLE_ACCESS);
}
staticvoid sigusr2_handler(int signo, siginfo_t *info, void *ucontext)
{ /* * pkru should be the init_pkru value which enabled MPK 0 so * we can use library functions.
*/
printf("%s invoked.\n", __func__);
}
staticvoid raise_sigusr2(void)
{
pid_t tid = 0;
tid = syscall_raw(SYS_gettid, 0, 0, 0, 0, 0, 0);
syscall_raw(SYS_tkill, tid, SIGUSR2, 0, 0, 0, 0);
/* * We should return from the signal handler here and be able to * return to the interrupted thread.
*/
}
/* * Setup alternate signal stack, which should be pkey_mprotect()ed by * MPK 0. The thread's stack cannot be used for signals because it is * not accessible by the default init_pkru value of 0x55555554.
*/
syscall_raw(SYS_sigaltstack, (long)stack, 0, 0, 0, 0, 0);
/* Disable MPK 0. Only MPK 1 is enabled. */
pkey_reg = pkey_reg_restrictive_default();
pkey_reg = set_pkey_bits(pkey_reg, 1, PKEY_UNRESTRICTED);
__write_pkey_reg(pkey_reg);
/* * Verify that the sigsegv handler is invoked when pkey 0 is disabled. * Note that the new thread stack and the alternate signal stack is * protected by MPK 0.
*/ staticvoid test_sigsegv_handler_with_pkey0_disabled(void)
{ struct sigaction sa;
pthread_attr_t attr;
pthread_t thr;
/* * Verify that the sigsegv handler is invoked when pkey 0 is disabled. * Note that the new thread stack and the alternate signal stack is * protected by MPK 0, which renders them inaccessible when MPK 0 * is disabled. So just the return from the thread should cause a * segfault with SEGV_PKUERR.
*/ staticvoid test_sigsegv_handler_cannot_access_stack(void)
{ struct sigaction sa;
pthread_attr_t attr;
pthread_t thr;
/* * Verify that the sigsegv handler that uses an alternate signal stack * is correctly invoked for a thread which uses a non-zero MPK to protect * its own stack, and disables all other MPKs (including 0).
*/ staticvoid test_sigsegv_handler_with_different_pkey_for_stack(void)
{ struct sigaction sa; static stack_t sigstack; void *stack; int pkey; int parent_pid = 0; int child_pid = 0;
u64 pkey_reg;
/* Protect the new stack with MPK 1 */
pkey = sys_pkey_alloc(0, PKEY_UNRESTRICTED);
sys_mprotect_pkey(stack, STACK_SIZE, PROT_READ | PROT_WRITE, pkey);
/* Set up alternate signal stack that will use the default MPK */
sigstack.ss_sp = mmap(0, STACK_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
sigstack.ss_flags = 0;
sigstack.ss_size = STACK_SIZE;
memset(&siginfo, 0, sizeof(siginfo));
/* Use clone to avoid newer glibcs using rseq on new threads */ long ret = clone_raw(CLONE_VM | CLONE_FS | CLONE_FILES |
CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM |
CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID |
CLONE_DETACHED,
stack + STACK_SIZE,
&parent_pid,
&child_pid);
/* * Verify that the PKRU value set by the application is correctly * restored upon return from signal handling.
*/ staticvoid test_pkru_preserved_after_sigusr1(void)
{ struct sigaction sa;
u64 pkey_reg;
/* Allow access to MPK 0 and an arbitrary set of keys */
pkey_reg = pkey_reg_restrictive_default();
pkey_reg = set_pkey_bits(pkey_reg, 0, PKEY_UNRESTRICTED);
pkey_reg = set_pkey_bits(pkey_reg, 3, PKEY_UNRESTRICTED);
pkey_reg = set_pkey_bits(pkey_reg, 7, PKEY_UNRESTRICTED);
pthread_mutex_lock(&mutex); while (siginfo.si_signo == 0)
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
/* Ensure the pkru value is the same after returning from signal. */
ksft_test_result(pkey_reg == __read_pkey_reg() &&
siginfo.si_signo == SIGUSR1, "%s\n", __func__);
}
static noinline void *thread_sigusr2_self(void *ptr)
{ /* * A const char array like "Resuming after SIGUSR2" won't be stored on * the stack and the code could access it via an offset from the program * counter. This makes sure it's on the function's stack frame.
*/ char str[] = {'R', 'e', 's', 'u', 'm', 'i', 'n', 'g', ' ', 'a', 'f', 't', 'e', 'r', ' ', 'S', 'I', 'G', 'U', 'S', 'R', '2', '.', '.', '.', '\n', '\0'};
stack_t *stack = ptr;
u64 pkey_reg;
/* * Setup alternate signal stack, which should be pkey_mprotect()ed by * MPK 0. The thread's stack cannot be used for signals because it is * not accessible by the default init_pkru value of 0x55555554.
*/
syscall(SYS_sigaltstack, (long)stack, 0, 0, 0, 0, 0);
/* Disable MPK 0. Only MPK 2 is enabled. */
pkey_reg = pkey_reg_restrictive_default();
pkey_reg = set_pkey_bits(pkey_reg, 2, PKEY_UNRESTRICTED);
__write_pkey_reg(pkey_reg);
raise_sigusr2();
/* Do something, to show the thread resumed execution after the signal */
syscall_raw(SYS_write, 1, (long) str, sizeof(str) - 1, 0, 0, 0);
/* * We can't return to test_pkru_sigreturn because it * will attempt to use a %rbp value which is on the stack * of the main thread.
*/
syscall_raw(SYS_exit, 0, 0, 0, 0, 0, 0); return NULL;
}
/* * Verify that sigreturn is able to restore altstack even if the thread had * disabled pkey 0.
*/ staticvoid test_pkru_sigreturn(void)
{ struct sigaction sa = {0}; static stack_t sigstack; void *stack; int pkey; int parent_pid = 0; int child_pid = 0;
u64 pkey_reg;
/* * For this testcase, we do not want to handle SIGSEGV. Reset handler * to default so that the application can crash if it receives SIGSEGV.
*/ if (sigaction(SIGSEGV, &sa, NULL) == -1) {
perror("sigaction"); exit(EXIT_FAILURE);
}
/* * Allow access to MPK 0 and MPK 2. The child thread (to be created * later in this flow) will have its stack protected by MPK 2, whereas * the current thread's stack is protected by the default MPK 0. Hence * both need to be enabled.
*/
pkey_reg = pkey_reg_restrictive_default();
pkey_reg = set_pkey_bits(pkey_reg, 0, PKEY_UNRESTRICTED);
pkey_reg = set_pkey_bits(pkey_reg, 2, PKEY_UNRESTRICTED);
__write_pkey_reg(pkey_reg);
/* Protect the stack with MPK 2 */
pkey = sys_pkey_alloc(0, PKEY_UNRESTRICTED);
sys_mprotect_pkey(stack, STACK_SIZE, PROT_READ | PROT_WRITE, pkey);
/* Set up alternate signal stack that will use the default MPK */
sigstack.ss_sp = mmap(0, STACK_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
sigstack.ss_flags = 0;
sigstack.ss_size = STACK_SIZE;
/* Use clone to avoid newer glibcs using rseq on new threads */ long ret = clone_raw(CLONE_VM | CLONE_FS | CLONE_FILES |
CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM |
CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID |
CLONE_DETACHED,
stack + STACK_SIZE,
&parent_pid,
&child_pid);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.