brintos

brintos / linux-shallow public Read only

0
0
Text · 6.7 KiB · 74c911a Raw
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