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

pidfd_fdinfo_test.c (7002B)


      1// SPDX-License-Identifier: GPL-2.0
      2
      3#define _GNU_SOURCE
      4#include <assert.h>
      5#include <errno.h>
      6#include <fcntl.h>
      7#include <linux/types.h>
      8#include <sched.h>
      9#include <signal.h>
     10#include <stdio.h>
     11#include <stdlib.h>
     12#include <string.h>
     13#include <syscall.h>
     14#include <sys/wait.h>
     15#include <sys/mman.h>
     16
     17#include "pidfd.h"
     18#include "../kselftest.h"
     19
     20struct error {
     21	int  code;
     22	char msg[512];
     23};
     24
     25static int error_set(struct error *err, int code, const char *fmt, ...)
     26{
     27	va_list args;
     28	int r;
     29
     30	if (code == PIDFD_PASS || !err || err->code != PIDFD_PASS)
     31		return code;
     32
     33	err->code = code;
     34	va_start(args, fmt);
     35	r = vsnprintf(err->msg, sizeof(err->msg), fmt, args);
     36	assert((size_t)r < sizeof(err->msg));
     37	va_end(args);
     38
     39	return code;
     40}
     41
     42static void error_report(struct error *err, const char *test_name)
     43{
     44	switch (err->code) {
     45	case PIDFD_ERROR:
     46		ksft_exit_fail_msg("%s test: Fatal: %s\n", test_name, err->msg);
     47		break;
     48
     49	case PIDFD_FAIL:
     50		/* will be: not ok %d # error %s test: %s */
     51		ksft_test_result_error("%s test: %s\n", test_name, err->msg);
     52		break;
     53
     54	case PIDFD_SKIP:
     55		/* will be: not ok %d # SKIP %s test: %s */
     56		ksft_test_result_skip("%s test: %s\n", test_name, err->msg);
     57		break;
     58
     59	case PIDFD_XFAIL:
     60		ksft_test_result_pass("%s test: Expected failure: %s\n",
     61				      test_name, err->msg);
     62		break;
     63
     64	case PIDFD_PASS:
     65		ksft_test_result_pass("%s test: Passed\n");
     66		break;
     67
     68	default:
     69		ksft_exit_fail_msg("%s test: Unknown code: %d %s\n",
     70				   test_name, err->code, err->msg);
     71		break;
     72	}
     73}
     74
     75static inline int error_check(struct error *err, const char *test_name)
     76{
     77	/* In case of error we bail out and terminate the test program */
     78	if (err->code == PIDFD_ERROR)
     79		error_report(err, test_name);
     80
     81	return err->code;
     82}
     83
     84#define CHILD_STACK_SIZE 8192
     85
     86struct child {
     87	char *stack;
     88	pid_t pid;
     89	int   fd;
     90};
     91
     92static struct child clone_newns(int (*fn)(void *), void *args,
     93				struct error *err)
     94{
     95	static int flags = CLONE_PIDFD | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD;
     96	struct child ret;
     97
     98	if (!(flags & CLONE_NEWUSER) && geteuid() != 0)
     99		flags |= CLONE_NEWUSER;
    100
    101	ret.stack = mmap(NULL, CHILD_STACK_SIZE, PROT_READ | PROT_WRITE,
    102			 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
    103	if (ret.stack == MAP_FAILED) {
    104		error_set(err, -1, "mmap of stack failed (errno %d)", errno);
    105		return ret;
    106	}
    107
    108#ifdef __ia64__
    109	ret.pid = __clone2(fn, ret.stack, CHILD_STACK_SIZE, flags, args, &ret.fd);
    110#else
    111	ret.pid = clone(fn, ret.stack + CHILD_STACK_SIZE, flags, args, &ret.fd);
    112#endif
    113
    114	if (ret.pid < 0) {
    115		error_set(err, PIDFD_ERROR, "clone failed (ret %d, errno %d)",
    116			  ret.fd, errno);
    117		return ret;
    118	}
    119
    120	ksft_print_msg("New child: %d, fd: %d\n", ret.pid, ret.fd);
    121
    122	return ret;
    123}
    124
    125static inline void child_close(struct child *child)
    126{
    127	close(child->fd);
    128}
    129
    130static inline int child_join(struct child *child, struct error *err)
    131{
    132	int r;
    133
    134	r = wait_for_pid(child->pid);
    135	if (r < 0)
    136		error_set(err, PIDFD_ERROR, "waitpid failed (ret %d, errno %d)",
    137			  r, errno);
    138	else if (r > 0)
    139		error_set(err, r, "child %d reported: %d", child->pid, r);
    140
    141	if (munmap(child->stack, CHILD_STACK_SIZE)) {
    142		error_set(err, -1, "munmap of child stack failed (errno %d)", errno);
    143		r = -1;
    144	}
    145
    146	return r;
    147}
    148
    149static inline int child_join_close(struct child *child, struct error *err)
    150{
    151	child_close(child);
    152	return child_join(child, err);
    153}
    154
    155static inline void trim_newline(char *str)
    156{
    157	char *pos = strrchr(str, '\n');
    158
    159	if (pos)
    160		*pos = '\0';
    161}
    162
    163static int verify_fdinfo(int pidfd, struct error *err, const char *prefix,
    164			 size_t prefix_len, const char *expect, ...)
    165{
    166	char buffer[512] = {0, };
    167	char path[512] = {0, };
    168	va_list args;
    169	FILE *f;
    170	char *line = NULL;
    171	size_t n = 0;
    172	int found = 0;
    173	int r;
    174
    175	va_start(args, expect);
    176	r = vsnprintf(buffer, sizeof(buffer), expect, args);
    177	assert((size_t)r < sizeof(buffer));
    178	va_end(args);
    179
    180	snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", pidfd);
    181	f = fopen(path, "re");
    182	if (!f)
    183		return error_set(err, PIDFD_ERROR, "fdinfo open failed for %d",
    184				 pidfd);
    185
    186	while (getline(&line, &n, f) != -1) {
    187		char *val;
    188
    189		if (strncmp(line, prefix, prefix_len))
    190			continue;
    191
    192		found = 1;
    193
    194		val = line + prefix_len;
    195		r = strcmp(val, buffer);
    196		if (r != 0) {
    197			trim_newline(line);
    198			trim_newline(buffer);
    199			error_set(err, PIDFD_FAIL, "%s '%s' != '%s'",
    200				  prefix, val, buffer);
    201		}
    202		break;
    203	}
    204
    205	free(line);
    206	fclose(f);
    207
    208	if (found == 0)
    209		return error_set(err, PIDFD_FAIL, "%s not found for fd %d",
    210				 prefix, pidfd);
    211
    212	return PIDFD_PASS;
    213}
    214
    215static int child_fdinfo_nspid_test(void *args)
    216{
    217	struct error err;
    218	int pidfd;
    219	int r;
    220
    221	/* if we got no fd for the sibling, we are done */
    222	if (!args)
    223		return PIDFD_PASS;
    224
    225	/* verify that we can not resolve the pidfd for a process
    226	 * in a sibling pid namespace, i.e. a pid namespace it is
    227	 * not in our or a descended namespace
    228	 */
    229	r = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0);
    230	if (r < 0) {
    231		ksft_print_msg("Failed to remount / private\n");
    232		return PIDFD_ERROR;
    233	}
    234
    235	(void)umount2("/proc", MNT_DETACH);
    236	r = mount("proc", "/proc", "proc", 0, NULL);
    237	if (r < 0) {
    238		ksft_print_msg("Failed to remount /proc\n");
    239		return PIDFD_ERROR;
    240	}
    241
    242	pidfd = *(int *)args;
    243	r = verify_fdinfo(pidfd, &err, "NSpid:", 6, "\t0\n");
    244
    245	if (r != PIDFD_PASS)
    246		ksft_print_msg("NSpid fdinfo check failed: %s\n", err.msg);
    247
    248	return r;
    249}
    250
    251static void test_pidfd_fdinfo_nspid(void)
    252{
    253	struct child a, b;
    254	struct error err = {0, };
    255	const char *test_name = "pidfd check for NSpid in fdinfo";
    256
    257	/* Create a new child in a new pid and mount namespace */
    258	a = clone_newns(child_fdinfo_nspid_test, NULL, &err);
    259	error_check(&err, test_name);
    260
    261	/* Pass the pidfd representing the first child to the
    262	 * second child, which will be in a sibling pid namespace,
    263	 * which means that the fdinfo NSpid entry for the pidfd
    264	 * should only contain '0'.
    265	 */
    266	b = clone_newns(child_fdinfo_nspid_test, &a.fd, &err);
    267	error_check(&err, test_name);
    268
    269	/* The children will have pid 1 in the new pid namespace,
    270	 * so the line must be 'NSPid:\t<pid>\t1'.
    271	 */
    272	verify_fdinfo(a.fd, &err, "NSpid:", 6, "\t%d\t%d\n", a.pid, 1);
    273	verify_fdinfo(b.fd, &err, "NSpid:", 6, "\t%d\t%d\n", b.pid, 1);
    274
    275	/* wait for the process, check the exit status and set
    276	 * 'err' accordingly, if it is not already set.
    277	 */
    278	child_join_close(&a, &err);
    279	child_join_close(&b, &err);
    280
    281	error_report(&err, test_name);
    282}
    283
    284static void test_pidfd_dead_fdinfo(void)
    285{
    286	struct child a;
    287	struct error err = {0, };
    288	const char *test_name = "pidfd check fdinfo for dead process";
    289
    290	/* Create a new child in a new pid and mount namespace */
    291	a = clone_newns(child_fdinfo_nspid_test, NULL, &err);
    292	error_check(&err, test_name);
    293	child_join(&a, &err);
    294
    295	verify_fdinfo(a.fd, &err, "Pid:", 4, "\t-1\n");
    296	verify_fdinfo(a.fd, &err, "NSpid:", 6, "\t-1\n");
    297	child_close(&a);
    298	error_report(&err, test_name);
    299}
    300
    301int main(int argc, char **argv)
    302{
    303	ksft_print_header();
    304	ksft_set_plan(2);
    305
    306	test_pidfd_fdinfo_nspid();
    307	test_pidfd_dead_fdinfo();
    308
    309	return ksft_exit_pass();
    310}