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;
|
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)
|
static inline u32 pkey_bit_position(int pkey)
|
||||||
{
|
{
|
||||||
return pkey * PKEY_BITS_PER_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
|
* do a plain mprotect() to a mprotect_pkey() area and make sure the pkey sticks
|
||||||
*
|
*
|
||||||
* Compile like this:
|
* Compile like this:
|
||||||
* gcc -o protection_keys -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 -m32 -o protection_keys_32 -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 _GNU_SOURCE
|
||||||
#define __SANE_USERSPACE_TYPES__
|
#define __SANE_USERSPACE_TYPES__
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <linux/elf.h>
|
||||||
#include <linux/futex.h>
|
#include <linux/futex.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/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");
|
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)
|
void test_mprotect_pkey_on_unsupported_cpu(int *ptr, u16 pkey)
|
||||||
{
|
{
|
||||||
int size = PAGE_SIZE;
|
int size = PAGE_SIZE;
|
||||||
|
@ -1585,6 +1709,9 @@ void (*pkey_tests[])(int *ptr, u16 pkey) = {
|
||||||
test_pkey_syscalls_bad_args,
|
test_pkey_syscalls_bad_args,
|
||||||
test_pkey_alloc_exhaust,
|
test_pkey_alloc_exhaust,
|
||||||
test_pkey_alloc_free_attach_pkey0,
|
test_pkey_alloc_free_attach_pkey0,
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
|
test_ptrace_modifies_pkru,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
void run_tests_once(void)
|
void run_tests_once(void)
|
||||||
|
|
Loading…
Add table
Reference in a new issue