335 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright IBM Corporation, 20214 *5 * Author: Mike Rapoport <rppt@linux.ibm.com>6 */7 8#define _GNU_SOURCE9#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 0x5532 33static const int prot = PROT_READ | PROT_WRITE;34static const int mode = MAP_SHARED;35 36static unsigned long page_size;37static unsigned long mlock_limit_cur;38static unsigned long mlock_limit_max;39 40static int memfd_secret(unsigned int flags)41{42 return syscall(__NR_memfd_secret, flags);43}44 45static 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 else55 pass("file IO is blocked as expected\n");56}57 58static 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 85static 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 the105 * 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 else116 fail("vmsplice: unexpected memory access with %s\n", desc);117 118 munmap(mem, page_size);119close_pipe:120 close(pipefd[0]);121 close(pipefd[1]);122}123 124static 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 148static 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 179static 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 199static 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 238static void test_process_vm_read(int fd)239{240 test_remote_access(fd, "process_vm_read", try_process_vm_read);241}242 243static void test_ptrace(int fd)244{245 test_remote_access(fd, "ptrace", try_ptrace);246}247 248static 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 269static 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 6299 300int 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 else314 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 was324 * 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