Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(181)

Side by Side Diff: sandbox/linux/suid/init_process.c

Issue 9295005: Calling clone(CLONE_NEWPID) results in the new pid namespace getting a new "init" process. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #define _GNU_SOURCE
6 #include "init_process.h"
7
8 #include <dirent.h>
9 #include <fcntl.h>
10 #include <signal.h>
11 #include <stdbool.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <sys/wait.h>
17 #include <unistd.h>
18
19
20 static void sigchld(int signo, siginfo_t *info, void *unused) {
21 return;
22 }
23
24 static int getProcessStatus(int proc_fd, const char *process,
25 const char *field) {
26 int ret = -1;
27
28 // Open "/proc/${process}/status"
29 char *buf = malloc(strlen(process) + 80);
30 sprintf(buf, "%s/status", process);
31 int fd = openat(proc_fd, buf, O_RDONLY);
32 if (fd >= 0) {
33 // Only bother to read the first 4kB. All of the fields that we
34 // are interested in will show up much earlier.
35 buf = realloc(buf, 4097);
36 size_t sz = read(fd, buf, 4096);
37 if (sz > 0) {
38 // Find a matching "field"
39 buf[sz] = '\000';
40 char *f = malloc(strlen(field) + 4);
41 sprintf(f, "\n%s:\t", field);
42 char *ptr = strstr(buf, f);
43 if (ptr) {
44 // Extract the numerical value of the "field"
45 ret = atoi(ptr + strlen(f));
46 }
47 free(f);
48 }
49 close(fd);
50 }
51 free(buf);
52 return ret;
53 }
54
55 static bool hasChildren(int proc_fd, int pid) {
56 bool ret = false;
57
58 // Open "/proc"
59 int fd = dup(proc_fd);
60 lseek(fd, SEEK_SET, 0);
61 DIR *dir = fd >= 0 ? fdopendir(fd) : NULL;
62 struct dirent de, *res;
63 while (dir && !readdir_r(dir, &de, &res) && res) {
64 // Find numerical entries. Those are processes.
65 if (res->d_name[0] <= '0' || res->d_name[0] > '9') {
66 continue;
67 }
68
69 // For each process, check the parent's pid
70 int ppid = getProcessStatus(proc_fd, res->d_name, "PPid");
71
72 if (ppid == pid) {
73 // We found a child process. We can stop searching, now
74 ret = true;
75 break;
76 }
77 }
78 closedir(dir);
79 return ret;
80 }
81
82 void SystemInitProcess(int init_fd, int child_pid, int proc_fd, int null_fd) {
83 int ret = 0;
84
85 // CLONE_NEWPID doesn't adjust the contents of the "/proc" file system.
86 // This is very confusing. And it is even possible the kernel developers
87 // will consider this a bug and fix it at some point in the future.
88 // So, to be on the safe side, we explicitly retrieve our process id
89 // from the "/proc" file system. This should continue to work, even if
90 // the kernel eventually gets fixed so that "/proc" shows the view from
91 // inside of the new pid namespace.
92 pid_t init_pid = getProcessStatus(proc_fd, "self", "Pid");
93 if (init_pid <= 0) {
94 fprintf(stderr,
95 "Failed to determine real process id of new \"init\" process\n");
96 _exit(1);
97 }
98
99 // Redirect stdio to /dev/null
100 if (null_fd < 0 ||
101 dup2(null_fd, 0) != 0 ||
102 dup2(null_fd, 1) != 1 ||
103 dup2(null_fd, 2) != 2) {
104 fprintf(stderr, "Failed to point stdio to a safe place\n");
105 _exit(1);
106 }
107 close(null_fd);
108
109 // Close all file handles
110 int fds_fd = openat(proc_fd, "self/fd", O_RDONLY | O_DIRECTORY);
111 DIR *dir = fds_fd >= 0 ? fdopendir(fds_fd) : NULL;
112 if (dir == NULL) {
113 // If we don't know the list of our open file handles, just try closing
114 // all valid ones.
115 for (int fd = sysconf(_SC_OPEN_MAX); --fd > 2; ) {
116 if (fd != init_fd && fd != proc_fd) {
117 close(fd);
118 }
119 }
120 } else {
121 // If available, it is much more efficient to just close the file
122 // handles that show up in "/proc/self/fd/"
123 struct dirent de, *res;
124 while (!readdir_r(dir, &de, &res) && res) {
125 if (res->d_name[0] < '0')
126 continue;
127 int fd = atoi(res->d_name);
128 if (fd > 2 && fd != init_fd && fd != proc_fd && fd != dirfd(dir)) {
129 close(fd);
130 }
131 }
132 closedir(dir);
133 }
134
135 // Set up signal handler to catch SIGCHLD, but mask the signal for now
136 sigset_t mask;
137 sigemptyset(&mask);
138 sigaddset(&mask, SIGCHLD);
139 sigprocmask(SIG_BLOCK, &mask, NULL);
140 struct sigaction sa;
141 memset(&sa, 0, sizeof(sa));
142 sa.sa_sigaction = sigchld;
143 sa.sa_flags = SA_RESTART | SA_SIGINFO;
agl 2012/01/27 15:14:32 you setup a sigaction struct, but I don't see wher
Markus (顧孟勤) 2012/01/27 17:44:09 Good call. There was supposed to be call to sigact
144
145 // Notify other processes that we are done initializing
146 write(init_fd, " ", 1);
147 close(init_fd);
148
149 // Handle dying processes that have been re-parented to the "init" process
150 for (;;) {
151 // Wait until we receive a SIGCHLD signal. Our signal handler doesn't
152 // actually need to do anything, though
153 sigwaitinfo(&mask, NULL);
154
155 bool retry = false;
156 do {
157 for (;;) {
158 // Reap all exit codes of our child processes. This includes both
159 // processes that originally were our immediate children, and processes
160 // that have since been re-parented to be our children.
161 int status;
162 pid_t pid = waitpid(0, &status, __WALL | WNOHANG);
163 if (pid <= 0) {
164 break;
165 } else {
166 // We found some newly deceased child processes. Better schedule
167 // another very thorough inspection of our state.
168 retry = false;
agl 2012/01/27 15:14:32 If you're assuming that waitpid will already retur
Markus (顧孟勤) 2012/01/27 17:44:09 This code is subtle. If you can think of a better
169 }
170 if (pid == child_pid) {
171 // If our first immediate child died, remember its exit code. That's
172 // the exit code that we should be reporting to our parent process
173 if (WIFEXITED(status)) {
174 ret = WEXITSTATUS(status);
175 } else if (WIFSIGNALED(status)) {
176 ret = -WTERMSIG(status);
177 }
178 }
179 }
180 if (hasChildren(proc_fd, init_pid)) {
181 // As long as we still have child processes, continue waiting for
182 // their ultimate demise.
183 retry = false;
184 } else {
185 if (retry) {
186 // No more child processes. We can exit now.
187 if (ret < 0) {
188 // Try to exit with the same signal that our child terminated with
189 signal(-ret, SIG_DFL);
190 kill(1, -ret);
191 ret = 1;
192 }
193 // Exit with the same exit code that our child exited with
194 _exit(ret);
195 } else {
196 // There is a little bit of a race condition between getting
197 // notifications and scanning the "/proc" file system. This is
198 // particularly true, because scanning "/proc" cannot possibly be
199 // an atomic operation.
200 // If we find that we no longer appear to have any children, we check
201 // one more time whether there are any children we can now reap.
202 // They might have died while we were scanning "/proc" and if so,
203 // they should now show up.
204 retry = true;
205 }
206 }
207 } while (retry);
208 }
209 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698