|
18 | 18 | * do a plain mprotect() to a mprotect_pkey() area and make sure the pkey sticks
|
19 | 19 | *
|
20 | 20 | * 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 |
23 | 23 | */
|
24 | 24 | #define _GNU_SOURCE
|
25 | 25 | #define __SANE_USERSPACE_TYPES__
|
26 | 26 | #include <errno.h>
|
| 27 | +#include <linux/elf.h> |
27 | 28 | #include <linux/futex.h>
|
28 | 29 | #include <time.h>
|
29 | 30 | #include <sys/time.h>
|
@@ -1550,6 +1551,129 @@ void test_implicit_mprotect_exec_only_memory(int *ptr, u16 pkey)
|
1550 | 1551 | do_not_expect_pkey_fault("plain read on recently PROT_EXEC area");
|
1551 | 1552 | }
|
1552 | 1553 |
|
| 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 | + |
1553 | 1677 | void test_mprotect_pkey_on_unsupported_cpu(int *ptr, u16 pkey)
|
1554 | 1678 | {
|
1555 | 1679 | int size = PAGE_SIZE;
|
@@ -1585,6 +1709,9 @@ void (*pkey_tests[])(int *ptr, u16 pkey) = {
|
1585 | 1709 | test_pkey_syscalls_bad_args,
|
1586 | 1710 | test_pkey_alloc_exhaust,
|
1587 | 1711 | test_pkey_alloc_free_attach_pkey0,
|
| 1712 | +#if defined(__i386__) || defined(__x86_64__) |
| 1713 | + test_ptrace_modifies_pkru, |
| 1714 | +#endif |
1588 | 1715 | };
|
1589 | 1716 |
|
1590 | 1717 | void run_tests_once(void)
|
|
0 commit comments