Skip to content

Commit 6ea2577

Browse files
khueyhansendc
authored andcommitted
selftests/vm/pkeys: Add a regression test for setting PKRU through ptrace
This tests PTRACE_SETREGSET with NT_X86_XSTATE modifying PKRU directly and removing the PKRU bit from XSTATE_BV. Signed-off-by: Kyle Huey <[email protected]> Signed-off-by: Dave Hansen <[email protected]> Link: https://lore.kernel.org/all/20221115230932.7126-7-khuey%40kylehuey.com
1 parent d7e5ace commit 6ea2577

File tree

2 files changed

+141
-2
lines changed

2 files changed

+141
-2
lines changed

tools/testing/selftests/vm/pkey-x86.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,18 @@ static inline int cpu_has_pkeys(void)
104104
return 1;
105105
}
106106

107+
static inline int cpu_max_xsave_size(void)
108+
{
109+
unsigned long XSTATE_CPUID = 0xd;
110+
unsigned int eax;
111+
unsigned int ebx;
112+
unsigned int ecx;
113+
unsigned int edx;
114+
115+
__cpuid_count(XSTATE_CPUID, 0, eax, ebx, ecx, edx);
116+
return ecx;
117+
}
118+
107119
static inline u32 pkey_bit_position(int pkey)
108120
{
109121
return pkey * PKEY_BITS_PER_PKEY;

tools/testing/selftests/vm/protection_keys.c

Lines changed: 129 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@
1818
* do a plain mprotect() to a mprotect_pkey() area and make sure the pkey sticks
1919
*
2020
* Compile like this:
21-
* gcc -o protection_keys -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm
22-
* gcc -m32 -o protection_keys_32 -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm
21+
* gcc -mxsave -o protection_keys -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm
22+
* gcc -mxsave -m32 -o protection_keys_32 -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm
2323
*/
2424
#define _GNU_SOURCE
2525
#define __SANE_USERSPACE_TYPES__
2626
#include <errno.h>
27+
#include <linux/elf.h>
2728
#include <linux/futex.h>
2829
#include <time.h>
2930
#include <sys/time.h>
@@ -1550,6 +1551,129 @@ void test_implicit_mprotect_exec_only_memory(int *ptr, u16 pkey)
15501551
do_not_expect_pkey_fault("plain read on recently PROT_EXEC area");
15511552
}
15521553

1554+
#if defined(__i386__) || defined(__x86_64__)
1555+
void test_ptrace_modifies_pkru(int *ptr, u16 pkey)
1556+
{
1557+
u32 new_pkru;
1558+
pid_t child;
1559+
int status, ret;
1560+
int pkey_offset = pkey_reg_xstate_offset();
1561+
size_t xsave_size = cpu_max_xsave_size();
1562+
void *xsave;
1563+
u32 *pkey_register;
1564+
u64 *xstate_bv;
1565+
struct iovec iov;
1566+
1567+
new_pkru = ~read_pkey_reg();
1568+
/* Don't make PROT_EXEC mappings inaccessible */
1569+
new_pkru &= ~3;
1570+
1571+
child = fork();
1572+
pkey_assert(child >= 0);
1573+
dprintf3("[%d] fork() ret: %d\n", getpid(), child);
1574+
if (!child) {
1575+
ptrace(PTRACE_TRACEME, 0, 0, 0);
1576+
/* Stop and allow the tracer to modify PKRU directly */
1577+
raise(SIGSTOP);
1578+
1579+
/*
1580+
* need __read_pkey_reg() version so we do not do shadow_pkey_reg
1581+
* checking
1582+
*/
1583+
if (__read_pkey_reg() != new_pkru)
1584+
exit(1);
1585+
1586+
/* Stop and allow the tracer to clear XSTATE_BV for PKRU */
1587+
raise(SIGSTOP);
1588+
1589+
if (__read_pkey_reg() != 0)
1590+
exit(1);
1591+
1592+
/* Stop and allow the tracer to examine PKRU */
1593+
raise(SIGSTOP);
1594+
1595+
exit(0);
1596+
}
1597+
1598+
pkey_assert(child == waitpid(child, &status, 0));
1599+
dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
1600+
pkey_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
1601+
1602+
xsave = (void *)malloc(xsave_size);
1603+
pkey_assert(xsave > 0);
1604+
1605+
/* Modify the PKRU register directly */
1606+
iov.iov_base = xsave;
1607+
iov.iov_len = xsave_size;
1608+
ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
1609+
pkey_assert(ret == 0);
1610+
1611+
pkey_register = (u32 *)(xsave + pkey_offset);
1612+
pkey_assert(*pkey_register == read_pkey_reg());
1613+
1614+
*pkey_register = new_pkru;
1615+
1616+
ret = ptrace(PTRACE_SETREGSET, child, (void *)NT_X86_XSTATE, &iov);
1617+
pkey_assert(ret == 0);
1618+
1619+
/* Test that the modification is visible in ptrace before any execution */
1620+
memset(xsave, 0xCC, xsave_size);
1621+
ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
1622+
pkey_assert(ret == 0);
1623+
pkey_assert(*pkey_register == new_pkru);
1624+
1625+
/* Execute the tracee */
1626+
ret = ptrace(PTRACE_CONT, child, 0, 0);
1627+
pkey_assert(ret == 0);
1628+
1629+
/* Test that the tracee saw the PKRU value change */
1630+
pkey_assert(child == waitpid(child, &status, 0));
1631+
dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
1632+
pkey_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
1633+
1634+
/* Test that the modification is visible in ptrace after execution */
1635+
memset(xsave, 0xCC, xsave_size);
1636+
ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
1637+
pkey_assert(ret == 0);
1638+
pkey_assert(*pkey_register == new_pkru);
1639+
1640+
/* Clear the PKRU bit from XSTATE_BV */
1641+
xstate_bv = (u64 *)(xsave + 512);
1642+
*xstate_bv &= ~(1 << 9);
1643+
1644+
ret = ptrace(PTRACE_SETREGSET, child, (void *)NT_X86_XSTATE, &iov);
1645+
pkey_assert(ret == 0);
1646+
1647+
/* Test that the modification is visible in ptrace before any execution */
1648+
memset(xsave, 0xCC, xsave_size);
1649+
ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
1650+
pkey_assert(ret == 0);
1651+
pkey_assert(*pkey_register == 0);
1652+
1653+
ret = ptrace(PTRACE_CONT, child, 0, 0);
1654+
pkey_assert(ret == 0);
1655+
1656+
/* Test that the tracee saw the PKRU value go to 0 */
1657+
pkey_assert(child == waitpid(child, &status, 0));
1658+
dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
1659+
pkey_assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
1660+
1661+
/* Test that the modification is visible in ptrace after execution */
1662+
memset(xsave, 0xCC, xsave_size);
1663+
ret = ptrace(PTRACE_GETREGSET, child, (void *)NT_X86_XSTATE, &iov);
1664+
pkey_assert(ret == 0);
1665+
pkey_assert(*pkey_register == 0);
1666+
1667+
ret = ptrace(PTRACE_CONT, child, 0, 0);
1668+
pkey_assert(ret == 0);
1669+
pkey_assert(child == waitpid(child, &status, 0));
1670+
dprintf3("[%d] waitpid(%d) status: %x\n", getpid(), child, status);
1671+
pkey_assert(WIFEXITED(status));
1672+
pkey_assert(WEXITSTATUS(status) == 0);
1673+
free(xsave);
1674+
}
1675+
#endif
1676+
15531677
void test_mprotect_pkey_on_unsupported_cpu(int *ptr, u16 pkey)
15541678
{
15551679
int size = PAGE_SIZE;
@@ -1585,6 +1709,9 @@ void (*pkey_tests[])(int *ptr, u16 pkey) = {
15851709
test_pkey_syscalls_bad_args,
15861710
test_pkey_alloc_exhaust,
15871711
test_pkey_alloc_free_attach_pkey0,
1712+
#if defined(__i386__) || defined(__x86_64__)
1713+
test_ptrace_modifies_pkru,
1714+
#endif
15881715
};
15891716

15901717
void run_tests_once(void)

0 commit comments

Comments
 (0)