162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright IBM Corporation, 2021 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Mike Rapoport <rppt@linux.ibm.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define _GNU_SOURCE 962306a36Sopenharmony_ci#include <sys/uio.h> 1062306a36Sopenharmony_ci#include <sys/mman.h> 1162306a36Sopenharmony_ci#include <sys/wait.h> 1262306a36Sopenharmony_ci#include <sys/types.h> 1362306a36Sopenharmony_ci#include <sys/ptrace.h> 1462306a36Sopenharmony_ci#include <sys/syscall.h> 1562306a36Sopenharmony_ci#include <sys/resource.h> 1662306a36Sopenharmony_ci#include <sys/capability.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <stdlib.h> 1962306a36Sopenharmony_ci#include <string.h> 2062306a36Sopenharmony_ci#include <unistd.h> 2162306a36Sopenharmony_ci#include <errno.h> 2262306a36Sopenharmony_ci#include <stdio.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "../kselftest.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define fail(fmt, ...) ksft_test_result_fail(fmt, ##__VA_ARGS__) 2762306a36Sopenharmony_ci#define pass(fmt, ...) ksft_test_result_pass(fmt, ##__VA_ARGS__) 2862306a36Sopenharmony_ci#define skip(fmt, ...) ksft_test_result_skip(fmt, ##__VA_ARGS__) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#ifdef __NR_memfd_secret 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define PATTERN 0x55 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic const int prot = PROT_READ | PROT_WRITE; 3562306a36Sopenharmony_cistatic const int mode = MAP_SHARED; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic unsigned long page_size; 3862306a36Sopenharmony_cistatic unsigned long mlock_limit_cur; 3962306a36Sopenharmony_cistatic unsigned long mlock_limit_max; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int memfd_secret(unsigned int flags) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci return syscall(__NR_memfd_secret, flags); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic void test_file_apis(int fd) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci char buf[64]; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if ((read(fd, buf, sizeof(buf)) >= 0) || 5162306a36Sopenharmony_ci (write(fd, buf, sizeof(buf)) >= 0) || 5262306a36Sopenharmony_ci (pread(fd, buf, sizeof(buf), 0) >= 0) || 5362306a36Sopenharmony_ci (pwrite(fd, buf, sizeof(buf), 0) >= 0)) 5462306a36Sopenharmony_ci fail("unexpected file IO\n"); 5562306a36Sopenharmony_ci else 5662306a36Sopenharmony_ci pass("file IO is blocked as expected\n"); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void test_mlock_limit(int fd) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci size_t len; 6262306a36Sopenharmony_ci char *mem; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci len = mlock_limit_cur; 6562306a36Sopenharmony_ci if (len % page_size != 0) 6662306a36Sopenharmony_ci len = (len/page_size) * page_size; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci mem = mmap(NULL, len, prot, mode, fd, 0); 6962306a36Sopenharmony_ci if (mem == MAP_FAILED) { 7062306a36Sopenharmony_ci fail("unable to mmap secret memory\n"); 7162306a36Sopenharmony_ci return; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci munmap(mem, len); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci len = mlock_limit_max * 2; 7662306a36Sopenharmony_ci mem = mmap(NULL, len, prot, mode, fd, 0); 7762306a36Sopenharmony_ci if (mem != MAP_FAILED) { 7862306a36Sopenharmony_ci fail("unexpected mlock limit violation\n"); 7962306a36Sopenharmony_ci munmap(mem, len); 8062306a36Sopenharmony_ci return; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci pass("mlock limit is respected\n"); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void try_process_vm_read(int fd, int pipefd[2]) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct iovec liov, riov; 8962306a36Sopenharmony_ci char buf[64]; 9062306a36Sopenharmony_ci char *mem; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (read(pipefd[0], &mem, sizeof(mem)) < 0) { 9362306a36Sopenharmony_ci fail("pipe write: %s\n", strerror(errno)); 9462306a36Sopenharmony_ci exit(KSFT_FAIL); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci liov.iov_len = riov.iov_len = sizeof(buf); 9862306a36Sopenharmony_ci liov.iov_base = buf; 9962306a36Sopenharmony_ci riov.iov_base = mem; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (process_vm_readv(getppid(), &liov, 1, &riov, 1, 0) < 0) { 10262306a36Sopenharmony_ci if (errno == ENOSYS) 10362306a36Sopenharmony_ci exit(KSFT_SKIP); 10462306a36Sopenharmony_ci exit(KSFT_PASS); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci exit(KSFT_FAIL); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void try_ptrace(int fd, int pipefd[2]) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci pid_t ppid = getppid(); 11362306a36Sopenharmony_ci int status; 11462306a36Sopenharmony_ci char *mem; 11562306a36Sopenharmony_ci long ret; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (read(pipefd[0], &mem, sizeof(mem)) < 0) { 11862306a36Sopenharmony_ci perror("pipe write"); 11962306a36Sopenharmony_ci exit(KSFT_FAIL); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci ret = ptrace(PTRACE_ATTACH, ppid, 0, 0); 12362306a36Sopenharmony_ci if (ret) { 12462306a36Sopenharmony_ci perror("ptrace_attach"); 12562306a36Sopenharmony_ci exit(KSFT_FAIL); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci ret = waitpid(ppid, &status, WUNTRACED); 12962306a36Sopenharmony_ci if ((ret != ppid) || !(WIFSTOPPED(status))) { 13062306a36Sopenharmony_ci fprintf(stderr, "weird waitppid result %ld stat %x\n", 13162306a36Sopenharmony_ci ret, status); 13262306a36Sopenharmony_ci exit(KSFT_FAIL); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (ptrace(PTRACE_PEEKDATA, ppid, mem, 0)) 13662306a36Sopenharmony_ci exit(KSFT_PASS); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci exit(KSFT_FAIL); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void check_child_status(pid_t pid, const char *name) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci int status; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci waitpid(pid, &status, 0); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (WIFEXITED(status) && WEXITSTATUS(status) == KSFT_SKIP) { 14862306a36Sopenharmony_ci skip("%s is not supported\n", name); 14962306a36Sopenharmony_ci return; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if ((WIFEXITED(status) && WEXITSTATUS(status) == KSFT_PASS) || 15362306a36Sopenharmony_ci WIFSIGNALED(status)) { 15462306a36Sopenharmony_ci pass("%s is blocked as expected\n", name); 15562306a36Sopenharmony_ci return; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci fail("%s: unexpected memory access\n", name); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void test_remote_access(int fd, const char *name, 16262306a36Sopenharmony_ci void (*func)(int fd, int pipefd[2])) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci int pipefd[2]; 16562306a36Sopenharmony_ci pid_t pid; 16662306a36Sopenharmony_ci char *mem; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (pipe(pipefd)) { 16962306a36Sopenharmony_ci fail("pipe failed: %s\n", strerror(errno)); 17062306a36Sopenharmony_ci return; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci pid = fork(); 17462306a36Sopenharmony_ci if (pid < 0) { 17562306a36Sopenharmony_ci fail("fork failed: %s\n", strerror(errno)); 17662306a36Sopenharmony_ci return; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (pid == 0) { 18062306a36Sopenharmony_ci func(fd, pipefd); 18162306a36Sopenharmony_ci return; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci mem = mmap(NULL, page_size, prot, mode, fd, 0); 18562306a36Sopenharmony_ci if (mem == MAP_FAILED) { 18662306a36Sopenharmony_ci fail("Unable to mmap secret memory\n"); 18762306a36Sopenharmony_ci return; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci ftruncate(fd, page_size); 19162306a36Sopenharmony_ci memset(mem, PATTERN, page_size); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (write(pipefd[1], &mem, sizeof(mem)) < 0) { 19462306a36Sopenharmony_ci fail("pipe write: %s\n", strerror(errno)); 19562306a36Sopenharmony_ci return; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci check_child_status(pid, name); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic void test_process_vm_read(int fd) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci test_remote_access(fd, "process_vm_read", try_process_vm_read); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void test_ptrace(int fd) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci test_remote_access(fd, "ptrace", try_ptrace); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int set_cap_limits(rlim_t max) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct rlimit new; 21462306a36Sopenharmony_ci cap_t cap = cap_init(); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci new.rlim_cur = max; 21762306a36Sopenharmony_ci new.rlim_max = max; 21862306a36Sopenharmony_ci if (setrlimit(RLIMIT_MEMLOCK, &new)) { 21962306a36Sopenharmony_ci perror("setrlimit() returns error"); 22062306a36Sopenharmony_ci return -1; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* drop capabilities including CAP_IPC_LOCK */ 22462306a36Sopenharmony_ci if (cap_set_proc(cap)) { 22562306a36Sopenharmony_ci perror("cap_set_proc() returns error"); 22662306a36Sopenharmony_ci return -2; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return 0; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic void prepare(void) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct rlimit rlim; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci page_size = sysconf(_SC_PAGE_SIZE); 23762306a36Sopenharmony_ci if (!page_size) 23862306a36Sopenharmony_ci ksft_exit_fail_msg("Failed to get page size %s\n", 23962306a36Sopenharmony_ci strerror(errno)); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (getrlimit(RLIMIT_MEMLOCK, &rlim)) 24262306a36Sopenharmony_ci ksft_exit_fail_msg("Unable to detect mlock limit: %s\n", 24362306a36Sopenharmony_ci strerror(errno)); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci mlock_limit_cur = rlim.rlim_cur; 24662306a36Sopenharmony_ci mlock_limit_max = rlim.rlim_max; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci printf("page_size: %ld, mlock.soft: %ld, mlock.hard: %ld\n", 24962306a36Sopenharmony_ci page_size, mlock_limit_cur, mlock_limit_max); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (page_size > mlock_limit_cur) 25262306a36Sopenharmony_ci mlock_limit_cur = page_size; 25362306a36Sopenharmony_ci if (page_size > mlock_limit_max) 25462306a36Sopenharmony_ci mlock_limit_max = page_size; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (set_cap_limits(mlock_limit_max)) 25762306a36Sopenharmony_ci ksft_exit_fail_msg("Unable to set mlock limit: %s\n", 25862306a36Sopenharmony_ci strerror(errno)); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci#define NUM_TESTS 4 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ciint main(int argc, char *argv[]) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci int fd; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci prepare(); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci ksft_print_header(); 27062306a36Sopenharmony_ci ksft_set_plan(NUM_TESTS); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci fd = memfd_secret(0); 27362306a36Sopenharmony_ci if (fd < 0) { 27462306a36Sopenharmony_ci if (errno == ENOSYS) 27562306a36Sopenharmony_ci ksft_exit_skip("memfd_secret is not supported\n"); 27662306a36Sopenharmony_ci else 27762306a36Sopenharmony_ci ksft_exit_fail_msg("memfd_secret failed: %s\n", 27862306a36Sopenharmony_ci strerror(errno)); 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci test_mlock_limit(fd); 28262306a36Sopenharmony_ci test_file_apis(fd); 28362306a36Sopenharmony_ci test_process_vm_read(fd); 28462306a36Sopenharmony_ci test_ptrace(fd); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci close(fd); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci ksft_finished(); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci#else /* __NR_memfd_secret */ 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ciint main(int argc, char *argv[]) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci printf("skip: skipping memfd_secret test (missing __NR_memfd_secret)\n"); 29662306a36Sopenharmony_ci return KSFT_SKIP; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci#endif /* __NR_memfd_secret */ 300