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

proc-pid-vm.c (10742B)


      1/*
      2 * Copyright (c) 2019 Alexey Dobriyan <adobriyan@gmail.com>
      3 *
      4 * Permission to use, copy, modify, and distribute this software for any
      5 * purpose with or without fee is hereby granted, provided that the above
      6 * copyright notice and this permission notice appear in all copies.
      7 *
      8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15 */
     16/*
     17 * Fork and exec tiny 1 page executable which precisely controls its VM.
     18 * Test /proc/$PID/maps
     19 * Test /proc/$PID/smaps
     20 * Test /proc/$PID/smaps_rollup
     21 * Test /proc/$PID/statm
     22 *
     23 * FIXME require CONFIG_TMPFS which can be disabled
     24 * FIXME test other values from "smaps"
     25 * FIXME support other archs
     26 */
     27#undef NDEBUG
     28#include <assert.h>
     29#include <errno.h>
     30#include <sched.h>
     31#include <signal.h>
     32#include <stdbool.h>
     33#include <stdint.h>
     34#include <stdio.h>
     35#include <string.h>
     36#include <stdlib.h>
     37#include <sys/mount.h>
     38#include <sys/types.h>
     39#include <sys/stat.h>
     40#include <sys/wait.h>
     41#include <fcntl.h>
     42#include <unistd.h>
     43#include <sys/syscall.h>
     44#include <sys/uio.h>
     45#include <linux/kdev_t.h>
     46#include <sys/time.h>
     47#include <sys/resource.h>
     48
     49#include "../kselftest.h"
     50
     51static inline long sys_execveat(int dirfd, const char *pathname, char **argv, char **envp, int flags)
     52{
     53	return syscall(SYS_execveat, dirfd, pathname, argv, envp, flags);
     54}
     55
     56static void make_private_tmp(void)
     57{
     58	if (unshare(CLONE_NEWNS) == -1) {
     59		if (errno == ENOSYS || errno == EPERM) {
     60			exit(4);
     61		}
     62		exit(1);
     63	}
     64	if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) == -1) {
     65		exit(1);
     66	}
     67	if (mount(NULL, "/tmp", "tmpfs", 0, NULL) == -1) {
     68		exit(1);
     69	}
     70}
     71
     72static pid_t pid = -1;
     73static void ate(void)
     74{
     75	if (pid > 0) {
     76		kill(pid, SIGTERM);
     77	}
     78}
     79
     80struct elf64_hdr {
     81	uint8_t e_ident[16];
     82	uint16_t e_type;
     83	uint16_t e_machine;
     84	uint32_t e_version;
     85	uint64_t e_entry;
     86	uint64_t e_phoff;
     87	uint64_t e_shoff;
     88	uint32_t e_flags;
     89	uint16_t e_ehsize;
     90	uint16_t e_phentsize;
     91	uint16_t e_phnum;
     92	uint16_t e_shentsize;
     93	uint16_t e_shnum;
     94	uint16_t e_shstrndx;
     95};
     96
     97struct elf64_phdr {
     98	uint32_t p_type;
     99	uint32_t p_flags;
    100	uint64_t p_offset;
    101	uint64_t p_vaddr;
    102	uint64_t p_paddr;
    103	uint64_t p_filesz;
    104	uint64_t p_memsz;
    105	uint64_t p_align;
    106};
    107
    108#ifdef __x86_64__
    109#define PAGE_SIZE 4096
    110#define VADDR (1UL << 32)
    111#define MAPS_OFFSET 73
    112
    113#define syscall	0x0f, 0x05
    114#define mov_rdi(x)	\
    115	0x48, 0xbf,	\
    116	(x)&0xff, ((x)>>8)&0xff, ((x)>>16)&0xff, ((x)>>24)&0xff,	\
    117	((x)>>32)&0xff, ((x)>>40)&0xff, ((x)>>48)&0xff, ((x)>>56)&0xff
    118
    119#define mov_rsi(x)	\
    120	0x48, 0xbe,	\
    121	(x)&0xff, ((x)>>8)&0xff, ((x)>>16)&0xff, ((x)>>24)&0xff,	\
    122	((x)>>32)&0xff, ((x)>>40)&0xff, ((x)>>48)&0xff, ((x)>>56)&0xff
    123
    124#define mov_eax(x)	\
    125	0xb8, (x)&0xff, ((x)>>8)&0xff, ((x)>>16)&0xff, ((x)>>24)&0xff
    126
    127static const uint8_t payload[] = {
    128	/* Casually unmap stack, vDSO and everything else. */
    129	/* munmap */
    130	mov_rdi(VADDR + 4096),
    131	mov_rsi((1ULL << 47) - 4096 - VADDR - 4096),
    132	mov_eax(11),
    133	syscall,
    134
    135	/* Ping parent. */
    136	/* write(0, &c, 1); */
    137	0x31, 0xff,					/* xor edi, edi */
    138	0x48, 0x8d, 0x35, 0x00, 0x00, 0x00, 0x00,	/* lea rsi, [rip] */
    139	0xba, 0x01, 0x00, 0x00, 0x00,			/* mov edx, 1 */
    140	mov_eax(1),
    141	syscall,
    142
    143	/* 1: pause(); */
    144	mov_eax(34),
    145	syscall,
    146
    147	0xeb, 0xf7,	/* jmp 1b */
    148};
    149
    150static int make_exe(const uint8_t *payload, size_t len)
    151{
    152	struct elf64_hdr h;
    153	struct elf64_phdr ph;
    154
    155	struct iovec iov[3] = {
    156		{&h, sizeof(struct elf64_hdr)},
    157		{&ph, sizeof(struct elf64_phdr)},
    158		{(void *)payload, len},
    159	};
    160	int fd, fd1;
    161	char buf[64];
    162
    163	memset(&h, 0, sizeof(h));
    164	h.e_ident[0] = 0x7f;
    165	h.e_ident[1] = 'E';
    166	h.e_ident[2] = 'L';
    167	h.e_ident[3] = 'F';
    168	h.e_ident[4] = 2;
    169	h.e_ident[5] = 1;
    170	h.e_ident[6] = 1;
    171	h.e_ident[7] = 0;
    172	h.e_type = 2;
    173	h.e_machine = 0x3e;
    174	h.e_version = 1;
    175	h.e_entry = VADDR + sizeof(struct elf64_hdr) + sizeof(struct elf64_phdr);
    176	h.e_phoff = sizeof(struct elf64_hdr);
    177	h.e_shoff = 0;
    178	h.e_flags = 0;
    179	h.e_ehsize = sizeof(struct elf64_hdr);
    180	h.e_phentsize = sizeof(struct elf64_phdr);
    181	h.e_phnum = 1;
    182	h.e_shentsize = 0;
    183	h.e_shnum = 0;
    184	h.e_shstrndx = 0;
    185
    186	memset(&ph, 0, sizeof(ph));
    187	ph.p_type = 1;
    188	ph.p_flags = (1<<2)|1;
    189	ph.p_offset = 0;
    190	ph.p_vaddr = VADDR;
    191	ph.p_paddr = 0;
    192	ph.p_filesz = sizeof(struct elf64_hdr) + sizeof(struct elf64_phdr) + len;
    193	ph.p_memsz = sizeof(struct elf64_hdr) + sizeof(struct elf64_phdr) + len;
    194	ph.p_align = 4096;
    195
    196	fd = openat(AT_FDCWD, "/tmp", O_WRONLY|O_EXCL|O_TMPFILE, 0700);
    197	if (fd == -1) {
    198		exit(1);
    199	}
    200
    201	if (writev(fd, iov, 3) != sizeof(struct elf64_hdr) + sizeof(struct elf64_phdr) + len) {
    202		exit(1);
    203	}
    204
    205	/* Avoid ETXTBSY on exec. */
    206	snprintf(buf, sizeof(buf), "/proc/self/fd/%u", fd);
    207	fd1 = open(buf, O_RDONLY|O_CLOEXEC);
    208	close(fd);
    209
    210	return fd1;
    211}
    212#endif
    213
    214static bool g_vsyscall = false;
    215
    216static const char str_vsyscall[] =
    217"ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]\n";
    218
    219#ifdef __x86_64__
    220static void sigaction_SIGSEGV(int _, siginfo_t *__, void *___)
    221{
    222	_exit(1);
    223}
    224
    225/*
    226 * vsyscall page can't be unmapped, probe it with memory load.
    227 */
    228static void vsyscall(void)
    229{
    230	pid_t pid;
    231	int wstatus;
    232
    233	pid = fork();
    234	if (pid < 0) {
    235		fprintf(stderr, "fork, errno %d\n", errno);
    236		exit(1);
    237	}
    238	if (pid == 0) {
    239		struct rlimit rlim = {0, 0};
    240		(void)setrlimit(RLIMIT_CORE, &rlim);
    241
    242		/* Hide "segfault at ffffffffff600000" messages. */
    243		struct sigaction act;
    244		memset(&act, 0, sizeof(struct sigaction));
    245		act.sa_flags = SA_SIGINFO;
    246		act.sa_sigaction = sigaction_SIGSEGV;
    247		(void)sigaction(SIGSEGV, &act, NULL);
    248
    249		*(volatile int *)0xffffffffff600000UL;
    250		exit(0);
    251	}
    252	waitpid(pid, &wstatus, 0);
    253	if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) {
    254		g_vsyscall = true;
    255	}
    256}
    257
    258int main(void)
    259{
    260	int pipefd[2];
    261	int exec_fd;
    262
    263	vsyscall();
    264
    265	atexit(ate);
    266
    267	make_private_tmp();
    268
    269	/* Reserve fd 0 for 1-byte pipe ping from child. */
    270	close(0);
    271	if (open("/", O_RDONLY|O_DIRECTORY|O_PATH) != 0) {
    272		return 1;
    273	}
    274
    275	exec_fd = make_exe(payload, sizeof(payload));
    276
    277	if (pipe(pipefd) == -1) {
    278		return 1;
    279	}
    280	if (dup2(pipefd[1], 0) != 0) {
    281		return 1;
    282	}
    283
    284	pid = fork();
    285	if (pid == -1) {
    286		return 1;
    287	}
    288	if (pid == 0) {
    289		sys_execveat(exec_fd, "", NULL, NULL, AT_EMPTY_PATH);
    290		return 1;
    291	}
    292
    293	char _;
    294	if (read(pipefd[0], &_, 1) != 1) {
    295		return 1;
    296	}
    297
    298	struct stat st;
    299	if (fstat(exec_fd, &st) == -1) {
    300		return 1;
    301	}
    302
    303	/* Generate "head -n1 /proc/$PID/maps" */
    304	char buf0[256];
    305	memset(buf0, ' ', sizeof(buf0));
    306	int len = snprintf(buf0, sizeof(buf0),
    307			"%08lx-%08lx r-xp 00000000 %02lx:%02lx %llu",
    308			VADDR, VADDR + PAGE_SIZE,
    309			MAJOR(st.st_dev), MINOR(st.st_dev),
    310			(unsigned long long)st.st_ino);
    311	buf0[len] = ' ';
    312	snprintf(buf0 + MAPS_OFFSET, sizeof(buf0) - MAPS_OFFSET,
    313		 "/tmp/#%llu (deleted)\n", (unsigned long long)st.st_ino);
    314
    315	/* Test /proc/$PID/maps */
    316	{
    317		const size_t len = strlen(buf0) + (g_vsyscall ? strlen(str_vsyscall) : 0);
    318		char buf[256];
    319		ssize_t rv;
    320		int fd;
    321
    322		snprintf(buf, sizeof(buf), "/proc/%u/maps", pid);
    323		fd = open(buf, O_RDONLY);
    324		if (fd == -1) {
    325			return 1;
    326		}
    327		rv = read(fd, buf, sizeof(buf));
    328		assert(rv == len);
    329		assert(memcmp(buf, buf0, strlen(buf0)) == 0);
    330		if (g_vsyscall) {
    331			assert(memcmp(buf + strlen(buf0), str_vsyscall, strlen(str_vsyscall)) == 0);
    332		}
    333	}
    334
    335	/* Test /proc/$PID/smaps */
    336	{
    337		char buf[4096];
    338		ssize_t rv;
    339		int fd;
    340
    341		snprintf(buf, sizeof(buf), "/proc/%u/smaps", pid);
    342		fd = open(buf, O_RDONLY);
    343		if (fd == -1) {
    344			return 1;
    345		}
    346		rv = read(fd, buf, sizeof(buf));
    347		assert(0 <= rv && rv <= sizeof(buf));
    348
    349		assert(rv >= strlen(buf0));
    350		assert(memcmp(buf, buf0, strlen(buf0)) == 0);
    351
    352#define RSS1 "Rss:                   4 kB\n"
    353#define RSS2 "Rss:                   0 kB\n"
    354#define PSS1 "Pss:                   4 kB\n"
    355#define PSS2 "Pss:                   0 kB\n"
    356		assert(memmem(buf, rv, RSS1, strlen(RSS1)) ||
    357		       memmem(buf, rv, RSS2, strlen(RSS2)));
    358		assert(memmem(buf, rv, PSS1, strlen(PSS1)) ||
    359		       memmem(buf, rv, PSS2, strlen(PSS2)));
    360
    361		static const char *S[] = {
    362			"Size:                  4 kB\n",
    363			"KernelPageSize:        4 kB\n",
    364			"MMUPageSize:           4 kB\n",
    365			"Anonymous:             0 kB\n",
    366			"AnonHugePages:         0 kB\n",
    367			"Shared_Hugetlb:        0 kB\n",
    368			"Private_Hugetlb:       0 kB\n",
    369			"Locked:                0 kB\n",
    370		};
    371		int i;
    372
    373		for (i = 0; i < ARRAY_SIZE(S); i++) {
    374			assert(memmem(buf, rv, S[i], strlen(S[i])));
    375		}
    376
    377		if (g_vsyscall) {
    378			assert(memmem(buf, rv, str_vsyscall, strlen(str_vsyscall)));
    379		}
    380	}
    381
    382	/* Test /proc/$PID/smaps_rollup */
    383	{
    384		char bufr[256];
    385		memset(bufr, ' ', sizeof(bufr));
    386		len = snprintf(bufr, sizeof(bufr),
    387				"%08lx-%08lx ---p 00000000 00:00 0",
    388				VADDR, VADDR + PAGE_SIZE);
    389		bufr[len] = ' ';
    390		snprintf(bufr + MAPS_OFFSET, sizeof(bufr) - MAPS_OFFSET,
    391			 "[rollup]\n");
    392
    393		char buf[1024];
    394		ssize_t rv;
    395		int fd;
    396
    397		snprintf(buf, sizeof(buf), "/proc/%u/smaps_rollup", pid);
    398		fd = open(buf, O_RDONLY);
    399		if (fd == -1) {
    400			return 1;
    401		}
    402		rv = read(fd, buf, sizeof(buf));
    403		assert(0 <= rv && rv <= sizeof(buf));
    404
    405		assert(rv >= strlen(bufr));
    406		assert(memcmp(buf, bufr, strlen(bufr)) == 0);
    407
    408		assert(memmem(buf, rv, RSS1, strlen(RSS1)) ||
    409		       memmem(buf, rv, RSS2, strlen(RSS2)));
    410		assert(memmem(buf, rv, PSS1, strlen(PSS1)) ||
    411		       memmem(buf, rv, PSS2, strlen(PSS2)));
    412
    413		static const char *S[] = {
    414			"Anonymous:             0 kB\n",
    415			"AnonHugePages:         0 kB\n",
    416			"Shared_Hugetlb:        0 kB\n",
    417			"Private_Hugetlb:       0 kB\n",
    418			"Locked:                0 kB\n",
    419		};
    420		int i;
    421
    422		for (i = 0; i < ARRAY_SIZE(S); i++) {
    423			assert(memmem(buf, rv, S[i], strlen(S[i])));
    424		}
    425	}
    426
    427	/* Test /proc/$PID/statm */
    428	{
    429		char buf[64];
    430		ssize_t rv;
    431		int fd;
    432
    433		snprintf(buf, sizeof(buf), "/proc/%u/statm", pid);
    434		fd = open(buf, O_RDONLY);
    435		if (fd == -1) {
    436			return 1;
    437		}
    438		rv = read(fd, buf, sizeof(buf));
    439		assert(rv == 7 * 2);
    440
    441		assert(buf[0] == '1');	/* ->total_vm */
    442		assert(buf[1] == ' ');
    443		assert(buf[2] == '0' || buf[2] == '1');	/* rss */
    444		assert(buf[3] == ' ');
    445		assert(buf[4] == '0' || buf[2] == '1');	/* file rss */
    446		assert(buf[5] == ' ');
    447		assert(buf[6] == '1');	/* ELF executable segments */
    448		assert(buf[7] == ' ');
    449		assert(buf[8] == '0');
    450		assert(buf[9] == ' ');
    451		assert(buf[10] == '0');	/* ->data_vm + ->stack_vm */
    452		assert(buf[11] == ' ');
    453		assert(buf[12] == '0');
    454		assert(buf[13] == '\n');
    455	}
    456
    457	return 0;
    458}
    459#else
    460int main(void)
    461{
    462	return 4;
    463}
    464#endif