1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 2f08c3bdfSopenharmony_ci/* 3f08c3bdfSopenharmony_ci * Copyright (c) 2019 Red Hat, Inc. 4f08c3bdfSopenharmony_ci * 5f08c3bdfSopenharmony_ci * Memory Protection Keys for Userspace (PKU aka PKEYs) is a Skylake-SP 6f08c3bdfSopenharmony_ci * server feature that provides a mechanism for enforcing page-based 7f08c3bdfSopenharmony_ci * protections, but without requiring modification of the page tables 8f08c3bdfSopenharmony_ci * when an application changes protection domains. It works by dedicating 9f08c3bdfSopenharmony_ci * 4 previously ignored bits in each page table entry to a "protection key", 10f08c3bdfSopenharmony_ci * giving 16 possible keys. 11f08c3bdfSopenharmony_ci * 12f08c3bdfSopenharmony_ci * Basic method for PKEYs testing: 13f08c3bdfSopenharmony_ci * 1. test allocates a pkey(e.g. PKEY_DISABLE_ACCESS) via pkey_alloc() 14f08c3bdfSopenharmony_ci * 2. pkey_mprotect() apply this pkey to a piece of memory(buffer) 15f08c3bdfSopenharmony_ci * 3. check if access right of the buffer has been changed and take effect 16f08c3bdfSopenharmony_ci * 4. remove the access right(pkey) from this buffer via pkey_mprotect() 17f08c3bdfSopenharmony_ci * 5. check if buffer area can be read or write after removing pkey 18f08c3bdfSopenharmony_ci * 6. pkey_free() releases the pkey after using it 19f08c3bdfSopenharmony_ci * 20f08c3bdfSopenharmony_ci * Looping around this basic test on diffenrent types of memory. 21f08c3bdfSopenharmony_ci */ 22f08c3bdfSopenharmony_ci 23f08c3bdfSopenharmony_ci#define _GNU_SOURCE 24f08c3bdfSopenharmony_ci#include <stdio.h> 25f08c3bdfSopenharmony_ci#include <unistd.h> 26f08c3bdfSopenharmony_ci#include <errno.h> 27f08c3bdfSopenharmony_ci#include <stdlib.h> 28f08c3bdfSopenharmony_ci#include <sys/syscall.h> 29f08c3bdfSopenharmony_ci#include <sys/mman.h> 30f08c3bdfSopenharmony_ci#include <sys/wait.h> 31f08c3bdfSopenharmony_ci 32f08c3bdfSopenharmony_ci#include "pkey.h" 33f08c3bdfSopenharmony_ci 34f08c3bdfSopenharmony_ci#define TEST_FILE "pkey_testfile" 35f08c3bdfSopenharmony_ci#define STR "abcdefghijklmnopqrstuvwxyz12345\n" 36f08c3bdfSopenharmony_ci#define PATH_VM_NRHPS "/proc/sys/vm/nr_hugepages" 37f08c3bdfSopenharmony_ci 38f08c3bdfSopenharmony_cistatic int size; 39f08c3bdfSopenharmony_ci 40f08c3bdfSopenharmony_cistatic struct tcase { 41f08c3bdfSopenharmony_ci unsigned long flags; 42f08c3bdfSopenharmony_ci unsigned long access_rights; 43f08c3bdfSopenharmony_ci char *name; 44f08c3bdfSopenharmony_ci} tcases[] = { 45f08c3bdfSopenharmony_ci {0, PKEY_DISABLE_ACCESS, "PKEY_DISABLE_ACCESS"}, 46f08c3bdfSopenharmony_ci {0, PKEY_DISABLE_WRITE, "PKEY_DISABLE_WRITE"}, 47f08c3bdfSopenharmony_ci}; 48f08c3bdfSopenharmony_ci 49f08c3bdfSopenharmony_cistatic void setup(void) 50f08c3bdfSopenharmony_ci{ 51f08c3bdfSopenharmony_ci int i, fd; 52f08c3bdfSopenharmony_ci 53f08c3bdfSopenharmony_ci check_pkey_support(); 54f08c3bdfSopenharmony_ci 55f08c3bdfSopenharmony_ci if (tst_hugepages == test.hugepages.number) 56f08c3bdfSopenharmony_ci size = SAFE_READ_MEMINFO("Hugepagesize:") * 1024; 57f08c3bdfSopenharmony_ci else 58f08c3bdfSopenharmony_ci size = getpagesize(); 59f08c3bdfSopenharmony_ci 60f08c3bdfSopenharmony_ci fd = SAFE_OPEN(TEST_FILE, O_RDWR | O_CREAT, 0664); 61f08c3bdfSopenharmony_ci for (i = 0; i < 128; i++) 62f08c3bdfSopenharmony_ci SAFE_WRITE(SAFE_WRITE_ALL, fd, STR, strlen(STR)); 63f08c3bdfSopenharmony_ci 64f08c3bdfSopenharmony_ci SAFE_CLOSE(fd); 65f08c3bdfSopenharmony_ci} 66f08c3bdfSopenharmony_ci 67f08c3bdfSopenharmony_cistatic struct mmap_param { 68f08c3bdfSopenharmony_ci int prot; 69f08c3bdfSopenharmony_ci int flags; 70f08c3bdfSopenharmony_ci int fd; 71f08c3bdfSopenharmony_ci} mmap_params[] = { 72f08c3bdfSopenharmony_ci {PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1}, 73f08c3bdfSopenharmony_ci {PROT_READ, MAP_ANONYMOUS | MAP_SHARED, -1}, 74f08c3bdfSopenharmony_ci {PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB, -1}, 75f08c3bdfSopenharmony_ci {PROT_READ, MAP_ANONYMOUS | MAP_SHARED | MAP_HUGETLB, -1}, 76f08c3bdfSopenharmony_ci {PROT_READ, MAP_PRIVATE, 0}, 77f08c3bdfSopenharmony_ci {PROT_READ, MAP_SHARED, 0}, 78f08c3bdfSopenharmony_ci 79f08c3bdfSopenharmony_ci {PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1}, 80f08c3bdfSopenharmony_ci {PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1}, 81f08c3bdfSopenharmony_ci {PROT_WRITE, MAP_PRIVATE, 0}, 82f08c3bdfSopenharmony_ci {PROT_WRITE, MAP_SHARED, 0}, 83f08c3bdfSopenharmony_ci {PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB, -1}, 84f08c3bdfSopenharmony_ci {PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED | MAP_HUGETLB, -1}, 85f08c3bdfSopenharmony_ci 86f08c3bdfSopenharmony_ci {PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1}, 87f08c3bdfSopenharmony_ci {PROT_EXEC, MAP_ANONYMOUS | MAP_SHARED, -1}, 88f08c3bdfSopenharmony_ci {PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB, -1}, 89f08c3bdfSopenharmony_ci {PROT_EXEC, MAP_ANONYMOUS | MAP_SHARED | MAP_HUGETLB, -1}, 90f08c3bdfSopenharmony_ci {PROT_EXEC, MAP_PRIVATE, 0}, 91f08c3bdfSopenharmony_ci {PROT_EXEC, MAP_SHARED, 0}, 92f08c3bdfSopenharmony_ci 93f08c3bdfSopenharmony_ci {PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1}, 94f08c3bdfSopenharmony_ci {PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1}, 95f08c3bdfSopenharmony_ci {PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB, -1}, 96f08c3bdfSopenharmony_ci {PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED | MAP_HUGETLB, -1}, 97f08c3bdfSopenharmony_ci {PROT_READ | PROT_WRITE, MAP_PRIVATE, 0}, 98f08c3bdfSopenharmony_ci {PROT_READ | PROT_WRITE, MAP_SHARED, 0}, 99f08c3bdfSopenharmony_ci 100f08c3bdfSopenharmony_ci {PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1}, 101f08c3bdfSopenharmony_ci {PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_SHARED, -1}, 102f08c3bdfSopenharmony_ci {PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB, -1}, 103f08c3bdfSopenharmony_ci {PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_SHARED | MAP_HUGETLB, -1}, 104f08c3bdfSopenharmony_ci {PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, 0}, 105f08c3bdfSopenharmony_ci {PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, 0}, 106f08c3bdfSopenharmony_ci}; 107f08c3bdfSopenharmony_ci 108f08c3bdfSopenharmony_cistatic char *flag_to_str(int flags) 109f08c3bdfSopenharmony_ci{ 110f08c3bdfSopenharmony_ci switch (flags) { 111f08c3bdfSopenharmony_ci case MAP_PRIVATE: 112f08c3bdfSopenharmony_ci return "MAP_PRIVATE"; 113f08c3bdfSopenharmony_ci case MAP_SHARED: 114f08c3bdfSopenharmony_ci return "MAP_SHARED"; 115f08c3bdfSopenharmony_ci case MAP_ANONYMOUS | MAP_PRIVATE: 116f08c3bdfSopenharmony_ci return "MAP_ANONYMOUS|MAP_PRIVATE"; 117f08c3bdfSopenharmony_ci case MAP_ANONYMOUS | MAP_SHARED: 118f08c3bdfSopenharmony_ci return "MAP_ANONYMOUS|MAP_SHARED"; 119f08c3bdfSopenharmony_ci case MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB: 120f08c3bdfSopenharmony_ci return "MAP_ANONYMOUS|MAP_PRIVATE|MAP_HUGETLB"; 121f08c3bdfSopenharmony_ci case MAP_ANONYMOUS | MAP_SHARED | MAP_HUGETLB: 122f08c3bdfSopenharmony_ci return "MAP_ANONYMOUS|MAP_SHARED|MAP_HUGETLB"; 123f08c3bdfSopenharmony_ci default: 124f08c3bdfSopenharmony_ci return "UNKNOWN FLAGS"; 125f08c3bdfSopenharmony_ci } 126f08c3bdfSopenharmony_ci} 127f08c3bdfSopenharmony_ci 128f08c3bdfSopenharmony_cistatic void pkey_test(struct tcase *tc, struct mmap_param *mpa) 129f08c3bdfSopenharmony_ci{ 130f08c3bdfSopenharmony_ci pid_t pid; 131f08c3bdfSopenharmony_ci char *buffer; 132f08c3bdfSopenharmony_ci int pkey, status; 133f08c3bdfSopenharmony_ci int fd = mpa->fd; 134f08c3bdfSopenharmony_ci 135f08c3bdfSopenharmony_ci if (!tst_hugepages && (mpa->flags & MAP_HUGETLB)) { 136f08c3bdfSopenharmony_ci tst_res(TINFO, "Skip test on (%s) buffer", flag_to_str(mpa->flags)); 137f08c3bdfSopenharmony_ci return; 138f08c3bdfSopenharmony_ci } 139f08c3bdfSopenharmony_ci 140f08c3bdfSopenharmony_ci if (fd == 0) 141f08c3bdfSopenharmony_ci fd = SAFE_OPEN(TEST_FILE, O_RDWR | O_CREAT, 0664); 142f08c3bdfSopenharmony_ci 143f08c3bdfSopenharmony_ci buffer = SAFE_MMAP(NULL, size, mpa->prot, mpa->flags, fd, 0); 144f08c3bdfSopenharmony_ci 145f08c3bdfSopenharmony_ci pkey = ltp_pkey_alloc(tc->flags, tc->access_rights); 146f08c3bdfSopenharmony_ci if (pkey == -1) 147f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "pkey_alloc failed"); 148f08c3bdfSopenharmony_ci 149f08c3bdfSopenharmony_ci tst_res(TINFO, "Set %s on (%s) buffer", tc->name, flag_to_str(mpa->flags)); 150f08c3bdfSopenharmony_ci if (ltp_pkey_mprotect(buffer, size, mpa->prot, pkey) == -1) 151f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "pkey_mprotect failed"); 152f08c3bdfSopenharmony_ci 153f08c3bdfSopenharmony_ci pid = SAFE_FORK(); 154f08c3bdfSopenharmony_ci if (pid == 0) { 155f08c3bdfSopenharmony_ci tst_no_corefile(0); 156f08c3bdfSopenharmony_ci 157f08c3bdfSopenharmony_ci switch (tc->access_rights) { 158f08c3bdfSopenharmony_ci case PKEY_DISABLE_ACCESS: 159f08c3bdfSopenharmony_ci tst_res(TFAIL | TERRNO, 160f08c3bdfSopenharmony_ci "Read buffer success, buffer[0] = %d", *buffer); 161f08c3bdfSopenharmony_ci break; 162f08c3bdfSopenharmony_ci case PKEY_DISABLE_WRITE: 163f08c3bdfSopenharmony_ci *buffer = 'a'; 164f08c3bdfSopenharmony_ci tst_res(TFAIL | TERRNO, 165f08c3bdfSopenharmony_ci "Write buffer success, buffer[0] = %d", *buffer); 166f08c3bdfSopenharmony_ci break; 167f08c3bdfSopenharmony_ci } 168f08c3bdfSopenharmony_ci exit(0); 169f08c3bdfSopenharmony_ci } 170f08c3bdfSopenharmony_ci 171f08c3bdfSopenharmony_ci SAFE_WAITPID(pid, &status, 0); 172f08c3bdfSopenharmony_ci 173f08c3bdfSopenharmony_ci if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) 174f08c3bdfSopenharmony_ci tst_res(TPASS, "Child ended by %s as expected", tst_strsig(SIGSEGV)); 175f08c3bdfSopenharmony_ci else 176f08c3bdfSopenharmony_ci tst_res(TFAIL, "Child: %s", tst_strstatus(status)); 177f08c3bdfSopenharmony_ci 178f08c3bdfSopenharmony_ci tst_res(TINFO, "Remove %s from the buffer", tc->name); 179f08c3bdfSopenharmony_ci if (ltp_pkey_mprotect(buffer, size, mpa->prot, 0x0) == -1) 180f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "pkey_mprotect failed"); 181f08c3bdfSopenharmony_ci 182f08c3bdfSopenharmony_ci switch (mpa->prot) { 183f08c3bdfSopenharmony_ci case PROT_READ: 184f08c3bdfSopenharmony_ci tst_res(TPASS, "Read buffer success, buffer[0] = %d", *buffer); 185f08c3bdfSopenharmony_ci break; 186f08c3bdfSopenharmony_ci case PROT_WRITE: 187f08c3bdfSopenharmony_ci *buffer = 'a'; 188f08c3bdfSopenharmony_ci tst_res(TPASS, "Write buffer success, buffer[0] = %d", *buffer); 189f08c3bdfSopenharmony_ci break; 190f08c3bdfSopenharmony_ci case PROT_READ | PROT_WRITE: 191f08c3bdfSopenharmony_ci case PROT_READ | PROT_WRITE | PROT_EXEC: 192f08c3bdfSopenharmony_ci *buffer = 'a'; 193f08c3bdfSopenharmony_ci tst_res(TPASS, "Read & Write buffer success, buffer[0] = %d", *buffer); 194f08c3bdfSopenharmony_ci break; 195f08c3bdfSopenharmony_ci } 196f08c3bdfSopenharmony_ci 197f08c3bdfSopenharmony_ci if (fd >= 0) 198f08c3bdfSopenharmony_ci SAFE_CLOSE(fd); 199f08c3bdfSopenharmony_ci 200f08c3bdfSopenharmony_ci SAFE_MUNMAP(buffer, size); 201f08c3bdfSopenharmony_ci 202f08c3bdfSopenharmony_ci if (ltp_pkey_free(pkey) == -1) 203f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "pkey_free failed"); 204f08c3bdfSopenharmony_ci} 205f08c3bdfSopenharmony_ci 206f08c3bdfSopenharmony_cistatic void verify_pkey(unsigned int i) 207f08c3bdfSopenharmony_ci{ 208f08c3bdfSopenharmony_ci long unsigned int j; 209f08c3bdfSopenharmony_ci struct mmap_param *mpa; 210f08c3bdfSopenharmony_ci 211f08c3bdfSopenharmony_ci struct tcase *tc = &tcases[i]; 212f08c3bdfSopenharmony_ci 213f08c3bdfSopenharmony_ci for (j = 0; j < ARRAY_SIZE(mmap_params); j++) { 214f08c3bdfSopenharmony_ci mpa = &mmap_params[j]; 215f08c3bdfSopenharmony_ci 216f08c3bdfSopenharmony_ci pkey_test(tc, mpa); 217f08c3bdfSopenharmony_ci } 218f08c3bdfSopenharmony_ci} 219f08c3bdfSopenharmony_ci 220f08c3bdfSopenharmony_cistatic struct tst_test test = { 221f08c3bdfSopenharmony_ci .tcnt = ARRAY_SIZE(tcases), 222f08c3bdfSopenharmony_ci .needs_root = 1, 223f08c3bdfSopenharmony_ci .needs_tmpdir = 1, 224f08c3bdfSopenharmony_ci .forks_child = 1, 225f08c3bdfSopenharmony_ci .test = verify_pkey, 226f08c3bdfSopenharmony_ci .setup = setup, 227f08c3bdfSopenharmony_ci .hugepages = {1, TST_REQUEST}, 228f08c3bdfSopenharmony_ci}; 229