1// SPDX-License-Identifier: GPL-2.0
2#define _GNU_SOURCE
3#include <sched.h>
4#include <unistd.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <signal.h>
8#include <errno.h>
9#include <sys/types.h>
10#include <sys/stat.h>
11#include <fcntl.h>
12#include <sys/ioctl.h>
13#include <sys/prctl.h>
14#include <sys/wait.h>
15
16#define NSIO    0xb7
17#define NS_GET_USERNS   _IO(NSIO, 0x1)
18
19#define pr_err(fmt, ...) \
20		({ \
21			fprintf(stderr, "%s:%d:" fmt ": %m\n", \
22				__func__, __LINE__, ##__VA_ARGS__); \
23			1; \
24		})
25
26int main(int argc, char *argvp[])
27{
28	int pfd[2], ns, uns, init_uns;
29	struct stat st1, st2;
30	char path[128];
31	pid_t pid;
32	char c;
33
34	if (pipe(pfd))
35		return 1;
36
37	pid = fork();
38	if (pid < 0)
39		return pr_err("fork");
40	if (pid == 0) {
41		prctl(PR_SET_PDEATHSIG, SIGKILL);
42		if (unshare(CLONE_NEWUTS | CLONE_NEWUSER))
43			return pr_err("unshare");
44		close(pfd[0]);
45		close(pfd[1]);
46		while (1)
47			sleep(1);
48		return 0;
49	}
50	close(pfd[1]);
51	if (read(pfd[0], &c, 1) != 0)
52		return pr_err("Unable to read from pipe");
53	close(pfd[0]);
54
55	snprintf(path, sizeof(path), "/proc/%d/ns/uts", pid);
56	ns = open(path, O_RDONLY);
57	if (ns < 0)
58		return pr_err("Unable to open %s", path);
59
60	uns = ioctl(ns, NS_GET_USERNS);
61	if (uns < 0)
62		return pr_err("Unable to get an owning user namespace");
63
64	if (fstat(uns, &st1))
65		return pr_err("fstat");
66
67	snprintf(path, sizeof(path), "/proc/%d/ns/user", pid);
68	if (stat(path, &st2))
69		return pr_err("stat");
70
71	if (st1.st_ino != st2.st_ino)
72		return pr_err("NS_GET_USERNS returned a wrong namespace");
73
74	init_uns = ioctl(uns, NS_GET_USERNS);
75	if (uns < 0)
76		return pr_err("Unable to get an owning user namespace");
77
78	if (ioctl(init_uns, NS_GET_USERNS) >= 0 || errno != EPERM)
79		return pr_err("Don't get EPERM");
80
81	if (unshare(CLONE_NEWUSER))
82		return pr_err("unshare");
83
84	if (ioctl(ns, NS_GET_USERNS) >= 0 || errno != EPERM)
85		return pr_err("Don't get EPERM");
86	if (ioctl(init_uns, NS_GET_USERNS) >= 0 || errno != EPERM)
87		return pr_err("Don't get EPERM");
88
89	kill(pid, SIGKILL);
90	wait(NULL);
91	return 0;
92}
93