1baa489faSSeongJae Park // SPDX-License-Identifier: GPL-2.0 2baa489faSSeongJae Park /* 3baa489faSSeongJae Park * Copyright IBM Corporation, 2021 4baa489faSSeongJae Park * 5baa489faSSeongJae Park * Author: Mike Rapoport <rppt@linux.ibm.com> 6baa489faSSeongJae Park */ 7baa489faSSeongJae Park 8baa489faSSeongJae Park #define _GNU_SOURCE 9baa489faSSeongJae Park #include <sys/uio.h> 10baa489faSSeongJae Park #include <sys/mman.h> 11baa489faSSeongJae Park #include <sys/wait.h> 12baa489faSSeongJae Park #include <sys/types.h> 13baa489faSSeongJae Park #include <sys/ptrace.h> 14baa489faSSeongJae Park #include <sys/syscall.h> 15baa489faSSeongJae Park #include <sys/resource.h> 16baa489faSSeongJae Park #include <sys/capability.h> 17baa489faSSeongJae Park 18baa489faSSeongJae Park #include <stdlib.h> 19baa489faSSeongJae Park #include <string.h> 20*a5c6bc59SJohn Hubbard #include <asm-generic/unistd.h> 21baa489faSSeongJae Park #include <errno.h> 22baa489faSSeongJae Park #include <stdio.h> 23c139ca42SDavid Hildenbrand #include <fcntl.h> 24baa489faSSeongJae Park 25baa489faSSeongJae Park #include "../kselftest.h" 26baa489faSSeongJae Park 27baa489faSSeongJae Park #define fail(fmt, ...) ksft_test_result_fail(fmt, ##__VA_ARGS__) 28baa489faSSeongJae Park #define pass(fmt, ...) ksft_test_result_pass(fmt, ##__VA_ARGS__) 29baa489faSSeongJae Park #define skip(fmt, ...) ksft_test_result_skip(fmt, ##__VA_ARGS__) 30baa489faSSeongJae Park 31baa489faSSeongJae Park #define PATTERN 0x55 32baa489faSSeongJae Park 33baa489faSSeongJae Park static const int prot = PROT_READ | PROT_WRITE; 34baa489faSSeongJae Park static const int mode = MAP_SHARED; 35baa489faSSeongJae Park 36baa489faSSeongJae Park static unsigned long page_size; 37baa489faSSeongJae Park static unsigned long mlock_limit_cur; 38baa489faSSeongJae Park static unsigned long mlock_limit_max; 39baa489faSSeongJae Park 40baa489faSSeongJae Park static int memfd_secret(unsigned int flags) 41baa489faSSeongJae Park { 42baa489faSSeongJae Park return syscall(__NR_memfd_secret, flags); 43baa489faSSeongJae Park } 44baa489faSSeongJae Park 45baa489faSSeongJae Park static void test_file_apis(int fd) 46baa489faSSeongJae Park { 47baa489faSSeongJae Park char buf[64]; 48baa489faSSeongJae Park 49baa489faSSeongJae Park if ((read(fd, buf, sizeof(buf)) >= 0) || 50baa489faSSeongJae Park (write(fd, buf, sizeof(buf)) >= 0) || 51baa489faSSeongJae Park (pread(fd, buf, sizeof(buf), 0) >= 0) || 52baa489faSSeongJae Park (pwrite(fd, buf, sizeof(buf), 0) >= 0)) 53baa489faSSeongJae Park fail("unexpected file IO\n"); 54baa489faSSeongJae Park else 55baa489faSSeongJae Park pass("file IO is blocked as expected\n"); 56baa489faSSeongJae Park } 57baa489faSSeongJae Park 58baa489faSSeongJae Park static void test_mlock_limit(int fd) 59baa489faSSeongJae Park { 60baa489faSSeongJae Park size_t len; 61baa489faSSeongJae Park char *mem; 62baa489faSSeongJae Park 63baa489faSSeongJae Park len = mlock_limit_cur; 640aac13adSMuhammad Usama Anjum if (len % page_size != 0) 650aac13adSMuhammad Usama Anjum len = (len/page_size) * page_size; 660aac13adSMuhammad Usama Anjum 67baa489faSSeongJae Park mem = mmap(NULL, len, prot, mode, fd, 0); 68baa489faSSeongJae Park if (mem == MAP_FAILED) { 69baa489faSSeongJae Park fail("unable to mmap secret memory\n"); 70baa489faSSeongJae Park return; 71baa489faSSeongJae Park } 72baa489faSSeongJae Park munmap(mem, len); 73baa489faSSeongJae Park 74baa489faSSeongJae Park len = mlock_limit_max * 2; 75baa489faSSeongJae Park mem = mmap(NULL, len, prot, mode, fd, 0); 76baa489faSSeongJae Park if (mem != MAP_FAILED) { 77baa489faSSeongJae Park fail("unexpected mlock limit violation\n"); 78baa489faSSeongJae Park munmap(mem, len); 79baa489faSSeongJae Park return; 80baa489faSSeongJae Park } 81baa489faSSeongJae Park 82baa489faSSeongJae Park pass("mlock limit is respected\n"); 83baa489faSSeongJae Park } 84baa489faSSeongJae Park 85c139ca42SDavid Hildenbrand static void test_vmsplice(int fd, const char *desc) 86c139ca42SDavid Hildenbrand { 87c139ca42SDavid Hildenbrand ssize_t transferred; 88c139ca42SDavid Hildenbrand struct iovec iov; 89c139ca42SDavid Hildenbrand int pipefd[2]; 90c139ca42SDavid Hildenbrand char *mem; 91c139ca42SDavid Hildenbrand 92c139ca42SDavid Hildenbrand if (pipe(pipefd)) { 93c139ca42SDavid Hildenbrand fail("pipe failed: %s\n", strerror(errno)); 94c139ca42SDavid Hildenbrand return; 95c139ca42SDavid Hildenbrand } 96c139ca42SDavid Hildenbrand 97c139ca42SDavid Hildenbrand mem = mmap(NULL, page_size, prot, mode, fd, 0); 98c139ca42SDavid Hildenbrand if (mem == MAP_FAILED) { 99c139ca42SDavid Hildenbrand fail("Unable to mmap secret memory\n"); 100c139ca42SDavid Hildenbrand goto close_pipe; 101c139ca42SDavid Hildenbrand } 102c139ca42SDavid Hildenbrand 103c139ca42SDavid Hildenbrand /* 104c139ca42SDavid Hildenbrand * vmsplice() may use GUP-fast, which must also fail. Prefault the 105c139ca42SDavid Hildenbrand * page table, so GUP-fast could find it. 106c139ca42SDavid Hildenbrand */ 107c139ca42SDavid Hildenbrand memset(mem, PATTERN, page_size); 108c139ca42SDavid Hildenbrand 109c139ca42SDavid Hildenbrand iov.iov_base = mem; 110c139ca42SDavid Hildenbrand iov.iov_len = page_size; 111c139ca42SDavid Hildenbrand transferred = vmsplice(pipefd[1], &iov, 1, 0); 112c139ca42SDavid Hildenbrand 113c139ca42SDavid Hildenbrand if (transferred < 0 && errno == EFAULT) 114c139ca42SDavid Hildenbrand pass("vmsplice is blocked as expected with %s\n", desc); 115c139ca42SDavid Hildenbrand else 116c139ca42SDavid Hildenbrand fail("vmsplice: unexpected memory access with %s\n", desc); 117c139ca42SDavid Hildenbrand 118c139ca42SDavid Hildenbrand munmap(mem, page_size); 119c139ca42SDavid Hildenbrand close_pipe: 120c139ca42SDavid Hildenbrand close(pipefd[0]); 121c139ca42SDavid Hildenbrand close(pipefd[1]); 122c139ca42SDavid Hildenbrand } 123c139ca42SDavid Hildenbrand 124baa489faSSeongJae Park static void try_process_vm_read(int fd, int pipefd[2]) 125baa489faSSeongJae Park { 126baa489faSSeongJae Park struct iovec liov, riov; 127baa489faSSeongJae Park char buf[64]; 128baa489faSSeongJae Park char *mem; 129baa489faSSeongJae Park 130baa489faSSeongJae Park if (read(pipefd[0], &mem, sizeof(mem)) < 0) { 131baa489faSSeongJae Park fail("pipe write: %s\n", strerror(errno)); 132baa489faSSeongJae Park exit(KSFT_FAIL); 133baa489faSSeongJae Park } 134baa489faSSeongJae Park 135baa489faSSeongJae Park liov.iov_len = riov.iov_len = sizeof(buf); 136baa489faSSeongJae Park liov.iov_base = buf; 137baa489faSSeongJae Park riov.iov_base = mem; 138baa489faSSeongJae Park 139baa489faSSeongJae Park if (process_vm_readv(getppid(), &liov, 1, &riov, 1, 0) < 0) { 140baa489faSSeongJae Park if (errno == ENOSYS) 141baa489faSSeongJae Park exit(KSFT_SKIP); 142baa489faSSeongJae Park exit(KSFT_PASS); 143baa489faSSeongJae Park } 144baa489faSSeongJae Park 145baa489faSSeongJae Park exit(KSFT_FAIL); 146baa489faSSeongJae Park } 147baa489faSSeongJae Park 148baa489faSSeongJae Park static void try_ptrace(int fd, int pipefd[2]) 149baa489faSSeongJae Park { 150baa489faSSeongJae Park pid_t ppid = getppid(); 151baa489faSSeongJae Park int status; 152baa489faSSeongJae Park char *mem; 153baa489faSSeongJae Park long ret; 154baa489faSSeongJae Park 155baa489faSSeongJae Park if (read(pipefd[0], &mem, sizeof(mem)) < 0) { 156baa489faSSeongJae Park perror("pipe write"); 157baa489faSSeongJae Park exit(KSFT_FAIL); 158baa489faSSeongJae Park } 159baa489faSSeongJae Park 160baa489faSSeongJae Park ret = ptrace(PTRACE_ATTACH, ppid, 0, 0); 161baa489faSSeongJae Park if (ret) { 162baa489faSSeongJae Park perror("ptrace_attach"); 163baa489faSSeongJae Park exit(KSFT_FAIL); 164baa489faSSeongJae Park } 165baa489faSSeongJae Park 166baa489faSSeongJae Park ret = waitpid(ppid, &status, WUNTRACED); 167baa489faSSeongJae Park if ((ret != ppid) || !(WIFSTOPPED(status))) { 168baa489faSSeongJae Park fprintf(stderr, "weird waitppid result %ld stat %x\n", 169baa489faSSeongJae Park ret, status); 170baa489faSSeongJae Park exit(KSFT_FAIL); 171baa489faSSeongJae Park } 172baa489faSSeongJae Park 173baa489faSSeongJae Park if (ptrace(PTRACE_PEEKDATA, ppid, mem, 0)) 174baa489faSSeongJae Park exit(KSFT_PASS); 175baa489faSSeongJae Park 176baa489faSSeongJae Park exit(KSFT_FAIL); 177baa489faSSeongJae Park } 178baa489faSSeongJae Park 179baa489faSSeongJae Park static void check_child_status(pid_t pid, const char *name) 180baa489faSSeongJae Park { 181baa489faSSeongJae Park int status; 182baa489faSSeongJae Park 183baa489faSSeongJae Park waitpid(pid, &status, 0); 184baa489faSSeongJae Park 185baa489faSSeongJae Park if (WIFEXITED(status) && WEXITSTATUS(status) == KSFT_SKIP) { 186baa489faSSeongJae Park skip("%s is not supported\n", name); 187baa489faSSeongJae Park return; 188baa489faSSeongJae Park } 189baa489faSSeongJae Park 190baa489faSSeongJae Park if ((WIFEXITED(status) && WEXITSTATUS(status) == KSFT_PASS) || 191baa489faSSeongJae Park WIFSIGNALED(status)) { 192baa489faSSeongJae Park pass("%s is blocked as expected\n", name); 193baa489faSSeongJae Park return; 194baa489faSSeongJae Park } 195baa489faSSeongJae Park 196baa489faSSeongJae Park fail("%s: unexpected memory access\n", name); 197baa489faSSeongJae Park } 198baa489faSSeongJae Park 199baa489faSSeongJae Park static void test_remote_access(int fd, const char *name, 200baa489faSSeongJae Park void (*func)(int fd, int pipefd[2])) 201baa489faSSeongJae Park { 202baa489faSSeongJae Park int pipefd[2]; 203baa489faSSeongJae Park pid_t pid; 204baa489faSSeongJae Park char *mem; 205baa489faSSeongJae Park 206baa489faSSeongJae Park if (pipe(pipefd)) { 207baa489faSSeongJae Park fail("pipe failed: %s\n", strerror(errno)); 208baa489faSSeongJae Park return; 209baa489faSSeongJae Park } 210baa489faSSeongJae Park 211baa489faSSeongJae Park pid = fork(); 212baa489faSSeongJae Park if (pid < 0) { 213baa489faSSeongJae Park fail("fork failed: %s\n", strerror(errno)); 214baa489faSSeongJae Park return; 215baa489faSSeongJae Park } 216baa489faSSeongJae Park 217baa489faSSeongJae Park if (pid == 0) { 218baa489faSSeongJae Park func(fd, pipefd); 219baa489faSSeongJae Park return; 220baa489faSSeongJae Park } 221baa489faSSeongJae Park 222baa489faSSeongJae Park mem = mmap(NULL, page_size, prot, mode, fd, 0); 223baa489faSSeongJae Park if (mem == MAP_FAILED) { 224baa489faSSeongJae Park fail("Unable to mmap secret memory\n"); 225baa489faSSeongJae Park return; 226baa489faSSeongJae Park } 227baa489faSSeongJae Park 228baa489faSSeongJae Park memset(mem, PATTERN, page_size); 229baa489faSSeongJae Park 230baa489faSSeongJae Park if (write(pipefd[1], &mem, sizeof(mem)) < 0) { 231baa489faSSeongJae Park fail("pipe write: %s\n", strerror(errno)); 232baa489faSSeongJae Park return; 233baa489faSSeongJae Park } 234baa489faSSeongJae Park 235baa489faSSeongJae Park check_child_status(pid, name); 236baa489faSSeongJae Park } 237baa489faSSeongJae Park 238baa489faSSeongJae Park static void test_process_vm_read(int fd) 239baa489faSSeongJae Park { 240baa489faSSeongJae Park test_remote_access(fd, "process_vm_read", try_process_vm_read); 241baa489faSSeongJae Park } 242baa489faSSeongJae Park 243baa489faSSeongJae Park static void test_ptrace(int fd) 244baa489faSSeongJae Park { 245baa489faSSeongJae Park test_remote_access(fd, "ptrace", try_ptrace); 246baa489faSSeongJae Park } 247baa489faSSeongJae Park 248baa489faSSeongJae Park static int set_cap_limits(rlim_t max) 249baa489faSSeongJae Park { 250baa489faSSeongJae Park struct rlimit new; 251baa489faSSeongJae Park cap_t cap = cap_init(); 252baa489faSSeongJae Park 253baa489faSSeongJae Park new.rlim_cur = max; 254baa489faSSeongJae Park new.rlim_max = max; 255baa489faSSeongJae Park if (setrlimit(RLIMIT_MEMLOCK, &new)) { 256baa489faSSeongJae Park perror("setrlimit() returns error"); 257baa489faSSeongJae Park return -1; 258baa489faSSeongJae Park } 259baa489faSSeongJae Park 260baa489faSSeongJae Park /* drop capabilities including CAP_IPC_LOCK */ 261baa489faSSeongJae Park if (cap_set_proc(cap)) { 262baa489faSSeongJae Park perror("cap_set_proc() returns error"); 263baa489faSSeongJae Park return -2; 264baa489faSSeongJae Park } 265baa489faSSeongJae Park 266baa489faSSeongJae Park return 0; 267baa489faSSeongJae Park } 268baa489faSSeongJae Park 269baa489faSSeongJae Park static void prepare(void) 270baa489faSSeongJae Park { 271baa489faSSeongJae Park struct rlimit rlim; 272baa489faSSeongJae Park 273baa489faSSeongJae Park page_size = sysconf(_SC_PAGE_SIZE); 274baa489faSSeongJae Park if (!page_size) 275baa489faSSeongJae Park ksft_exit_fail_msg("Failed to get page size %s\n", 276baa489faSSeongJae Park strerror(errno)); 277baa489faSSeongJae Park 278baa489faSSeongJae Park if (getrlimit(RLIMIT_MEMLOCK, &rlim)) 279baa489faSSeongJae Park ksft_exit_fail_msg("Unable to detect mlock limit: %s\n", 280baa489faSSeongJae Park strerror(errno)); 281baa489faSSeongJae Park 282baa489faSSeongJae Park mlock_limit_cur = rlim.rlim_cur; 283baa489faSSeongJae Park mlock_limit_max = rlim.rlim_max; 284baa489faSSeongJae Park 285baa489faSSeongJae Park printf("page_size: %ld, mlock.soft: %ld, mlock.hard: %ld\n", 286baa489faSSeongJae Park page_size, mlock_limit_cur, mlock_limit_max); 287baa489faSSeongJae Park 288baa489faSSeongJae Park if (page_size > mlock_limit_cur) 289baa489faSSeongJae Park mlock_limit_cur = page_size; 290baa489faSSeongJae Park if (page_size > mlock_limit_max) 291baa489faSSeongJae Park mlock_limit_max = page_size; 292baa489faSSeongJae Park 293baa489faSSeongJae Park if (set_cap_limits(mlock_limit_max)) 294baa489faSSeongJae Park ksft_exit_fail_msg("Unable to set mlock limit: %s\n", 295baa489faSSeongJae Park strerror(errno)); 296baa489faSSeongJae Park } 297baa489faSSeongJae Park 298c139ca42SDavid Hildenbrand #define NUM_TESTS 6 299baa489faSSeongJae Park 300baa489faSSeongJae Park int main(int argc, char *argv[]) 301baa489faSSeongJae Park { 302baa489faSSeongJae Park int fd; 303baa489faSSeongJae Park 304baa489faSSeongJae Park prepare(); 305baa489faSSeongJae Park 306baa489faSSeongJae Park ksft_print_header(); 307baa489faSSeongJae Park ksft_set_plan(NUM_TESTS); 308baa489faSSeongJae Park 309baa489faSSeongJae Park fd = memfd_secret(0); 310baa489faSSeongJae Park if (fd < 0) { 311baa489faSSeongJae Park if (errno == ENOSYS) 312baa489faSSeongJae Park ksft_exit_skip("memfd_secret is not supported\n"); 313baa489faSSeongJae Park else 314baa489faSSeongJae Park ksft_exit_fail_msg("memfd_secret failed: %s\n", 315baa489faSSeongJae Park strerror(errno)); 316baa489faSSeongJae Park } 317c139ca42SDavid Hildenbrand if (ftruncate(fd, page_size)) 318c139ca42SDavid Hildenbrand ksft_exit_fail_msg("ftruncate failed: %s\n", strerror(errno)); 319baa489faSSeongJae Park 320baa489faSSeongJae Park test_mlock_limit(fd); 321baa489faSSeongJae Park test_file_apis(fd); 322c139ca42SDavid Hildenbrand /* 323c139ca42SDavid Hildenbrand * We have to run the first vmsplice test before any secretmem page was 324c139ca42SDavid Hildenbrand * allocated for this fd. 325c139ca42SDavid Hildenbrand */ 326c139ca42SDavid Hildenbrand test_vmsplice(fd, "fresh page"); 327c139ca42SDavid Hildenbrand test_vmsplice(fd, "existing page"); 328baa489faSSeongJae Park test_process_vm_read(fd); 329baa489faSSeongJae Park test_ptrace(fd); 330baa489faSSeongJae Park 331baa489faSSeongJae Park close(fd); 332baa489faSSeongJae Park 333baa489faSSeongJae Park ksft_finished(); 334baa489faSSeongJae Park } 335