18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci#define _GNU_SOURCE
48c2ecf20Sopenharmony_ci#include <assert.h>
58c2ecf20Sopenharmony_ci#include <errno.h>
68c2ecf20Sopenharmony_ci#include <fcntl.h>
78c2ecf20Sopenharmony_ci#include <linux/types.h>
88c2ecf20Sopenharmony_ci#include <sched.h>
98c2ecf20Sopenharmony_ci#include <signal.h>
108c2ecf20Sopenharmony_ci#include <stdio.h>
118c2ecf20Sopenharmony_ci#include <stdlib.h>
128c2ecf20Sopenharmony_ci#include <string.h>
138c2ecf20Sopenharmony_ci#include <syscall.h>
148c2ecf20Sopenharmony_ci#include <sys/wait.h>
158c2ecf20Sopenharmony_ci#include <sys/mman.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "pidfd.h"
188c2ecf20Sopenharmony_ci#include "../kselftest.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct error {
218c2ecf20Sopenharmony_ci	int  code;
228c2ecf20Sopenharmony_ci	char msg[512];
238c2ecf20Sopenharmony_ci};
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int error_set(struct error *err, int code, const char *fmt, ...)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	va_list args;
288c2ecf20Sopenharmony_ci	int r;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	if (code == PIDFD_PASS || !err || err->code != PIDFD_PASS)
318c2ecf20Sopenharmony_ci		return code;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	err->code = code;
348c2ecf20Sopenharmony_ci	va_start(args, fmt);
358c2ecf20Sopenharmony_ci	r = vsnprintf(err->msg, sizeof(err->msg), fmt, args);
368c2ecf20Sopenharmony_ci	assert((size_t)r < sizeof(err->msg));
378c2ecf20Sopenharmony_ci	va_end(args);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	return code;
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic void error_report(struct error *err, const char *test_name)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	switch (err->code) {
458c2ecf20Sopenharmony_ci	case PIDFD_ERROR:
468c2ecf20Sopenharmony_ci		ksft_exit_fail_msg("%s test: Fatal: %s\n", test_name, err->msg);
478c2ecf20Sopenharmony_ci		break;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	case PIDFD_FAIL:
508c2ecf20Sopenharmony_ci		/* will be: not ok %d # error %s test: %s */
518c2ecf20Sopenharmony_ci		ksft_test_result_error("%s test: %s\n", test_name, err->msg);
528c2ecf20Sopenharmony_ci		break;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	case PIDFD_SKIP:
558c2ecf20Sopenharmony_ci		/* will be: not ok %d # SKIP %s test: %s */
568c2ecf20Sopenharmony_ci		ksft_test_result_skip("%s test: %s\n", test_name, err->msg);
578c2ecf20Sopenharmony_ci		break;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	case PIDFD_XFAIL:
608c2ecf20Sopenharmony_ci		ksft_test_result_pass("%s test: Expected failure: %s\n",
618c2ecf20Sopenharmony_ci				      test_name, err->msg);
628c2ecf20Sopenharmony_ci		break;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	case PIDFD_PASS:
658c2ecf20Sopenharmony_ci		ksft_test_result_pass("%s test: Passed\n", test_name);
668c2ecf20Sopenharmony_ci		break;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	default:
698c2ecf20Sopenharmony_ci		ksft_exit_fail_msg("%s test: Unknown code: %d %s\n",
708c2ecf20Sopenharmony_ci				   test_name, err->code, err->msg);
718c2ecf20Sopenharmony_ci		break;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic inline int error_check(struct error *err, const char *test_name)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	/* In case of error we bail out and terminate the test program */
788c2ecf20Sopenharmony_ci	if (err->code == PIDFD_ERROR)
798c2ecf20Sopenharmony_ci		error_report(err, test_name);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return err->code;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#define CHILD_STACK_SIZE 8192
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistruct child {
878c2ecf20Sopenharmony_ci	char *stack;
888c2ecf20Sopenharmony_ci	pid_t pid;
898c2ecf20Sopenharmony_ci	int   fd;
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic struct child clone_newns(int (*fn)(void *), void *args,
938c2ecf20Sopenharmony_ci				struct error *err)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	static int flags = CLONE_PIDFD | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD;
968c2ecf20Sopenharmony_ci	struct child ret;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (!(flags & CLONE_NEWUSER) && geteuid() != 0)
998c2ecf20Sopenharmony_ci		flags |= CLONE_NEWUSER;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	ret.stack = mmap(NULL, CHILD_STACK_SIZE, PROT_READ | PROT_WRITE,
1028c2ecf20Sopenharmony_ci			 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
1038c2ecf20Sopenharmony_ci	if (ret.stack == MAP_FAILED) {
1048c2ecf20Sopenharmony_ci		error_set(err, -1, "mmap of stack failed (errno %d)", errno);
1058c2ecf20Sopenharmony_ci		return ret;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci#ifdef __ia64__
1098c2ecf20Sopenharmony_ci	ret.pid = __clone2(fn, ret.stack, CHILD_STACK_SIZE, flags, args, &ret.fd);
1108c2ecf20Sopenharmony_ci#else
1118c2ecf20Sopenharmony_ci	ret.pid = clone(fn, ret.stack + CHILD_STACK_SIZE, flags, args, &ret.fd);
1128c2ecf20Sopenharmony_ci#endif
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (ret.pid < 0) {
1158c2ecf20Sopenharmony_ci		error_set(err, PIDFD_ERROR, "clone failed (ret %d, errno %d)",
1168c2ecf20Sopenharmony_ci			  ret.fd, errno);
1178c2ecf20Sopenharmony_ci		return ret;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	ksft_print_msg("New child: %d, fd: %d\n", ret.pid, ret.fd);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	return ret;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic inline void child_close(struct child *child)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	close(child->fd);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic inline int child_join(struct child *child, struct error *err)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	int r;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	r = wait_for_pid(child->pid);
1358c2ecf20Sopenharmony_ci	if (r < 0)
1368c2ecf20Sopenharmony_ci		error_set(err, PIDFD_ERROR, "waitpid failed (ret %d, errno %d)",
1378c2ecf20Sopenharmony_ci			  r, errno);
1388c2ecf20Sopenharmony_ci	else if (r > 0)
1398c2ecf20Sopenharmony_ci		error_set(err, r, "child %d reported: %d", child->pid, r);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (munmap(child->stack, CHILD_STACK_SIZE)) {
1428c2ecf20Sopenharmony_ci		error_set(err, -1, "munmap of child stack failed (errno %d)", errno);
1438c2ecf20Sopenharmony_ci		r = -1;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	return r;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic inline int child_join_close(struct child *child, struct error *err)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	child_close(child);
1528c2ecf20Sopenharmony_ci	return child_join(child, err);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic inline void trim_newline(char *str)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	char *pos = strrchr(str, '\n');
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (pos)
1608c2ecf20Sopenharmony_ci		*pos = '\0';
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic int verify_fdinfo(int pidfd, struct error *err, const char *prefix,
1648c2ecf20Sopenharmony_ci			 size_t prefix_len, const char *expect, ...)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	char buffer[512] = {0, };
1678c2ecf20Sopenharmony_ci	char path[512] = {0, };
1688c2ecf20Sopenharmony_ci	va_list args;
1698c2ecf20Sopenharmony_ci	FILE *f;
1708c2ecf20Sopenharmony_ci	char *line = NULL;
1718c2ecf20Sopenharmony_ci	size_t n = 0;
1728c2ecf20Sopenharmony_ci	int found = 0;
1738c2ecf20Sopenharmony_ci	int r;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	va_start(args, expect);
1768c2ecf20Sopenharmony_ci	r = vsnprintf(buffer, sizeof(buffer), expect, args);
1778c2ecf20Sopenharmony_ci	assert((size_t)r < sizeof(buffer));
1788c2ecf20Sopenharmony_ci	va_end(args);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", pidfd);
1818c2ecf20Sopenharmony_ci	f = fopen(path, "re");
1828c2ecf20Sopenharmony_ci	if (!f)
1838c2ecf20Sopenharmony_ci		return error_set(err, PIDFD_ERROR, "fdinfo open failed for %d",
1848c2ecf20Sopenharmony_ci				 pidfd);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	while (getline(&line, &n, f) != -1) {
1878c2ecf20Sopenharmony_ci		char *val;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		if (strncmp(line, prefix, prefix_len))
1908c2ecf20Sopenharmony_ci			continue;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		found = 1;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci		val = line + prefix_len;
1958c2ecf20Sopenharmony_ci		r = strcmp(val, buffer);
1968c2ecf20Sopenharmony_ci		if (r != 0) {
1978c2ecf20Sopenharmony_ci			trim_newline(line);
1988c2ecf20Sopenharmony_ci			trim_newline(buffer);
1998c2ecf20Sopenharmony_ci			error_set(err, PIDFD_FAIL, "%s '%s' != '%s'",
2008c2ecf20Sopenharmony_ci				  prefix, val, buffer);
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci		break;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	free(line);
2068c2ecf20Sopenharmony_ci	fclose(f);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (found == 0)
2098c2ecf20Sopenharmony_ci		return error_set(err, PIDFD_FAIL, "%s not found for fd %d",
2108c2ecf20Sopenharmony_ci				 prefix, pidfd);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	return PIDFD_PASS;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic int child_fdinfo_nspid_test(void *args)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	struct error err;
2188c2ecf20Sopenharmony_ci	int pidfd;
2198c2ecf20Sopenharmony_ci	int r;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* if we got no fd for the sibling, we are done */
2228c2ecf20Sopenharmony_ci	if (!args)
2238c2ecf20Sopenharmony_ci		return PIDFD_PASS;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* verify that we can not resolve the pidfd for a process
2268c2ecf20Sopenharmony_ci	 * in a sibling pid namespace, i.e. a pid namespace it is
2278c2ecf20Sopenharmony_ci	 * not in our or a descended namespace
2288c2ecf20Sopenharmony_ci	 */
2298c2ecf20Sopenharmony_ci	r = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0);
2308c2ecf20Sopenharmony_ci	if (r < 0) {
2318c2ecf20Sopenharmony_ci		ksft_print_msg("Failed to remount / private\n");
2328c2ecf20Sopenharmony_ci		return PIDFD_ERROR;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	(void)umount2("/proc", MNT_DETACH);
2368c2ecf20Sopenharmony_ci	r = mount("proc", "/proc", "proc", 0, NULL);
2378c2ecf20Sopenharmony_ci	if (r < 0) {
2388c2ecf20Sopenharmony_ci		ksft_print_msg("Failed to remount /proc\n");
2398c2ecf20Sopenharmony_ci		return PIDFD_ERROR;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	pidfd = *(int *)args;
2438c2ecf20Sopenharmony_ci	r = verify_fdinfo(pidfd, &err, "NSpid:", 6, "\t0\n");
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (r != PIDFD_PASS)
2468c2ecf20Sopenharmony_ci		ksft_print_msg("NSpid fdinfo check failed: %s\n", err.msg);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return r;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic void test_pidfd_fdinfo_nspid(void)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct child a, b;
2548c2ecf20Sopenharmony_ci	struct error err = {0, };
2558c2ecf20Sopenharmony_ci	const char *test_name = "pidfd check for NSpid in fdinfo";
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	/* Create a new child in a new pid and mount namespace */
2588c2ecf20Sopenharmony_ci	a = clone_newns(child_fdinfo_nspid_test, NULL, &err);
2598c2ecf20Sopenharmony_ci	error_check(&err, test_name);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	/* Pass the pidfd representing the first child to the
2628c2ecf20Sopenharmony_ci	 * second child, which will be in a sibling pid namespace,
2638c2ecf20Sopenharmony_ci	 * which means that the fdinfo NSpid entry for the pidfd
2648c2ecf20Sopenharmony_ci	 * should only contain '0'.
2658c2ecf20Sopenharmony_ci	 */
2668c2ecf20Sopenharmony_ci	b = clone_newns(child_fdinfo_nspid_test, &a.fd, &err);
2678c2ecf20Sopenharmony_ci	error_check(&err, test_name);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	/* The children will have pid 1 in the new pid namespace,
2708c2ecf20Sopenharmony_ci	 * so the line must be 'NSPid:\t<pid>\t1'.
2718c2ecf20Sopenharmony_ci	 */
2728c2ecf20Sopenharmony_ci	verify_fdinfo(a.fd, &err, "NSpid:", 6, "\t%d\t%d\n", a.pid, 1);
2738c2ecf20Sopenharmony_ci	verify_fdinfo(b.fd, &err, "NSpid:", 6, "\t%d\t%d\n", b.pid, 1);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* wait for the process, check the exit status and set
2768c2ecf20Sopenharmony_ci	 * 'err' accordingly, if it is not already set.
2778c2ecf20Sopenharmony_ci	 */
2788c2ecf20Sopenharmony_ci	child_join_close(&a, &err);
2798c2ecf20Sopenharmony_ci	child_join_close(&b, &err);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	error_report(&err, test_name);
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic void test_pidfd_dead_fdinfo(void)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	struct child a;
2878c2ecf20Sopenharmony_ci	struct error err = {0, };
2888c2ecf20Sopenharmony_ci	const char *test_name = "pidfd check fdinfo for dead process";
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	/* Create a new child in a new pid and mount namespace */
2918c2ecf20Sopenharmony_ci	a = clone_newns(child_fdinfo_nspid_test, NULL, &err);
2928c2ecf20Sopenharmony_ci	error_check(&err, test_name);
2938c2ecf20Sopenharmony_ci	child_join(&a, &err);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	verify_fdinfo(a.fd, &err, "Pid:", 4, "\t-1\n");
2968c2ecf20Sopenharmony_ci	verify_fdinfo(a.fd, &err, "NSpid:", 6, "\t-1\n");
2978c2ecf20Sopenharmony_ci	child_close(&a);
2988c2ecf20Sopenharmony_ci	error_report(&err, test_name);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ciint main(int argc, char **argv)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	ksft_print_header();
3048c2ecf20Sopenharmony_ci	ksft_set_plan(2);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	test_pidfd_fdinfo_nspid();
3078c2ecf20Sopenharmony_ci	test_pidfd_dead_fdinfo();
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return ksft_exit_pass();
3108c2ecf20Sopenharmony_ci}
311