|
|
@@ -72,10 +72,9 @@ extern void abort_hooks(void);
|
|
|
test_nr, iteration_nr); \
|
|
|
dprintf0("errno at assert: %d", errno); \
|
|
|
abort_hooks(); \
|
|
|
- assert(condition); \
|
|
|
+ exit(__LINE__); \
|
|
|
} \
|
|
|
} while (0)
|
|
|
-#define raw_assert(cond) assert(cond)
|
|
|
|
|
|
void cat_into_file(char *str, char *file)
|
|
|
{
|
|
|
@@ -87,12 +86,17 @@ void cat_into_file(char *str, char *file)
|
|
|
* these need to be raw because they are called under
|
|
|
* pkey_assert()
|
|
|
*/
|
|
|
- raw_assert(fd >= 0);
|
|
|
+ if (fd < 0) {
|
|
|
+ fprintf(stderr, "error opening '%s'\n", str);
|
|
|
+ perror("error: ");
|
|
|
+ exit(__LINE__);
|
|
|
+ }
|
|
|
+
|
|
|
ret = write(fd, str, strlen(str));
|
|
|
if (ret != strlen(str)) {
|
|
|
perror("write to file failed");
|
|
|
fprintf(stderr, "filename: '%s' str: '%s'\n", file, str);
|
|
|
- raw_assert(0);
|
|
|
+ exit(__LINE__);
|
|
|
}
|
|
|
close(fd);
|
|
|
}
|
|
|
@@ -191,26 +195,30 @@ void lots_o_noops_around_write(int *write_to_me)
|
|
|
#ifdef __i386__
|
|
|
|
|
|
#ifndef SYS_mprotect_key
|
|
|
-# define SYS_mprotect_key 380
|
|
|
+# define SYS_mprotect_key 380
|
|
|
#endif
|
|
|
+
|
|
|
#ifndef SYS_pkey_alloc
|
|
|
-# define SYS_pkey_alloc 381
|
|
|
-# define SYS_pkey_free 382
|
|
|
+# define SYS_pkey_alloc 381
|
|
|
+# define SYS_pkey_free 382
|
|
|
#endif
|
|
|
-#define REG_IP_IDX REG_EIP
|
|
|
-#define si_pkey_offset 0x14
|
|
|
+
|
|
|
+#define REG_IP_IDX REG_EIP
|
|
|
+#define si_pkey_offset 0x14
|
|
|
|
|
|
#else
|
|
|
|
|
|
#ifndef SYS_mprotect_key
|
|
|
-# define SYS_mprotect_key 329
|
|
|
+# define SYS_mprotect_key 329
|
|
|
#endif
|
|
|
+
|
|
|
#ifndef SYS_pkey_alloc
|
|
|
-# define SYS_pkey_alloc 330
|
|
|
-# define SYS_pkey_free 331
|
|
|
+# define SYS_pkey_alloc 330
|
|
|
+# define SYS_pkey_free 331
|
|
|
#endif
|
|
|
-#define REG_IP_IDX REG_RIP
|
|
|
-#define si_pkey_offset 0x20
|
|
|
+
|
|
|
+#define REG_IP_IDX REG_RIP
|
|
|
+#define si_pkey_offset 0x20
|
|
|
|
|
|
#endif
|
|
|
|
|
|
@@ -225,8 +233,14 @@ void dump_mem(void *dumpme, int len_bytes)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-#define SEGV_BNDERR 3 /* failed address bound checks */
|
|
|
-#define SEGV_PKUERR 4
|
|
|
+/* Failed address bound checks: */
|
|
|
+#ifndef SEGV_BNDERR
|
|
|
+# define SEGV_BNDERR 3
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifndef SEGV_PKUERR
|
|
|
+# define SEGV_PKUERR 4
|
|
|
+#endif
|
|
|
|
|
|
static char *si_code_str(int si_code)
|
|
|
{
|
|
|
@@ -289,13 +303,6 @@ void signal_handler(int signum, siginfo_t *si, void *vucontext)
|
|
|
dump_mem(pkru_ptr - 128, 256);
|
|
|
pkey_assert(*pkru_ptr);
|
|
|
|
|
|
- si_pkey_ptr = (u32 *)(((u8 *)si) + si_pkey_offset);
|
|
|
- dprintf1("si_pkey_ptr: %p\n", si_pkey_ptr);
|
|
|
- dump_mem(si_pkey_ptr - 8, 24);
|
|
|
- siginfo_pkey = *si_pkey_ptr;
|
|
|
- pkey_assert(siginfo_pkey < NR_PKEYS);
|
|
|
- last_si_pkey = siginfo_pkey;
|
|
|
-
|
|
|
if ((si->si_code == SEGV_MAPERR) ||
|
|
|
(si->si_code == SEGV_ACCERR) ||
|
|
|
(si->si_code == SEGV_BNDERR)) {
|
|
|
@@ -303,6 +310,13 @@ void signal_handler(int signum, siginfo_t *si, void *vucontext)
|
|
|
exit(4);
|
|
|
}
|
|
|
|
|
|
+ si_pkey_ptr = (u32 *)(((u8 *)si) + si_pkey_offset);
|
|
|
+ dprintf1("si_pkey_ptr: %p\n", si_pkey_ptr);
|
|
|
+ dump_mem((u8 *)si_pkey_ptr - 8, 24);
|
|
|
+ siginfo_pkey = *si_pkey_ptr;
|
|
|
+ pkey_assert(siginfo_pkey < NR_PKEYS);
|
|
|
+ last_si_pkey = siginfo_pkey;
|
|
|
+
|
|
|
dprintf1("signal pkru from xsave: %08x\n", *pkru_ptr);
|
|
|
/* need __rdpkru() version so we do not do shadow_pkru checking */
|
|
|
dprintf1("signal pkru from pkru: %08x\n", __rdpkru());
|
|
|
@@ -311,22 +325,6 @@ void signal_handler(int signum, siginfo_t *si, void *vucontext)
|
|
|
dprintf1("WARNING: set PRKU=0 to allow faulting instruction to continue\n");
|
|
|
pkru_faults++;
|
|
|
dprintf1("<<<<==================================================\n");
|
|
|
- return;
|
|
|
- if (trapno == 14) {
|
|
|
- fprintf(stderr,
|
|
|
- "ERROR: In signal handler, page fault, trapno = %d, ip = %016lx\n",
|
|
|
- trapno, ip);
|
|
|
- fprintf(stderr, "si_addr %p\n", si->si_addr);
|
|
|
- fprintf(stderr, "REG_ERR: %lx\n",
|
|
|
- (unsigned long)uctxt->uc_mcontext.gregs[REG_ERR]);
|
|
|
- exit(1);
|
|
|
- } else {
|
|
|
- fprintf(stderr, "unexpected trap %d! at 0x%lx\n", trapno, ip);
|
|
|
- fprintf(stderr, "si_addr %p\n", si->si_addr);
|
|
|
- fprintf(stderr, "REG_ERR: %lx\n",
|
|
|
- (unsigned long)uctxt->uc_mcontext.gregs[REG_ERR]);
|
|
|
- exit(2);
|
|
|
- }
|
|
|
dprint_in_signal = 0;
|
|
|
}
|
|
|
|
|
|
@@ -393,10 +391,15 @@ pid_t fork_lazy_child(void)
|
|
|
return forkret;
|
|
|
}
|
|
|
|
|
|
-#define PKEY_DISABLE_ACCESS 0x1
|
|
|
-#define PKEY_DISABLE_WRITE 0x2
|
|
|
+#ifndef PKEY_DISABLE_ACCESS
|
|
|
+# define PKEY_DISABLE_ACCESS 0x1
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifndef PKEY_DISABLE_WRITE
|
|
|
+# define PKEY_DISABLE_WRITE 0x2
|
|
|
+#endif
|
|
|
|
|
|
-u32 pkey_get(int pkey, unsigned long flags)
|
|
|
+static u32 hw_pkey_get(int pkey, unsigned long flags)
|
|
|
{
|
|
|
u32 mask = (PKEY_DISABLE_ACCESS|PKEY_DISABLE_WRITE);
|
|
|
u32 pkru = __rdpkru();
|
|
|
@@ -418,7 +421,7 @@ u32 pkey_get(int pkey, unsigned long flags)
|
|
|
return masked_pkru;
|
|
|
}
|
|
|
|
|
|
-int pkey_set(int pkey, unsigned long rights, unsigned long flags)
|
|
|
+static int hw_pkey_set(int pkey, unsigned long rights, unsigned long flags)
|
|
|
{
|
|
|
u32 mask = (PKEY_DISABLE_ACCESS|PKEY_DISABLE_WRITE);
|
|
|
u32 old_pkru = __rdpkru();
|
|
|
@@ -452,15 +455,15 @@ void pkey_disable_set(int pkey, int flags)
|
|
|
pkey, flags);
|
|
|
pkey_assert(flags & (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE));
|
|
|
|
|
|
- pkey_rights = pkey_get(pkey, syscall_flags);
|
|
|
+ pkey_rights = hw_pkey_get(pkey, syscall_flags);
|
|
|
|
|
|
- dprintf1("%s(%d) pkey_get(%d): %x\n", __func__,
|
|
|
+ dprintf1("%s(%d) hw_pkey_get(%d): %x\n", __func__,
|
|
|
pkey, pkey, pkey_rights);
|
|
|
pkey_assert(pkey_rights >= 0);
|
|
|
|
|
|
pkey_rights |= flags;
|
|
|
|
|
|
- ret = pkey_set(pkey, pkey_rights, syscall_flags);
|
|
|
+ ret = hw_pkey_set(pkey, pkey_rights, syscall_flags);
|
|
|
assert(!ret);
|
|
|
/*pkru and flags have the same format */
|
|
|
shadow_pkru |= flags << (pkey * 2);
|
|
|
@@ -468,8 +471,8 @@ void pkey_disable_set(int pkey, int flags)
|
|
|
|
|
|
pkey_assert(ret >= 0);
|
|
|
|
|
|
- pkey_rights = pkey_get(pkey, syscall_flags);
|
|
|
- dprintf1("%s(%d) pkey_get(%d): %x\n", __func__,
|
|
|
+ pkey_rights = hw_pkey_get(pkey, syscall_flags);
|
|
|
+ dprintf1("%s(%d) hw_pkey_get(%d): %x\n", __func__,
|
|
|
pkey, pkey, pkey_rights);
|
|
|
|
|
|
dprintf1("%s(%d) pkru: 0x%x\n", __func__, pkey, rdpkru());
|
|
|
@@ -483,24 +486,24 @@ void pkey_disable_clear(int pkey, int flags)
|
|
|
{
|
|
|
unsigned long syscall_flags = 0;
|
|
|
int ret;
|
|
|
- int pkey_rights = pkey_get(pkey, syscall_flags);
|
|
|
+ int pkey_rights = hw_pkey_get(pkey, syscall_flags);
|
|
|
u32 orig_pkru = rdpkru();
|
|
|
|
|
|
pkey_assert(flags & (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE));
|
|
|
|
|
|
- dprintf1("%s(%d) pkey_get(%d): %x\n", __func__,
|
|
|
+ dprintf1("%s(%d) hw_pkey_get(%d): %x\n", __func__,
|
|
|
pkey, pkey, pkey_rights);
|
|
|
pkey_assert(pkey_rights >= 0);
|
|
|
|
|
|
pkey_rights |= flags;
|
|
|
|
|
|
- ret = pkey_set(pkey, pkey_rights, 0);
|
|
|
+ ret = hw_pkey_set(pkey, pkey_rights, 0);
|
|
|
/* pkru and flags have the same format */
|
|
|
shadow_pkru &= ~(flags << (pkey * 2));
|
|
|
pkey_assert(ret >= 0);
|
|
|
|
|
|
- pkey_rights = pkey_get(pkey, syscall_flags);
|
|
|
- dprintf1("%s(%d) pkey_get(%d): %x\n", __func__,
|
|
|
+ pkey_rights = hw_pkey_get(pkey, syscall_flags);
|
|
|
+ dprintf1("%s(%d) hw_pkey_get(%d): %x\n", __func__,
|
|
|
pkey, pkey, pkey_rights);
|
|
|
|
|
|
dprintf1("%s(%d) pkru: 0x%x\n", __func__, pkey, rdpkru());
|
|
|
@@ -674,10 +677,12 @@ int mprotect_pkey(void *ptr, size_t size, unsigned long orig_prot,
|
|
|
struct pkey_malloc_record {
|
|
|
void *ptr;
|
|
|
long size;
|
|
|
+ int prot;
|
|
|
};
|
|
|
struct pkey_malloc_record *pkey_malloc_records;
|
|
|
+struct pkey_malloc_record *pkey_last_malloc_record;
|
|
|
long nr_pkey_malloc_records;
|
|
|
-void record_pkey_malloc(void *ptr, long size)
|
|
|
+void record_pkey_malloc(void *ptr, long size, int prot)
|
|
|
{
|
|
|
long i;
|
|
|
struct pkey_malloc_record *rec = NULL;
|
|
|
@@ -709,6 +714,8 @@ void record_pkey_malloc(void *ptr, long size)
|
|
|
(int)(rec - pkey_malloc_records), rec, ptr, size);
|
|
|
rec->ptr = ptr;
|
|
|
rec->size = size;
|
|
|
+ rec->prot = prot;
|
|
|
+ pkey_last_malloc_record = rec;
|
|
|
nr_pkey_malloc_records++;
|
|
|
}
|
|
|
|
|
|
@@ -753,7 +760,7 @@ void *malloc_pkey_with_mprotect(long size, int prot, u16 pkey)
|
|
|
pkey_assert(ptr != (void *)-1);
|
|
|
ret = mprotect_pkey((void *)ptr, PAGE_SIZE, prot, pkey);
|
|
|
pkey_assert(!ret);
|
|
|
- record_pkey_malloc(ptr, size);
|
|
|
+ record_pkey_malloc(ptr, size, prot);
|
|
|
rdpkru();
|
|
|
|
|
|
dprintf1("%s() for pkey %d @ %p\n", __func__, pkey, ptr);
|
|
|
@@ -774,7 +781,7 @@ void *malloc_pkey_anon_huge(long size, int prot, u16 pkey)
|
|
|
size = ALIGN_UP(size, HPAGE_SIZE * 2);
|
|
|
ptr = mmap(NULL, size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
|
|
pkey_assert(ptr != (void *)-1);
|
|
|
- record_pkey_malloc(ptr, size);
|
|
|
+ record_pkey_malloc(ptr, size, prot);
|
|
|
mprotect_pkey(ptr, size, prot, pkey);
|
|
|
|
|
|
dprintf1("unaligned ptr: %p\n", ptr);
|
|
|
@@ -847,7 +854,7 @@ void *malloc_pkey_hugetlb(long size, int prot, u16 pkey)
|
|
|
pkey_assert(ptr != (void *)-1);
|
|
|
mprotect_pkey(ptr, size, prot, pkey);
|
|
|
|
|
|
- record_pkey_malloc(ptr, size);
|
|
|
+ record_pkey_malloc(ptr, size, prot);
|
|
|
|
|
|
dprintf1("mmap()'d hugetlbfs for pkey %d @ %p\n", pkey, ptr);
|
|
|
return ptr;
|
|
|
@@ -869,7 +876,7 @@ void *malloc_pkey_mmap_dax(long size, int prot, u16 pkey)
|
|
|
|
|
|
mprotect_pkey(ptr, size, prot, pkey);
|
|
|
|
|
|
- record_pkey_malloc(ptr, size);
|
|
|
+ record_pkey_malloc(ptr, size, prot);
|
|
|
|
|
|
dprintf1("mmap()'d for pkey %d @ %p\n", pkey, ptr);
|
|
|
close(fd);
|
|
|
@@ -918,13 +925,21 @@ void *malloc_pkey(long size, int prot, u16 pkey)
|
|
|
}
|
|
|
|
|
|
int last_pkru_faults;
|
|
|
+#define UNKNOWN_PKEY -2
|
|
|
void expected_pk_fault(int pkey)
|
|
|
{
|
|
|
dprintf2("%s(): last_pkru_faults: %d pkru_faults: %d\n",
|
|
|
__func__, last_pkru_faults, pkru_faults);
|
|
|
dprintf2("%s(%d): last_si_pkey: %d\n", __func__, pkey, last_si_pkey);
|
|
|
pkey_assert(last_pkru_faults + 1 == pkru_faults);
|
|
|
- pkey_assert(last_si_pkey == pkey);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * For exec-only memory, we do not know the pkey in
|
|
|
+ * advance, so skip this check.
|
|
|
+ */
|
|
|
+ if (pkey != UNKNOWN_PKEY)
|
|
|
+ pkey_assert(last_si_pkey == pkey);
|
|
|
+
|
|
|
/*
|
|
|
* The signal handler shold have cleared out PKRU to let the
|
|
|
* test program continue. We now have to restore it.
|
|
|
@@ -939,10 +954,11 @@ void expected_pk_fault(int pkey)
|
|
|
last_si_pkey = -1;
|
|
|
}
|
|
|
|
|
|
-void do_not_expect_pk_fault(void)
|
|
|
-{
|
|
|
- pkey_assert(last_pkru_faults == pkru_faults);
|
|
|
-}
|
|
|
+#define do_not_expect_pk_fault(msg) do { \
|
|
|
+ if (last_pkru_faults != pkru_faults) \
|
|
|
+ dprintf0("unexpected PK fault: %s\n", msg); \
|
|
|
+ pkey_assert(last_pkru_faults == pkru_faults); \
|
|
|
+} while (0)
|
|
|
|
|
|
int test_fds[10] = { -1 };
|
|
|
int nr_test_fds;
|
|
|
@@ -1151,12 +1167,15 @@ void test_pkey_alloc_exhaust(int *ptr, u16 pkey)
|
|
|
pkey_assert(i < NR_PKEYS*2);
|
|
|
|
|
|
/*
|
|
|
- * There are 16 pkeys supported in hardware. One is taken
|
|
|
- * up for the default (0) and another can be taken up by
|
|
|
- * an execute-only mapping. Ensure that we can allocate
|
|
|
- * at least 14 (16-2).
|
|
|
+ * There are 16 pkeys supported in hardware. Three are
|
|
|
+ * allocated by the time we get here:
|
|
|
+ * 1. The default key (0)
|
|
|
+ * 2. One possibly consumed by an execute-only mapping.
|
|
|
+ * 3. One allocated by the test code and passed in via
|
|
|
+ * 'pkey' to this function.
|
|
|
+ * Ensure that we can allocate at least another 13 (16-3).
|
|
|
*/
|
|
|
- pkey_assert(i >= NR_PKEYS-2);
|
|
|
+ pkey_assert(i >= NR_PKEYS-3);
|
|
|
|
|
|
for (i = 0; i < nr_allocated_pkeys; i++) {
|
|
|
err = sys_pkey_free(allocated_pkeys[i]);
|
|
|
@@ -1165,6 +1184,35 @@ void test_pkey_alloc_exhaust(int *ptr, u16 pkey)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * pkey 0 is special. It is allocated by default, so you do not
|
|
|
+ * have to call pkey_alloc() to use it first. Make sure that it
|
|
|
+ * is usable.
|
|
|
+ */
|
|
|
+void test_mprotect_with_pkey_0(int *ptr, u16 pkey)
|
|
|
+{
|
|
|
+ long size;
|
|
|
+ int prot;
|
|
|
+
|
|
|
+ assert(pkey_last_malloc_record);
|
|
|
+ size = pkey_last_malloc_record->size;
|
|
|
+ /*
|
|
|
+ * This is a bit of a hack. But mprotect() requires
|
|
|
+ * huge-page-aligned sizes when operating on hugetlbfs.
|
|
|
+ * So, make sure that we use something that's a multiple
|
|
|
+ * of a huge page when we can.
|
|
|
+ */
|
|
|
+ if (size >= HPAGE_SIZE)
|
|
|
+ size = HPAGE_SIZE;
|
|
|
+ prot = pkey_last_malloc_record->prot;
|
|
|
+
|
|
|
+ /* Use pkey 0 */
|
|
|
+ mprotect_pkey(ptr, size, prot, 0);
|
|
|
+
|
|
|
+ /* Make sure that we can set it back to the original pkey. */
|
|
|
+ mprotect_pkey(ptr, size, prot, pkey);
|
|
|
+}
|
|
|
+
|
|
|
void test_ptrace_of_child(int *ptr, u16 pkey)
|
|
|
{
|
|
|
__attribute__((__unused__)) int peek_result;
|
|
|
@@ -1228,7 +1276,7 @@ void test_ptrace_of_child(int *ptr, u16 pkey)
|
|
|
pkey_assert(ret != -1);
|
|
|
/* Now access from the current task, and expect NO exception: */
|
|
|
peek_result = read_ptr(plain_ptr);
|
|
|
- do_not_expect_pk_fault();
|
|
|
+ do_not_expect_pk_fault("read plain pointer after ptrace");
|
|
|
|
|
|
ret = ptrace(PTRACE_DETACH, child_pid, ignored, 0);
|
|
|
pkey_assert(ret != -1);
|
|
|
@@ -1241,12 +1289,9 @@ void test_ptrace_of_child(int *ptr, u16 pkey)
|
|
|
free(plain_ptr_unaligned);
|
|
|
}
|
|
|
|
|
|
-void test_executing_on_unreadable_memory(int *ptr, u16 pkey)
|
|
|
+void *get_pointer_to_instructions(void)
|
|
|
{
|
|
|
void *p1;
|
|
|
- int scratch;
|
|
|
- int ptr_contents;
|
|
|
- int ret;
|
|
|
|
|
|
p1 = ALIGN_PTR_UP(&lots_o_noops_around_write, PAGE_SIZE);
|
|
|
dprintf3("&lots_o_noops: %p\n", &lots_o_noops_around_write);
|
|
|
@@ -1256,7 +1301,23 @@ void test_executing_on_unreadable_memory(int *ptr, u16 pkey)
|
|
|
/* Point 'p1' at the *second* page of the function: */
|
|
|
p1 += PAGE_SIZE;
|
|
|
|
|
|
+ /*
|
|
|
+ * Try to ensure we fault this in on next touch to ensure
|
|
|
+ * we get an instruction fault as opposed to a data one
|
|
|
+ */
|
|
|
madvise(p1, PAGE_SIZE, MADV_DONTNEED);
|
|
|
+
|
|
|
+ return p1;
|
|
|
+}
|
|
|
+
|
|
|
+void test_executing_on_unreadable_memory(int *ptr, u16 pkey)
|
|
|
+{
|
|
|
+ void *p1;
|
|
|
+ int scratch;
|
|
|
+ int ptr_contents;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ p1 = get_pointer_to_instructions();
|
|
|
lots_o_noops_around_write(&scratch);
|
|
|
ptr_contents = read_ptr(p1);
|
|
|
dprintf2("ptr (%p) contents@%d: %x\n", p1, __LINE__, ptr_contents);
|
|
|
@@ -1272,12 +1333,55 @@ void test_executing_on_unreadable_memory(int *ptr, u16 pkey)
|
|
|
*/
|
|
|
madvise(p1, PAGE_SIZE, MADV_DONTNEED);
|
|
|
lots_o_noops_around_write(&scratch);
|
|
|
- do_not_expect_pk_fault();
|
|
|
+ do_not_expect_pk_fault("executing on PROT_EXEC memory");
|
|
|
ptr_contents = read_ptr(p1);
|
|
|
dprintf2("ptr (%p) contents@%d: %x\n", p1, __LINE__, ptr_contents);
|
|
|
expected_pk_fault(pkey);
|
|
|
}
|
|
|
|
|
|
+void test_implicit_mprotect_exec_only_memory(int *ptr, u16 pkey)
|
|
|
+{
|
|
|
+ void *p1;
|
|
|
+ int scratch;
|
|
|
+ int ptr_contents;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ dprintf1("%s() start\n", __func__);
|
|
|
+
|
|
|
+ p1 = get_pointer_to_instructions();
|
|
|
+ lots_o_noops_around_write(&scratch);
|
|
|
+ ptr_contents = read_ptr(p1);
|
|
|
+ dprintf2("ptr (%p) contents@%d: %x\n", p1, __LINE__, ptr_contents);
|
|
|
+
|
|
|
+ /* Use a *normal* mprotect(), not mprotect_pkey(): */
|
|
|
+ ret = mprotect(p1, PAGE_SIZE, PROT_EXEC);
|
|
|
+ pkey_assert(!ret);
|
|
|
+
|
|
|
+ dprintf2("pkru: %x\n", rdpkru());
|
|
|
+
|
|
|
+ /* Make sure this is an *instruction* fault */
|
|
|
+ madvise(p1, PAGE_SIZE, MADV_DONTNEED);
|
|
|
+ lots_o_noops_around_write(&scratch);
|
|
|
+ do_not_expect_pk_fault("executing on PROT_EXEC memory");
|
|
|
+ ptr_contents = read_ptr(p1);
|
|
|
+ dprintf2("ptr (%p) contents@%d: %x\n", p1, __LINE__, ptr_contents);
|
|
|
+ expected_pk_fault(UNKNOWN_PKEY);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Put the memory back to non-PROT_EXEC. Should clear the
|
|
|
+ * exec-only pkey off the VMA and allow it to be readable
|
|
|
+ * again. Go to PROT_NONE first to check for a kernel bug
|
|
|
+ * that did not clear the pkey when doing PROT_NONE.
|
|
|
+ */
|
|
|
+ ret = mprotect(p1, PAGE_SIZE, PROT_NONE);
|
|
|
+ pkey_assert(!ret);
|
|
|
+
|
|
|
+ ret = mprotect(p1, PAGE_SIZE, PROT_READ|PROT_EXEC);
|
|
|
+ pkey_assert(!ret);
|
|
|
+ ptr_contents = read_ptr(p1);
|
|
|
+ do_not_expect_pk_fault("plain read on recently PROT_EXEC area");
|
|
|
+}
|
|
|
+
|
|
|
void test_mprotect_pkey_on_unsupported_cpu(int *ptr, u16 pkey)
|
|
|
{
|
|
|
int size = PAGE_SIZE;
|
|
|
@@ -1302,6 +1406,8 @@ void (*pkey_tests[])(int *ptr, u16 pkey) = {
|
|
|
test_kernel_gup_of_access_disabled_region,
|
|
|
test_kernel_gup_write_to_write_disabled_region,
|
|
|
test_executing_on_unreadable_memory,
|
|
|
+ test_implicit_mprotect_exec_only_memory,
|
|
|
+ test_mprotect_with_pkey_0,
|
|
|
test_ptrace_of_child,
|
|
|
test_pkey_syscalls_on_non_allocated_pkey,
|
|
|
test_pkey_syscalls_bad_args,
|