16 int command[2], report[2];
23static void* task_view_worker(
void* argument) {
27 if (unshare(CLONE_NEWUTS) || ns_set(
"proc-worker",
"proc-worker") ||
28 ns_path_identity(
"/proc/self/ns/uts", &report.self) ||
29 ns_path_identity(
"/proc/thread-self/ns/uts", &report.caller))
31 state->held = open(
"/proc/thread-self/ns/uts", O_RDONLY | O_CLOEXEC);
32 if (state->held < 0 || ns_write(state->report[1], &report,
sizeof(report)) ||
33 ns_receive(state->command[0],
'Q'))
39static int proc_views(
void) {
40 int failed = 0, started = 0, joined = 0, saved = -1, old_view = -1;
42 struct task_view state = {.command = {-1, -1}, .report = {-1, -1}, .held = -1};
45 char worker_path[96], leader_path[96], ns_directory[96], link[80];
46 DIR* directory = NULL;
47 void* readonly = MAP_FAILED;
48 CHECK(ns_set(
"proc-leader",
"proc-leader") == 0);
49 saved = open(
"/proc/thread-self/ns/uts", O_RDONLY | O_CLOEXEC);
50 CHECK(saved >= 0 && ns_fd_identity(saved, &leader) == 0);
51 snprintf(leader_path,
sizeof(leader_path),
"/proc/%d/ns/uts", getpid());
52 CHECK(ns_path_identity(leader_path, &path) == 0 && ns_same(path, leader));
53 snprintf(ns_directory,
sizeof(ns_directory),
"/proc/%d/ns", getpid());
54 directory = opendir(ns_directory);
55 CHECK(directory != NULL);
57 for (
struct dirent* entry; (entry = readdir(directory));)
58 found += !strcmp(entry->d_name,
"uts");
62 CHECK(pipe(state.command) == 0 && pipe(state.report) == 0);
63 CHECK(pthread_create(&
worker, NULL, task_view_worker, &state) == 0);
65 CHECK(ns_read(state.report[0], &report,
sizeof(report)) == 0);
66 CHECK(ns_same(report.self, leader) && !ns_same(report.caller, leader));
67 snprintf(worker_path,
sizeof(worker_path),
"/proc/%d/task/%d/ns/uts", getpid(), report.tid);
68 CHECK(ns_path_identity(worker_path, &path) == 0 && ns_same(path, report.caller));
69 snprintf(ns_directory,
sizeof(ns_directory),
"/proc/%d/task/%d/ns", getpid(), report.tid);
70 old_view = open(ns_directory, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
72 memset(link,
'?',
sizeof(link));
73 ssize_t count = readlink(worker_path, link,
sizeof(link) - 1);
74 CHECK(count > 6 && count < (ssize_t)
sizeof(link));
76 CHECK(!strncmp(link,
"uts:[", 5) && link[count - 1] ==
']');
77 char short_link[4] = {
'?',
'?',
'?',
'?'};
78 CHECK(readlink(worker_path, short_link, 3) == 3 && !memcmp(short_link,
"uts", 3) &&
79 short_link[3] ==
'?');
80 readonly = mmap(NULL, ns_page, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
81 CHECK(readonly != MAP_FAILED);
83 CHECK(readlink(worker_path, readonly, 20) == -1 && errno == EFAULT);
84 CHECK(ns_expect(
"proc-leader",
"proc-leader") == 0);
85 CHECK(ns_send(state.command[1],
'Q') == 0);
86 CHECK(pthread_join(
worker, NULL) == 0);
88 CHECK(state.result == 0);
89 CHECK(ns_fd_identity(state.held, &held) == 0 && ns_same(held, report.caller));
90 CHECK(setns(state.held, 0) == 0 && ns_expect(
"proc-worker",
"proc-worker") == 0);
91 CHECK(ns_path_identity(
"/proc/thread-self/ns/uts", &path) == 0 && ns_same(path, held));
92 CHECK(ns_fd_identity(saved, &path) == 0 && ns_same(path, leader));
93 CHECK(setns(saved, CLONE_NEWUTS) == 0 && ns_expect(
"proc-leader",
"proc-leader") == 0);
94 int stale = openat(old_view,
"uts", O_RDONLY | O_CLOEXEC);
99 if (failed && state.command[1] >= 0) {
100 close(state.command[1]);
101 state.command[1] = -1;
103 if (started && !joined)
104 pthread_join(
worker, NULL);
105 for (
int i = 0; i < 2; ++i) {
106 if (state.command[i] >= 0)
107 close(state.command[i]);
108 if (state.report[i] >= 0)
109 close(state.report[i]);
122 if (readonly != MAP_FAILED)
123 munmap(readonly, ns_page);
127static int credential_target(
int command,
int report,
void* argument) {
129 if (unshare(CLONE_NEWUTS) || setgroups(0, NULL) || setresgid(1002, 1002, 1002) ||
130 setresuid(1001, 1001, 1001) || prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) || ns_send(report,
'R') ||
131 ns_receive(command,
'D') || prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) || ns_send(report,
'D') ||
132 ns_receive(command,
'Q'))
137static int proc_permissions(
void) {
138 int failed = 0, fd = -1;
139 struct ns_peer child = NS_PEER_INITIALIZER;
140 char path[80], text[80];
141 CHECK(ns_spawn(&child, credential_target, NULL) == 0 && ns_receive(child.report,
'R') == 0);
142 snprintf(path,
sizeof(path),
"/proc/%d/ns/uts", child.pid);
144 CHECK(readlink(path, text,
sizeof(text)) == -1 && errno == EACCES);
147 CHECK(setfsuid((uid_t)-1) == 1001 && setfsgid((gid_t)-1) == 1002);
148 CHECK(getuid() == 0 && geteuid() == 0);
149 CHECK(readlink(path, text,
sizeof(text)) > 0);
150 fd = open(path, O_RDONLY | O_CLOEXEC);
152 CHECK(ns_send(child.command,
'D') == 0 && ns_receive(child.report,
'D') == 0);
154 CHECK(readlink(path, text,
sizeof(text)) == -1 && errno == EACCES);
155 CHECK(readlink(
"/proc/thread-self/ns/uts", text,
sizeof(text)) > 0);
156 CHECK(ns_send(child.command,
'Q') == 0 && ns_join(&child) == 0);
158 CHECK(ns_fd_identity(fd, &retained) == 0);
162 if (setfsuid((uid_t)-1) != 0 || setfsgid((gid_t)-1) != 0)
171 return proc_views() || proc_permissions();