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

fd-003-kthread.c (3993B)


      1/*
      2 * Copyright © 2018 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// Test that /proc/$KERNEL_THREAD/fd/ is empty.
     17
     18#undef NDEBUG
     19#include <sys/syscall.h>
     20#include <assert.h>
     21#include <dirent.h>
     22#include <limits.h>
     23#include <stdio.h>
     24#include <string.h>
     25#include <sys/types.h>
     26#include <sys/stat.h>
     27#include <fcntl.h>
     28#include <unistd.h>
     29
     30#include "proc.h"
     31
     32#define PF_KHTREAD 0x00200000
     33
     34/*
     35 * Test for kernel threadness atomically with openat().
     36 *
     37 * Return /proc/$PID/fd descriptor if process is kernel thread.
     38 * Return -1 if a process is userspace process.
     39 */
     40static int kernel_thread_fd(unsigned int pid)
     41{
     42	unsigned int flags = 0;
     43	char buf[4096];
     44	int dir_fd, fd;
     45	ssize_t rv;
     46
     47	snprintf(buf, sizeof(buf), "/proc/%u", pid);
     48	dir_fd = open(buf, O_RDONLY|O_DIRECTORY);
     49	if (dir_fd == -1)
     50		return -1;
     51
     52	/*
     53	 * Believe it or not, struct task_struct::flags is directly exposed
     54	 * to userspace!
     55	 */
     56	fd = openat(dir_fd, "stat", O_RDONLY);
     57	if (fd == -1) {
     58		close(dir_fd);
     59		return -1;
     60	}
     61	rv = read(fd, buf, sizeof(buf));
     62	close(fd);
     63	if (0 < rv && rv <= sizeof(buf)) {
     64		unsigned long long flags_ull;
     65		char *p, *end;
     66		int i;
     67
     68		assert(buf[rv - 1] == '\n');
     69		buf[rv - 1] = '\0';
     70
     71		/* Search backwards: ->comm can contain whitespace and ')'. */
     72		for (i = 0; i < 43; i++) {
     73			p = strrchr(buf, ' ');
     74			assert(p);
     75			*p = '\0';
     76		}
     77
     78		p = strrchr(buf, ' ');
     79		assert(p);
     80
     81		flags_ull = xstrtoull(p + 1, &end);
     82		assert(*end == '\0');
     83		assert(flags_ull == (unsigned int)flags_ull);
     84
     85		flags = flags_ull;
     86	}
     87
     88	fd = -1;
     89	if (flags & PF_KHTREAD) {
     90		fd = openat(dir_fd, "fd", O_RDONLY|O_DIRECTORY);
     91	}
     92	close(dir_fd);
     93	return fd;
     94}
     95
     96static void test_readdir(int fd)
     97{
     98	DIR *d;
     99	struct dirent *de;
    100
    101	d = fdopendir(fd);
    102	assert(d);
    103
    104	de = xreaddir(d);
    105	assert(streq(de->d_name, "."));
    106	assert(de->d_type == DT_DIR);
    107
    108	de = xreaddir(d);
    109	assert(streq(de->d_name, ".."));
    110	assert(de->d_type == DT_DIR);
    111
    112	de = xreaddir(d);
    113	assert(!de);
    114}
    115
    116static inline int sys_statx(int dirfd, const char *pathname, int flags,
    117			    unsigned int mask, void *stx)
    118{
    119	return syscall(SYS_statx, dirfd, pathname, flags, mask, stx);
    120}
    121
    122static void test_lookup_fail(int fd, const char *pathname)
    123{
    124	char stx[256] __attribute__((aligned(8)));
    125	int rv;
    126
    127	rv = sys_statx(fd, pathname, AT_SYMLINK_NOFOLLOW, 0, (void *)stx);
    128	assert(rv == -1 && errno == ENOENT);
    129}
    130
    131static void test_lookup(int fd)
    132{
    133	char buf[64];
    134	unsigned int u;
    135	int i;
    136
    137	for (i = INT_MIN; i < INT_MIN + 1024; i++) {
    138		snprintf(buf, sizeof(buf), "%d", i);
    139		test_lookup_fail(fd, buf);
    140	}
    141	for (i = -1024; i < 1024; i++) {
    142		snprintf(buf, sizeof(buf), "%d", i);
    143		test_lookup_fail(fd, buf);
    144	}
    145	for (u = INT_MAX - 1024; u < (unsigned int)INT_MAX + 1024; u++) {
    146		snprintf(buf, sizeof(buf), "%u", u);
    147		test_lookup_fail(fd, buf);
    148	}
    149	for (u = UINT_MAX - 1024; u != 0; u++) {
    150		snprintf(buf, sizeof(buf), "%u", u);
    151		test_lookup_fail(fd, buf);
    152	}
    153}
    154
    155int main(void)
    156{
    157	unsigned int pid;
    158	int fd;
    159
    160	/*
    161	 * In theory this will loop indefinitely if kernel threads are exiled
    162	 * from /proc.
    163	 *
    164	 * Start with kthreadd.
    165	 */
    166	pid = 2;
    167	while ((fd = kernel_thread_fd(pid)) == -1 && pid < 1024) {
    168		pid++;
    169	}
    170	/* EACCES if run as non-root. */
    171	if (pid >= 1024)
    172		return 1;
    173
    174	test_readdir(fd);
    175	test_lookup(fd);
    176
    177	return 0;
    178}