123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright IBM Corporation, 2021
- *
- * Author: Mike Rapoport <[email protected]>
- */
- #define _GNU_SOURCE
- #include <sys/uio.h>
- #include <sys/mman.h>
- #include <sys/wait.h>
- #include <sys/types.h>
- #include <sys/ptrace.h>
- #include <sys/syscall.h>
- #include <sys/resource.h>
- #include <sys/capability.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <errno.h>
- #include <stdio.h>
- #include "../kselftest.h"
- #define fail(fmt, ...) ksft_test_result_fail(fmt, ##__VA_ARGS__)
- #define pass(fmt, ...) ksft_test_result_pass(fmt, ##__VA_ARGS__)
- #define skip(fmt, ...) ksft_test_result_skip(fmt, ##__VA_ARGS__)
- #ifdef __NR_memfd_secret
- #define PATTERN 0x55
- static const int prot = PROT_READ | PROT_WRITE;
- static const int mode = MAP_SHARED;
- static unsigned long page_size;
- static unsigned long mlock_limit_cur;
- static unsigned long mlock_limit_max;
- static int memfd_secret(unsigned int flags)
- {
- return syscall(__NR_memfd_secret, flags);
- }
- static void test_file_apis(int fd)
- {
- char buf[64];
- if ((read(fd, buf, sizeof(buf)) >= 0) ||
- (write(fd, buf, sizeof(buf)) >= 0) ||
- (pread(fd, buf, sizeof(buf), 0) >= 0) ||
- (pwrite(fd, buf, sizeof(buf), 0) >= 0))
- fail("unexpected file IO\n");
- else
- pass("file IO is blocked as expected\n");
- }
- static void test_mlock_limit(int fd)
- {
- size_t len;
- char *mem;
- len = mlock_limit_cur;
- mem = mmap(NULL, len, prot, mode, fd, 0);
- if (mem == MAP_FAILED) {
- fail("unable to mmap secret memory\n");
- return;
- }
- munmap(mem, len);
- len = mlock_limit_max * 2;
- mem = mmap(NULL, len, prot, mode, fd, 0);
- if (mem != MAP_FAILED) {
- fail("unexpected mlock limit violation\n");
- munmap(mem, len);
- return;
- }
- pass("mlock limit is respected\n");
- }
- static void try_process_vm_read(int fd, int pipefd[2])
- {
- struct iovec liov, riov;
- char buf[64];
- char *mem;
- if (read(pipefd[0], &mem, sizeof(mem)) < 0) {
- fail("pipe write: %s\n", strerror(errno));
- exit(KSFT_FAIL);
- }
- liov.iov_len = riov.iov_len = sizeof(buf);
- liov.iov_base = buf;
- riov.iov_base = mem;
- if (process_vm_readv(getppid(), &liov, 1, &riov, 1, 0) < 0) {
- if (errno == ENOSYS)
- exit(KSFT_SKIP);
- exit(KSFT_PASS);
- }
- exit(KSFT_FAIL);
- }
- static void try_ptrace(int fd, int pipefd[2])
- {
- pid_t ppid = getppid();
- int status;
- char *mem;
- long ret;
- if (read(pipefd[0], &mem, sizeof(mem)) < 0) {
- perror("pipe write");
- exit(KSFT_FAIL);
- }
- ret = ptrace(PTRACE_ATTACH, ppid, 0, 0);
- if (ret) {
- perror("ptrace_attach");
- exit(KSFT_FAIL);
- }
- ret = waitpid(ppid, &status, WUNTRACED);
- if ((ret != ppid) || !(WIFSTOPPED(status))) {
- fprintf(stderr, "weird waitppid result %ld stat %x\n",
- ret, status);
- exit(KSFT_FAIL);
- }
- if (ptrace(PTRACE_PEEKDATA, ppid, mem, 0))
- exit(KSFT_PASS);
- exit(KSFT_FAIL);
- }
- static void check_child_status(pid_t pid, const char *name)
- {
- int status;
- waitpid(pid, &status, 0);
- if (WIFEXITED(status) && WEXITSTATUS(status) == KSFT_SKIP) {
- skip("%s is not supported\n", name);
- return;
- }
- if ((WIFEXITED(status) && WEXITSTATUS(status) == KSFT_PASS) ||
- WIFSIGNALED(status)) {
- pass("%s is blocked as expected\n", name);
- return;
- }
- fail("%s: unexpected memory access\n", name);
- }
- static void test_remote_access(int fd, const char *name,
- void (*func)(int fd, int pipefd[2]))
- {
- int pipefd[2];
- pid_t pid;
- char *mem;
- if (pipe(pipefd)) {
- fail("pipe failed: %s\n", strerror(errno));
- return;
- }
- pid = fork();
- if (pid < 0) {
- fail("fork failed: %s\n", strerror(errno));
- return;
- }
- if (pid == 0) {
- func(fd, pipefd);
- return;
- }
- mem = mmap(NULL, page_size, prot, mode, fd, 0);
- if (mem == MAP_FAILED) {
- fail("Unable to mmap secret memory\n");
- return;
- }
- ftruncate(fd, page_size);
- memset(mem, PATTERN, page_size);
- if (write(pipefd[1], &mem, sizeof(mem)) < 0) {
- fail("pipe write: %s\n", strerror(errno));
- return;
- }
- check_child_status(pid, name);
- }
- static void test_process_vm_read(int fd)
- {
- test_remote_access(fd, "process_vm_read", try_process_vm_read);
- }
- static void test_ptrace(int fd)
- {
- test_remote_access(fd, "ptrace", try_ptrace);
- }
- static int set_cap_limits(rlim_t max)
- {
- struct rlimit new;
- cap_t cap = cap_init();
- new.rlim_cur = max;
- new.rlim_max = max;
- if (setrlimit(RLIMIT_MEMLOCK, &new)) {
- perror("setrlimit() returns error");
- return -1;
- }
- /* drop capabilities including CAP_IPC_LOCK */
- if (cap_set_proc(cap)) {
- perror("cap_set_proc() returns error");
- return -2;
- }
- return 0;
- }
- static void prepare(void)
- {
- struct rlimit rlim;
- page_size = sysconf(_SC_PAGE_SIZE);
- if (!page_size)
- ksft_exit_fail_msg("Failed to get page size %s\n",
- strerror(errno));
- if (getrlimit(RLIMIT_MEMLOCK, &rlim))
- ksft_exit_fail_msg("Unable to detect mlock limit: %s\n",
- strerror(errno));
- mlock_limit_cur = rlim.rlim_cur;
- mlock_limit_max = rlim.rlim_max;
- printf("page_size: %ld, mlock.soft: %ld, mlock.hard: %ld\n",
- page_size, mlock_limit_cur, mlock_limit_max);
- if (page_size > mlock_limit_cur)
- mlock_limit_cur = page_size;
- if (page_size > mlock_limit_max)
- mlock_limit_max = page_size;
- if (set_cap_limits(mlock_limit_max))
- ksft_exit_fail_msg("Unable to set mlock limit: %s\n",
- strerror(errno));
- }
- #define NUM_TESTS 4
- int main(int argc, char *argv[])
- {
- int fd;
- prepare();
- ksft_print_header();
- ksft_set_plan(NUM_TESTS);
- fd = memfd_secret(0);
- if (fd < 0) {
- if (errno == ENOSYS)
- ksft_exit_skip("memfd_secret is not supported\n");
- else
- ksft_exit_fail_msg("memfd_secret failed: %s\n",
- strerror(errno));
- }
- test_mlock_limit(fd);
- test_file_apis(fd);
- test_process_vm_read(fd);
- test_ptrace(fd);
- close(fd);
- ksft_finished();
- }
- #else /* __NR_memfd_secret */
- int main(int argc, char *argv[])
- {
- printf("skip: skipping memfd_secret test (missing __NR_memfd_secret)\n");
- return KSFT_SKIP;
- }
- #endif /* __NR_memfd_secret */
|