Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 // Copyright (c) 2008 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.
5 #include "base/process_util.h"
7 #include <ctype.h>
8 #include <dirent.h>
9 #include <fcntl.h>
10 #include <memory>
11 #include <unistd.h>
12 #include <string>
13 #include <sys/types.h>
14 #include <sys/wait.h>
16 #include "base/debug_util.h"
17 #include "base/eintr_wrapper.h"
18 #include "base/file_util.h"
19 #include "base/logging.h"
20 #include "base/string_tokenizer.h"
21 #include "base/string_util.h"
23 #ifdef MOZ_WIDGET_GONK
24 /*
25 * AID_APP is the first application UID used by Android. We're using
26 * it as our unprivilegied UID. This ensure the UID used is not
27 * shared with any other processes than our own childs.
28 */
29 # include <private/android_filesystem_config.h>
30 # define CHILD_UNPRIVILEGED_UID AID_APP
31 # define CHILD_UNPRIVILEGED_GID AID_APP
32 #else
33 /*
34 * On platforms that are not gonk based, we fall back to an arbitrary
35 * UID. This is generally the UID for user `nobody', albeit it is not
36 * always the case.
37 */
38 # define CHILD_UNPRIVILEGED_UID 65534
39 # define CHILD_UNPRIVILEGED_GID 65534
40 #endif
42 #ifdef ANDROID
43 #include <pthread.h>
44 /*
45 * Currently, PR_DuplicateEnvironment is implemented in
46 * mozglue/build/BionicGlue.cpp
47 */
48 #define HAVE_PR_DUPLICATE_ENVIRONMENT
50 #include "plstr.h"
51 #include "prenv.h"
52 #include "prmem.h"
53 /* Temporary until we have PR_DuplicateEnvironment in prenv.h */
54 extern "C" { NSPR_API(pthread_mutex_t *)PR_GetEnvLock(void); }
55 #endif
57 namespace {
59 enum ParsingState {
60 KEY_NAME,
61 KEY_VALUE
62 };
64 static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
66 } // namespace
68 namespace base {
70 #ifdef HAVE_PR_DUPLICATE_ENVIRONMENT
71 /*
72 * I tried to put PR_DuplicateEnvironment down in mozglue, but on android
73 * this winds up failing because the strdup/free calls wind up mismatching.
74 */
76 static char **
77 PR_DuplicateEnvironment(void)
78 {
79 char **result = NULL;
80 char **s;
81 char **d;
82 pthread_mutex_lock(PR_GetEnvLock());
83 for (s = environ; *s != NULL; s++)
84 ;
85 if ((result = (char **)PR_Malloc(sizeof(char *) * (s - environ + 1))) != NULL) {
86 for (s = environ, d = result; *s != NULL; s++, d++) {
87 *d = PL_strdup(*s);
88 }
89 *d = NULL;
90 }
91 pthread_mutex_unlock(PR_GetEnvLock());
92 return result;
93 }
95 class EnvironmentEnvp
96 {
97 public:
98 EnvironmentEnvp()
99 : mEnvp(PR_DuplicateEnvironment()) {}
101 EnvironmentEnvp(const environment_map &em)
102 {
103 mEnvp = (char **)PR_Malloc(sizeof(char *) * (em.size() + 1));
104 if (!mEnvp) {
105 return;
106 }
107 char **e = mEnvp;
108 for (environment_map::const_iterator it = em.begin();
109 it != em.end(); ++it, ++e) {
110 std::string str = it->first;
111 str += "=";
112 str += it->second;
113 *e = PL_strdup(str.c_str());
114 }
115 *e = NULL;
116 }
118 ~EnvironmentEnvp()
119 {
120 if (!mEnvp) {
121 return;
122 }
123 for (char **e = mEnvp; *e; ++e) {
124 PL_strfree(*e);
125 }
126 PR_Free(mEnvp);
127 }
129 char * const *AsEnvp() { return mEnvp; }
131 void ToMap(environment_map &em)
132 {
133 if (!mEnvp) {
134 return;
135 }
136 em.clear();
137 for (char **e = mEnvp; *e; ++e) {
138 const char *eq;
139 if ((eq = strchr(*e, '=')) != NULL) {
140 std::string varname(*e, eq - *e);
141 em[varname.c_str()] = &eq[1];
142 }
143 }
144 }
146 private:
147 char **mEnvp;
148 };
150 class Environment : public environment_map
151 {
152 public:
153 Environment()
154 {
155 EnvironmentEnvp envp;
156 envp.ToMap(*this);
157 }
159 char * const *AsEnvp() {
160 mEnvp.reset(new EnvironmentEnvp(*this));
161 return mEnvp->AsEnvp();
162 }
164 void Merge(const environment_map &em)
165 {
166 for (const_iterator it = em.begin(); it != em.end(); ++it) {
167 (*this)[it->first] = it->second;
168 }
169 }
170 private:
171 std::auto_ptr<EnvironmentEnvp> mEnvp;
172 };
173 #endif // HAVE_PR_DUPLICATE_ENVIRONMENT
175 bool LaunchApp(const std::vector<std::string>& argv,
176 const file_handle_mapping_vector& fds_to_remap,
177 bool wait, ProcessHandle* process_handle) {
178 return LaunchApp(argv, fds_to_remap, environment_map(),
179 wait, process_handle);
180 }
182 bool LaunchApp(const std::vector<std::string>& argv,
183 const file_handle_mapping_vector& fds_to_remap,
184 const environment_map& env_vars_to_set,
185 bool wait, ProcessHandle* process_handle,
186 ProcessArchitecture arch) {
187 return LaunchApp(argv, fds_to_remap, env_vars_to_set,
188 PRIVILEGES_INHERIT,
189 wait, process_handle);
190 }
192 bool LaunchApp(const std::vector<std::string>& argv,
193 const file_handle_mapping_vector& fds_to_remap,
194 const environment_map& env_vars_to_set,
195 ChildPrivileges privs,
196 bool wait, ProcessHandle* process_handle,
197 ProcessArchitecture arch) {
198 scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
199 // Illegal to allocate memory after fork and before execvp
200 InjectiveMultimap fd_shuffle1, fd_shuffle2;
201 fd_shuffle1.reserve(fds_to_remap.size());
202 fd_shuffle2.reserve(fds_to_remap.size());
204 #ifdef HAVE_PR_DUPLICATE_ENVIRONMENT
205 Environment env;
206 env.Merge(env_vars_to_set);
207 char * const *envp = env.AsEnvp();
208 if (!envp) {
209 DLOG(ERROR) << "FAILED to duplicate environment for: " << argv_cstr[0];
210 return false;
211 }
212 #endif
214 pid_t pid = fork();
215 if (pid < 0)
216 return false;
218 if (pid == 0) {
219 for (file_handle_mapping_vector::const_iterator
220 it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
221 fd_shuffle1.push_back(InjectionArc(it->first, it->second, false));
222 fd_shuffle2.push_back(InjectionArc(it->first, it->second, false));
223 }
225 if (!ShuffleFileDescriptors(&fd_shuffle1))
226 _exit(127);
228 CloseSuperfluousFds(fd_shuffle2);
230 for (size_t i = 0; i < argv.size(); i++)
231 argv_cstr[i] = const_cast<char*>(argv[i].c_str());
232 argv_cstr[argv.size()] = NULL;
234 SetCurrentProcessPrivileges(privs);
236 #ifdef HAVE_PR_DUPLICATE_ENVIRONMENT
237 execve(argv_cstr[0], argv_cstr.get(), envp);
238 #else
239 for (environment_map::const_iterator it = env_vars_to_set.begin();
240 it != env_vars_to_set.end(); ++it) {
241 if (setenv(it->first.c_str(), it->second.c_str(), 1/*overwrite*/))
242 _exit(127);
243 }
244 execv(argv_cstr[0], argv_cstr.get());
245 #endif
246 // if we get here, we're in serious trouble and should complain loudly
247 DLOG(ERROR) << "FAILED TO exec() CHILD PROCESS, path: " << argv_cstr[0];
248 _exit(127);
249 } else {
250 gProcessLog.print("==> process %d launched child process %d\n",
251 GetCurrentProcId(), pid);
252 if (wait)
253 HANDLE_EINTR(waitpid(pid, 0, 0));
255 if (process_handle)
256 *process_handle = pid;
257 }
259 return true;
260 }
262 bool LaunchApp(const CommandLine& cl,
263 bool wait, bool start_hidden,
264 ProcessHandle* process_handle) {
265 file_handle_mapping_vector no_files;
266 return LaunchApp(cl.argv(), no_files, wait, process_handle);
267 }
269 void SetCurrentProcessPrivileges(ChildPrivileges privs) {
270 if (privs == PRIVILEGES_INHERIT) {
271 return;
272 }
274 gid_t gid = CHILD_UNPRIVILEGED_GID;
275 uid_t uid = CHILD_UNPRIVILEGED_UID;
276 #ifdef MOZ_WIDGET_GONK
277 {
278 static bool checked_pix_max, pix_max_ok;
279 if (!checked_pix_max) {
280 checked_pix_max = true;
281 int fd = open("/proc/sys/kernel/pid_max", O_CLOEXEC | O_RDONLY);
282 if (fd < 0) {
283 DLOG(ERROR) << "Failed to open pid_max";
284 _exit(127);
285 }
286 char buf[PATH_MAX];
287 ssize_t len = read(fd, buf, sizeof(buf) - 1);
288 close(fd);
289 if (len < 0) {
290 DLOG(ERROR) << "Failed to read pid_max";
291 _exit(127);
292 }
293 buf[len] = '\0';
294 int pid_max = atoi(buf);
295 pix_max_ok =
296 (pid_max + CHILD_UNPRIVILEGED_UID > CHILD_UNPRIVILEGED_UID);
297 }
298 if (!pix_max_ok) {
299 DLOG(ERROR) << "Can't safely get unique uid/gid";
300 _exit(127);
301 }
302 gid += getpid();
303 uid += getpid();
304 }
305 #endif
306 if (setgid(gid) != 0) {
307 DLOG(ERROR) << "FAILED TO setgid() CHILD PROCESS";
308 _exit(127);
309 }
310 if (setuid(uid) != 0) {
311 DLOG(ERROR) << "FAILED TO setuid() CHILD PROCESS";
312 _exit(127);
313 }
314 if (chdir("/") != 0)
315 gProcessLog.print("==> could not chdir()\n");
316 }
318 NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name,
319 const ProcessFilter* filter)
320 : executable_name_(executable_name), filter_(filter) {
321 procfs_dir_ = opendir("/proc");
322 }
324 NamedProcessIterator::~NamedProcessIterator() {
325 if (procfs_dir_) {
326 closedir(procfs_dir_);
327 procfs_dir_ = NULL;
328 }
329 }
331 const ProcessEntry* NamedProcessIterator::NextProcessEntry() {
332 bool result = false;
333 do {
334 result = CheckForNextProcess();
335 } while (result && !IncludeEntry());
337 if (result)
338 return &entry_;
340 return NULL;
341 }
343 bool NamedProcessIterator::CheckForNextProcess() {
344 // TODO(port): skip processes owned by different UID
346 dirent* slot = 0;
347 const char* openparen;
348 const char* closeparen;
350 // Arbitrarily guess that there will never be more than 200 non-process
351 // files in /proc. Hardy has 53.
352 int skipped = 0;
353 const int kSkipLimit = 200;
354 while (skipped < kSkipLimit) {
355 slot = readdir(procfs_dir_);
356 // all done looking through /proc?
357 if (!slot)
358 return false;
360 // If not a process, keep looking for one.
361 bool notprocess = false;
362 int i;
363 for (i = 0; i < NAME_MAX && slot->d_name[i]; ++i) {
364 if (!isdigit(slot->d_name[i])) {
365 notprocess = true;
366 break;
367 }
368 }
369 if (i == NAME_MAX || notprocess) {
370 skipped++;
371 continue;
372 }
374 // Read the process's status.
375 char buf[NAME_MAX + 12];
376 sprintf(buf, "/proc/%s/stat", slot->d_name);
377 FILE *fp = fopen(buf, "r");
378 if (!fp)
379 return false;
380 const char* result = fgets(buf, sizeof(buf), fp);
381 fclose(fp);
382 if (!result)
383 return false;
385 // Parse the status. It is formatted like this:
386 // %d (%s) %c %d ...
387 // pid (name) runstate ppid
388 // To avoid being fooled by names containing a closing paren, scan
389 // backwards.
390 openparen = strchr(buf, '(');
391 closeparen = strrchr(buf, ')');
392 if (!openparen || !closeparen)
393 return false;
394 char runstate = closeparen[2];
396 // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped?
397 // Allowed values: D R S T Z
398 if (runstate != 'Z')
399 break;
401 // Nope, it's a zombie; somebody isn't cleaning up after their children.
402 // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.)
403 // There could be a lot of zombies, can't really decrement i here.
404 }
405 if (skipped >= kSkipLimit) {
406 NOTREACHED();
407 return false;
408 }
410 entry_.pid = atoi(slot->d_name);
411 entry_.ppid = atoi(closeparen + 3);
413 // TODO(port): read pid's commandline's $0, like killall does. Using the
414 // short name between openparen and closeparen won't work for long names!
415 int len = closeparen - openparen - 1;
416 if (len > NAME_MAX)
417 len = NAME_MAX;
418 memcpy(entry_.szExeFile, openparen + 1, len);
419 entry_.szExeFile[len] = 0;
421 return true;
422 }
424 bool NamedProcessIterator::IncludeEntry() {
425 // TODO(port): make this also work for non-ASCII filenames
426 if (WideToASCII(executable_name_) != entry_.szExeFile)
427 return false;
428 if (!filter_)
429 return true;
430 return filter_->Includes(entry_.pid, entry_.ppid);
431 }
433 } // namespace base