mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-03-17 20:54:10 +00:00
selftests/vm/pkeys: Add a regression test for setting PKRU through ptrace
commit 6ea25770b0
upstream
This tests PTRACE_SETREGSET with NT_X86_XSTATE modifying PKRU directly and
removing the PKRU bit from XSTATE_BV.
Signed-off-by: Kyle Huey <me@kylehuey.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Link: https://lore.kernel.org/all/20221115230932.7126-7-khuey%40kylehuey.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
921b089af2
commit
ae14b7746b
2 changed files with 141 additions and 2 deletions
|
@ -119,6 +119,18 @@ static inline int cpu_has_pkeys(void)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static inline int cpu_max_xsave_size(void)
|
||||
{
|
||||
unsigned long XSTATE_CPUID = 0xd;
|
||||
unsigned int eax;
|
||||
unsigned int ebx;
|
||||
unsigned int ecx;
|
||||
unsigned int edx;
|
||||
|
||||
__cpuid_count(XSTATE_CPUID, 0, eax, ebx, ecx, edx);
|
||||
return ecx;
|
||||
}
|
||||
|
||||
static inline u32 pkey_bit_position(int pkey)
|
||||
{
|
||||
return pkey * PKEY_BITS_PER_PKEY;
|
||||
|
|
|
@ -18,12 +18,13 @@
|
|||
* do a plain mprotect() to a mprotect_pkey() area and make sure the pkey sticks
|
||||
*
|
||||
* Compile like this:
|
||||
* gcc -o protection_keys -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm
|
||||
* gcc -m32 -o protection_keys_32 -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm
|
||||
* gcc -mxsave -o protection_keys -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm
|
||||
* gcc -mxsave -m32 -o protection_keys_32 -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#define __SANE_USERSPACE_TYPES__
|
||||
#include <errno.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/futex.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
@ -1550,6 +1551,129 @@ void test_implicit_mprotect_exec_only_memory(int *ptr, u16 pkey)
|
|||
do_not_expect_pkey_fault("plain read on recently PROT_EXEC area");
|
||||
}
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
void test_ptrace_modifies_pkru(int *ptr, u16 pkey)
|
||||
{
|
||||
u32 new_pkru;
|
||||
pid_t child;
|
||||
int status, ret;
|
||||
int pkey_offset = pkey_reg_xstate_offset();
|
||||
size_t xsave_size = cpu_max_xsave_size();
|
||||
void *xsave;
|
||||
u32 *pkey_register;
|
||||
u64 *xstate_bv;
|
||||
struct iovec iov;
|
||||
|
||||
new_pkru = ~read_pkey_reg();
|
||||
/* Don't make PROT_EXEC mappings inaccessible */
|
||||
new_pkru &= ~3;
|
||||
|
||||
child = fork();
|
||||
pkey_assert(child >= 0);
|
||||
dprintf3("[%d] fork() ret: %d\n", getpid(), child);
|
||||
if (!child) {
|
||||
ptrace(PTRACE_TRACEME, 0, 0, 0);
|
||||
/* Stop and allow the tracer to modify PKRU directly */
|
||||
raise(SIGSTOP);
|
||||
|
||||
/*
|
||||
* need __read_pkey_reg() version so we do not do shadow_pkey_reg
|
||||
* checking
|
||||
*/
|
||||
if (__read_pkey_reg() != new_pkru)
|
||||
exit(1);
|
||||
|
||||
/* Stop and allow the tracer to clear XSTATE_BV for PKRU */
|
||||
raise(SIGSTOP);
|
||||
|
||||
if (__read_pkey_reg() != 0)
|
||||
exit(1);
|
||||
|
||||
/* Stop and allow the tracer to examine PKRU */
|
||||
raise(SIGSTOP);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
pkey_assert(child == waitpid(child, &status, 0));
|
||||
dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
|
||||
pkey_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
|
||||
|
||||
xsave = (void *)malloc(xsave_size);
|
||||
pkey_assert(xsave > 0);
|
||||
|
||||
/* Modify the PKRU register directly */
|
||||
iov.iov_base = xsave;
|
||||
iov.iov_len = xsave_size;
|
||||
ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
|
||||
pkey_assert(ret == 0);
|
||||
|
||||
pkey_register = (u32 *)(xsave + pkey_offset);
|
||||
pkey_assert(*pkey_register == read_pkey_reg());
|
||||
|
||||
*pkey_register = new_pkru;
|
||||
|
||||
ret = ptrace(PTRACE_SETREGSET, child, (void *)NT_X86_XSTATE, &iov);
|
||||
pkey_assert(ret == 0);
|
||||
|
||||
/* Test that the modification is visible in ptrace before any execution */
|
||||
memset(xsave, 0xCC, xsave_size);
|
||||
ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
|
||||
pkey_assert(ret == 0);
|
||||
pkey_assert(*pkey_register == new_pkru);
|
||||
|
||||
/* Execute the tracee */
|
||||
ret = ptrace(PTRACE_CONT, child, 0, 0);
|
||||
pkey_assert(ret == 0);
|
||||
|
||||
/* Test that the tracee saw the PKRU value change */
|
||||
pkey_assert(child == waitpid(child, &status, 0));
|
||||
dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
|
||||
pkey_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
|
||||
|
||||
/* Test that the modification is visible in ptrace after execution */
|
||||
memset(xsave, 0xCC, xsave_size);
|
||||
ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
|
||||
pkey_assert(ret == 0);
|
||||
pkey_assert(*pkey_register == new_pkru);
|
||||
|
||||
/* Clear the PKRU bit from XSTATE_BV */
|
||||
xstate_bv = (u64 *)(xsave + 512);
|
||||
*xstate_bv &= ~(1 << 9);
|
||||
|
||||
ret = ptrace(PTRACE_SETREGSET, child, (void *)NT_X86_XSTATE, &iov);
|
||||
pkey_assert(ret == 0);
|
||||
|
||||
/* Test that the modification is visible in ptrace before any execution */
|
||||
memset(xsave, 0xCC, xsave_size);
|
||||
ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
|
||||
pkey_assert(ret == 0);
|
||||
pkey_assert(*pkey_register == 0);
|
||||
|
||||
ret = ptrace(PTRACE_CONT, child, 0, 0);
|
||||
pkey_assert(ret == 0);
|
||||
|
||||
/* Test that the tracee saw the PKRU value go to 0 */
|
||||
pkey_assert(child == waitpid(child, &status, 0));
|
||||
dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
|
||||
pkey_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
|
||||
|
||||
/* Test that the modification is visible in ptrace after execution */
|
||||
memset(xsave, 0xCC, xsave_size);
|
||||
ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
|
||||
pkey_assert(ret == 0);
|
||||
pkey_assert(*pkey_register == 0);
|
||||
|
||||
ret = ptrace(PTRACE_CONT, child, 0, 0);
|
||||
pkey_assert(ret == 0);
|
||||
pkey_assert(child == waitpid(child, &status, 0));
|
||||
dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
|
||||
pkey_assert(WIFEXITED(status));
|
||||
pkey_assert(WEXITSTATUS(status) == 0);
|
||||
free(xsave);
|
||||
}
|
||||
#endif
|
||||
|
||||
void test_mprotect_pkey_on_unsupported_cpu(int *ptr, u16 pkey)
|
||||
{
|
||||
int size = PAGE_SIZE;
|
||||
|
@ -1585,6 +1709,9 @@ void (*pkey_tests[])(int *ptr, u16 pkey) = {
|
|||
test_pkey_syscalls_bad_args,
|
||||
test_pkey_alloc_exhaust,
|
||||
test_pkey_alloc_free_attach_pkey0,
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
test_ptrace_modifies_pkru,
|
||||
#endif
|
||||
};
|
||||
|
||||
void run_tests_once(void)
|
||||
|
|
Loading…
Add table
Reference in a new issue