cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

memfd_secret.c (5891B)


      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 <unistd.h>
     21#include <errno.h>
     22#include <stdio.h>
     23
     24#include "../kselftest.h"
     25
     26#define fail(fmt, ...) ksft_test_result_fail(fmt, ##__VA_ARGS__)
     27#define pass(fmt, ...) ksft_test_result_pass(fmt, ##__VA_ARGS__)
     28#define skip(fmt, ...) ksft_test_result_skip(fmt, ##__VA_ARGS__)
     29
     30#ifdef __NR_memfd_secret
     31
     32#define PATTERN	0x55
     33
     34static const int prot = PROT_READ | PROT_WRITE;
     35static const int mode = MAP_SHARED;
     36
     37static unsigned long page_size;
     38static unsigned long mlock_limit_cur;
     39static unsigned long mlock_limit_max;
     40
     41static int memfd_secret(unsigned int flags)
     42{
     43	return syscall(__NR_memfd_secret, flags);
     44}
     45
     46static void test_file_apis(int fd)
     47{
     48	char buf[64];
     49
     50	if ((read(fd, buf, sizeof(buf)) >= 0) ||
     51	    (write(fd, buf, sizeof(buf)) >= 0) ||
     52	    (pread(fd, buf, sizeof(buf), 0) >= 0) ||
     53	    (pwrite(fd, buf, sizeof(buf), 0) >= 0))
     54		fail("unexpected file IO\n");
     55	else
     56		pass("file IO is blocked as expected\n");
     57}
     58
     59static void test_mlock_limit(int fd)
     60{
     61	size_t len;
     62	char *mem;
     63
     64	len = mlock_limit_cur;
     65	mem = mmap(NULL, len, prot, mode, fd, 0);
     66	if (mem == MAP_FAILED) {
     67		fail("unable to mmap secret memory\n");
     68		return;
     69	}
     70	munmap(mem, len);
     71
     72	len = mlock_limit_max * 2;
     73	mem = mmap(NULL, len, prot, mode, fd, 0);
     74	if (mem != MAP_FAILED) {
     75		fail("unexpected mlock limit violation\n");
     76		munmap(mem, len);
     77		return;
     78	}
     79
     80	pass("mlock limit is respected\n");
     81}
     82
     83static void try_process_vm_read(int fd, int pipefd[2])
     84{
     85	struct iovec liov, riov;
     86	char buf[64];
     87	char *mem;
     88
     89	if (read(pipefd[0], &mem, sizeof(mem)) < 0) {
     90		fail("pipe write: %s\n", strerror(errno));
     91		exit(KSFT_FAIL);
     92	}
     93
     94	liov.iov_len = riov.iov_len = sizeof(buf);
     95	liov.iov_base = buf;
     96	riov.iov_base = mem;
     97
     98	if (process_vm_readv(getppid(), &liov, 1, &riov, 1, 0) < 0) {
     99		if (errno == ENOSYS)
    100			exit(KSFT_SKIP);
    101		exit(KSFT_PASS);
    102	}
    103
    104	exit(KSFT_FAIL);
    105}
    106
    107static void try_ptrace(int fd, int pipefd[2])
    108{
    109	pid_t ppid = getppid();
    110	int status;
    111	char *mem;
    112	long ret;
    113
    114	if (read(pipefd[0], &mem, sizeof(mem)) < 0) {
    115		perror("pipe write");
    116		exit(KSFT_FAIL);
    117	}
    118
    119	ret = ptrace(PTRACE_ATTACH, ppid, 0, 0);
    120	if (ret) {
    121		perror("ptrace_attach");
    122		exit(KSFT_FAIL);
    123	}
    124
    125	ret = waitpid(ppid, &status, WUNTRACED);
    126	if ((ret != ppid) || !(WIFSTOPPED(status))) {
    127		fprintf(stderr, "weird waitppid result %ld stat %x\n",
    128			ret, status);
    129		exit(KSFT_FAIL);
    130	}
    131
    132	if (ptrace(PTRACE_PEEKDATA, ppid, mem, 0))
    133		exit(KSFT_PASS);
    134
    135	exit(KSFT_FAIL);
    136}
    137
    138static void check_child_status(pid_t pid, const char *name)
    139{
    140	int status;
    141
    142	waitpid(pid, &status, 0);
    143
    144	if (WIFEXITED(status) && WEXITSTATUS(status) == KSFT_SKIP) {
    145		skip("%s is not supported\n", name);
    146		return;
    147	}
    148
    149	if ((WIFEXITED(status) && WEXITSTATUS(status) == KSFT_PASS) ||
    150	    WIFSIGNALED(status)) {
    151		pass("%s is blocked as expected\n", name);
    152		return;
    153	}
    154
    155	fail("%s: unexpected memory access\n", name);
    156}
    157
    158static void test_remote_access(int fd, const char *name,
    159			       void (*func)(int fd, int pipefd[2]))
    160{
    161	int pipefd[2];
    162	pid_t pid;
    163	char *mem;
    164
    165	if (pipe(pipefd)) {
    166		fail("pipe failed: %s\n", strerror(errno));
    167		return;
    168	}
    169
    170	pid = fork();
    171	if (pid < 0) {
    172		fail("fork failed: %s\n", strerror(errno));
    173		return;
    174	}
    175
    176	if (pid == 0) {
    177		func(fd, pipefd);
    178		return;
    179	}
    180
    181	mem = mmap(NULL, page_size, prot, mode, fd, 0);
    182	if (mem == MAP_FAILED) {
    183		fail("Unable to mmap secret memory\n");
    184		return;
    185	}
    186
    187	ftruncate(fd, page_size);
    188	memset(mem, PATTERN, page_size);
    189
    190	if (write(pipefd[1], &mem, sizeof(mem)) < 0) {
    191		fail("pipe write: %s\n", strerror(errno));
    192		return;
    193	}
    194
    195	check_child_status(pid, name);
    196}
    197
    198static void test_process_vm_read(int fd)
    199{
    200	test_remote_access(fd, "process_vm_read", try_process_vm_read);
    201}
    202
    203static void test_ptrace(int fd)
    204{
    205	test_remote_access(fd, "ptrace", try_ptrace);
    206}
    207
    208static int set_cap_limits(rlim_t max)
    209{
    210	struct rlimit new;
    211	cap_t cap = cap_init();
    212
    213	new.rlim_cur = max;
    214	new.rlim_max = max;
    215	if (setrlimit(RLIMIT_MEMLOCK, &new)) {
    216		perror("setrlimit() returns error");
    217		return -1;
    218	}
    219
    220	/* drop capabilities including CAP_IPC_LOCK */
    221	if (cap_set_proc(cap)) {
    222		perror("cap_set_proc() returns error");
    223		return -2;
    224	}
    225
    226	return 0;
    227}
    228
    229static void prepare(void)
    230{
    231	struct rlimit rlim;
    232
    233	page_size = sysconf(_SC_PAGE_SIZE);
    234	if (!page_size)
    235		ksft_exit_fail_msg("Failed to get page size %s\n",
    236				   strerror(errno));
    237
    238	if (getrlimit(RLIMIT_MEMLOCK, &rlim))
    239		ksft_exit_fail_msg("Unable to detect mlock limit: %s\n",
    240				   strerror(errno));
    241
    242	mlock_limit_cur = rlim.rlim_cur;
    243	mlock_limit_max = rlim.rlim_max;
    244
    245	printf("page_size: %ld, mlock.soft: %ld, mlock.hard: %ld\n",
    246	       page_size, mlock_limit_cur, mlock_limit_max);
    247
    248	if (page_size > mlock_limit_cur)
    249		mlock_limit_cur = page_size;
    250	if (page_size > mlock_limit_max)
    251		mlock_limit_max = page_size;
    252
    253	if (set_cap_limits(mlock_limit_max))
    254		ksft_exit_fail_msg("Unable to set mlock limit: %s\n",
    255				   strerror(errno));
    256}
    257
    258#define NUM_TESTS 4
    259
    260int main(int argc, char *argv[])
    261{
    262	int fd;
    263
    264	prepare();
    265
    266	ksft_print_header();
    267	ksft_set_plan(NUM_TESTS);
    268
    269	fd = memfd_secret(0);
    270	if (fd < 0) {
    271		if (errno == ENOSYS)
    272			ksft_exit_skip("memfd_secret is not supported\n");
    273		else
    274			ksft_exit_fail_msg("memfd_secret failed: %s\n",
    275					   strerror(errno));
    276	}
    277
    278	test_mlock_limit(fd);
    279	test_file_apis(fd);
    280	test_process_vm_read(fd);
    281	test_ptrace(fd);
    282
    283	close(fd);
    284
    285	ksft_finished();
    286}
    287
    288#else /* __NR_memfd_secret */
    289
    290int main(int argc, char *argv[])
    291{
    292	printf("skip: skipping memfd_secret test (missing __NR_memfd_secret)\n");
    293	return KSFT_SKIP;
    294}
    295
    296#endif /* __NR_memfd_secret */