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