1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/chromium/src/base/process_util_linux.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,433 @@ 1.4 +// Copyright (c) 2008 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#include "base/process_util.h" 1.9 + 1.10 +#include <ctype.h> 1.11 +#include <dirent.h> 1.12 +#include <fcntl.h> 1.13 +#include <memory> 1.14 +#include <unistd.h> 1.15 +#include <string> 1.16 +#include <sys/types.h> 1.17 +#include <sys/wait.h> 1.18 + 1.19 +#include "base/debug_util.h" 1.20 +#include "base/eintr_wrapper.h" 1.21 +#include "base/file_util.h" 1.22 +#include "base/logging.h" 1.23 +#include "base/string_tokenizer.h" 1.24 +#include "base/string_util.h" 1.25 + 1.26 +#ifdef MOZ_WIDGET_GONK 1.27 +/* 1.28 + * AID_APP is the first application UID used by Android. We're using 1.29 + * it as our unprivilegied UID. This ensure the UID used is not 1.30 + * shared with any other processes than our own childs. 1.31 + */ 1.32 +# include <private/android_filesystem_config.h> 1.33 +# define CHILD_UNPRIVILEGED_UID AID_APP 1.34 +# define CHILD_UNPRIVILEGED_GID AID_APP 1.35 +#else 1.36 +/* 1.37 + * On platforms that are not gonk based, we fall back to an arbitrary 1.38 + * UID. This is generally the UID for user `nobody', albeit it is not 1.39 + * always the case. 1.40 + */ 1.41 +# define CHILD_UNPRIVILEGED_UID 65534 1.42 +# define CHILD_UNPRIVILEGED_GID 65534 1.43 +#endif 1.44 + 1.45 +#ifdef ANDROID 1.46 +#include <pthread.h> 1.47 +/* 1.48 + * Currently, PR_DuplicateEnvironment is implemented in 1.49 + * mozglue/build/BionicGlue.cpp 1.50 + */ 1.51 +#define HAVE_PR_DUPLICATE_ENVIRONMENT 1.52 + 1.53 +#include "plstr.h" 1.54 +#include "prenv.h" 1.55 +#include "prmem.h" 1.56 +/* Temporary until we have PR_DuplicateEnvironment in prenv.h */ 1.57 +extern "C" { NSPR_API(pthread_mutex_t *)PR_GetEnvLock(void); } 1.58 +#endif 1.59 + 1.60 +namespace { 1.61 + 1.62 +enum ParsingState { 1.63 + KEY_NAME, 1.64 + KEY_VALUE 1.65 +}; 1.66 + 1.67 +static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); 1.68 + 1.69 +} // namespace 1.70 + 1.71 +namespace base { 1.72 + 1.73 +#ifdef HAVE_PR_DUPLICATE_ENVIRONMENT 1.74 +/* 1.75 + * I tried to put PR_DuplicateEnvironment down in mozglue, but on android 1.76 + * this winds up failing because the strdup/free calls wind up mismatching. 1.77 + */ 1.78 + 1.79 +static char ** 1.80 +PR_DuplicateEnvironment(void) 1.81 +{ 1.82 + char **result = NULL; 1.83 + char **s; 1.84 + char **d; 1.85 + pthread_mutex_lock(PR_GetEnvLock()); 1.86 + for (s = environ; *s != NULL; s++) 1.87 + ; 1.88 + if ((result = (char **)PR_Malloc(sizeof(char *) * (s - environ + 1))) != NULL) { 1.89 + for (s = environ, d = result; *s != NULL; s++, d++) { 1.90 + *d = PL_strdup(*s); 1.91 + } 1.92 + *d = NULL; 1.93 + } 1.94 + pthread_mutex_unlock(PR_GetEnvLock()); 1.95 + return result; 1.96 +} 1.97 + 1.98 +class EnvironmentEnvp 1.99 +{ 1.100 +public: 1.101 + EnvironmentEnvp() 1.102 + : mEnvp(PR_DuplicateEnvironment()) {} 1.103 + 1.104 + EnvironmentEnvp(const environment_map &em) 1.105 + { 1.106 + mEnvp = (char **)PR_Malloc(sizeof(char *) * (em.size() + 1)); 1.107 + if (!mEnvp) { 1.108 + return; 1.109 + } 1.110 + char **e = mEnvp; 1.111 + for (environment_map::const_iterator it = em.begin(); 1.112 + it != em.end(); ++it, ++e) { 1.113 + std::string str = it->first; 1.114 + str += "="; 1.115 + str += it->second; 1.116 + *e = PL_strdup(str.c_str()); 1.117 + } 1.118 + *e = NULL; 1.119 + } 1.120 + 1.121 + ~EnvironmentEnvp() 1.122 + { 1.123 + if (!mEnvp) { 1.124 + return; 1.125 + } 1.126 + for (char **e = mEnvp; *e; ++e) { 1.127 + PL_strfree(*e); 1.128 + } 1.129 + PR_Free(mEnvp); 1.130 + } 1.131 + 1.132 + char * const *AsEnvp() { return mEnvp; } 1.133 + 1.134 + void ToMap(environment_map &em) 1.135 + { 1.136 + if (!mEnvp) { 1.137 + return; 1.138 + } 1.139 + em.clear(); 1.140 + for (char **e = mEnvp; *e; ++e) { 1.141 + const char *eq; 1.142 + if ((eq = strchr(*e, '=')) != NULL) { 1.143 + std::string varname(*e, eq - *e); 1.144 + em[varname.c_str()] = &eq[1]; 1.145 + } 1.146 + } 1.147 + } 1.148 + 1.149 +private: 1.150 + char **mEnvp; 1.151 +}; 1.152 + 1.153 +class Environment : public environment_map 1.154 +{ 1.155 +public: 1.156 + Environment() 1.157 + { 1.158 + EnvironmentEnvp envp; 1.159 + envp.ToMap(*this); 1.160 + } 1.161 + 1.162 + char * const *AsEnvp() { 1.163 + mEnvp.reset(new EnvironmentEnvp(*this)); 1.164 + return mEnvp->AsEnvp(); 1.165 + } 1.166 + 1.167 + void Merge(const environment_map &em) 1.168 + { 1.169 + for (const_iterator it = em.begin(); it != em.end(); ++it) { 1.170 + (*this)[it->first] = it->second; 1.171 + } 1.172 + } 1.173 +private: 1.174 + std::auto_ptr<EnvironmentEnvp> mEnvp; 1.175 +}; 1.176 +#endif // HAVE_PR_DUPLICATE_ENVIRONMENT 1.177 + 1.178 +bool LaunchApp(const std::vector<std::string>& argv, 1.179 + const file_handle_mapping_vector& fds_to_remap, 1.180 + bool wait, ProcessHandle* process_handle) { 1.181 + return LaunchApp(argv, fds_to_remap, environment_map(), 1.182 + wait, process_handle); 1.183 +} 1.184 + 1.185 +bool LaunchApp(const std::vector<std::string>& argv, 1.186 + const file_handle_mapping_vector& fds_to_remap, 1.187 + const environment_map& env_vars_to_set, 1.188 + bool wait, ProcessHandle* process_handle, 1.189 + ProcessArchitecture arch) { 1.190 + return LaunchApp(argv, fds_to_remap, env_vars_to_set, 1.191 + PRIVILEGES_INHERIT, 1.192 + wait, process_handle); 1.193 +} 1.194 + 1.195 +bool LaunchApp(const std::vector<std::string>& argv, 1.196 + const file_handle_mapping_vector& fds_to_remap, 1.197 + const environment_map& env_vars_to_set, 1.198 + ChildPrivileges privs, 1.199 + bool wait, ProcessHandle* process_handle, 1.200 + ProcessArchitecture arch) { 1.201 + scoped_array<char*> argv_cstr(new char*[argv.size() + 1]); 1.202 + // Illegal to allocate memory after fork and before execvp 1.203 + InjectiveMultimap fd_shuffle1, fd_shuffle2; 1.204 + fd_shuffle1.reserve(fds_to_remap.size()); 1.205 + fd_shuffle2.reserve(fds_to_remap.size()); 1.206 + 1.207 +#ifdef HAVE_PR_DUPLICATE_ENVIRONMENT 1.208 + Environment env; 1.209 + env.Merge(env_vars_to_set); 1.210 + char * const *envp = env.AsEnvp(); 1.211 + if (!envp) { 1.212 + DLOG(ERROR) << "FAILED to duplicate environment for: " << argv_cstr[0]; 1.213 + return false; 1.214 + } 1.215 +#endif 1.216 + 1.217 + pid_t pid = fork(); 1.218 + if (pid < 0) 1.219 + return false; 1.220 + 1.221 + if (pid == 0) { 1.222 + for (file_handle_mapping_vector::const_iterator 1.223 + it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) { 1.224 + fd_shuffle1.push_back(InjectionArc(it->first, it->second, false)); 1.225 + fd_shuffle2.push_back(InjectionArc(it->first, it->second, false)); 1.226 + } 1.227 + 1.228 + if (!ShuffleFileDescriptors(&fd_shuffle1)) 1.229 + _exit(127); 1.230 + 1.231 + CloseSuperfluousFds(fd_shuffle2); 1.232 + 1.233 + for (size_t i = 0; i < argv.size(); i++) 1.234 + argv_cstr[i] = const_cast<char*>(argv[i].c_str()); 1.235 + argv_cstr[argv.size()] = NULL; 1.236 + 1.237 + SetCurrentProcessPrivileges(privs); 1.238 + 1.239 +#ifdef HAVE_PR_DUPLICATE_ENVIRONMENT 1.240 + execve(argv_cstr[0], argv_cstr.get(), envp); 1.241 +#else 1.242 + for (environment_map::const_iterator it = env_vars_to_set.begin(); 1.243 + it != env_vars_to_set.end(); ++it) { 1.244 + if (setenv(it->first.c_str(), it->second.c_str(), 1/*overwrite*/)) 1.245 + _exit(127); 1.246 + } 1.247 + execv(argv_cstr[0], argv_cstr.get()); 1.248 +#endif 1.249 + // if we get here, we're in serious trouble and should complain loudly 1.250 + DLOG(ERROR) << "FAILED TO exec() CHILD PROCESS, path: " << argv_cstr[0]; 1.251 + _exit(127); 1.252 + } else { 1.253 + gProcessLog.print("==> process %d launched child process %d\n", 1.254 + GetCurrentProcId(), pid); 1.255 + if (wait) 1.256 + HANDLE_EINTR(waitpid(pid, 0, 0)); 1.257 + 1.258 + if (process_handle) 1.259 + *process_handle = pid; 1.260 + } 1.261 + 1.262 + return true; 1.263 +} 1.264 + 1.265 +bool LaunchApp(const CommandLine& cl, 1.266 + bool wait, bool start_hidden, 1.267 + ProcessHandle* process_handle) { 1.268 + file_handle_mapping_vector no_files; 1.269 + return LaunchApp(cl.argv(), no_files, wait, process_handle); 1.270 +} 1.271 + 1.272 +void SetCurrentProcessPrivileges(ChildPrivileges privs) { 1.273 + if (privs == PRIVILEGES_INHERIT) { 1.274 + return; 1.275 + } 1.276 + 1.277 + gid_t gid = CHILD_UNPRIVILEGED_GID; 1.278 + uid_t uid = CHILD_UNPRIVILEGED_UID; 1.279 +#ifdef MOZ_WIDGET_GONK 1.280 + { 1.281 + static bool checked_pix_max, pix_max_ok; 1.282 + if (!checked_pix_max) { 1.283 + checked_pix_max = true; 1.284 + int fd = open("/proc/sys/kernel/pid_max", O_CLOEXEC | O_RDONLY); 1.285 + if (fd < 0) { 1.286 + DLOG(ERROR) << "Failed to open pid_max"; 1.287 + _exit(127); 1.288 + } 1.289 + char buf[PATH_MAX]; 1.290 + ssize_t len = read(fd, buf, sizeof(buf) - 1); 1.291 + close(fd); 1.292 + if (len < 0) { 1.293 + DLOG(ERROR) << "Failed to read pid_max"; 1.294 + _exit(127); 1.295 + } 1.296 + buf[len] = '\0'; 1.297 + int pid_max = atoi(buf); 1.298 + pix_max_ok = 1.299 + (pid_max + CHILD_UNPRIVILEGED_UID > CHILD_UNPRIVILEGED_UID); 1.300 + } 1.301 + if (!pix_max_ok) { 1.302 + DLOG(ERROR) << "Can't safely get unique uid/gid"; 1.303 + _exit(127); 1.304 + } 1.305 + gid += getpid(); 1.306 + uid += getpid(); 1.307 + } 1.308 +#endif 1.309 + if (setgid(gid) != 0) { 1.310 + DLOG(ERROR) << "FAILED TO setgid() CHILD PROCESS"; 1.311 + _exit(127); 1.312 + } 1.313 + if (setuid(uid) != 0) { 1.314 + DLOG(ERROR) << "FAILED TO setuid() CHILD PROCESS"; 1.315 + _exit(127); 1.316 + } 1.317 + if (chdir("/") != 0) 1.318 + gProcessLog.print("==> could not chdir()\n"); 1.319 +} 1.320 + 1.321 +NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name, 1.322 + const ProcessFilter* filter) 1.323 + : executable_name_(executable_name), filter_(filter) { 1.324 + procfs_dir_ = opendir("/proc"); 1.325 +} 1.326 + 1.327 +NamedProcessIterator::~NamedProcessIterator() { 1.328 + if (procfs_dir_) { 1.329 + closedir(procfs_dir_); 1.330 + procfs_dir_ = NULL; 1.331 + } 1.332 +} 1.333 + 1.334 +const ProcessEntry* NamedProcessIterator::NextProcessEntry() { 1.335 + bool result = false; 1.336 + do { 1.337 + result = CheckForNextProcess(); 1.338 + } while (result && !IncludeEntry()); 1.339 + 1.340 + if (result) 1.341 + return &entry_; 1.342 + 1.343 + return NULL; 1.344 +} 1.345 + 1.346 +bool NamedProcessIterator::CheckForNextProcess() { 1.347 + // TODO(port): skip processes owned by different UID 1.348 + 1.349 + dirent* slot = 0; 1.350 + const char* openparen; 1.351 + const char* closeparen; 1.352 + 1.353 + // Arbitrarily guess that there will never be more than 200 non-process 1.354 + // files in /proc. Hardy has 53. 1.355 + int skipped = 0; 1.356 + const int kSkipLimit = 200; 1.357 + while (skipped < kSkipLimit) { 1.358 + slot = readdir(procfs_dir_); 1.359 + // all done looking through /proc? 1.360 + if (!slot) 1.361 + return false; 1.362 + 1.363 + // If not a process, keep looking for one. 1.364 + bool notprocess = false; 1.365 + int i; 1.366 + for (i = 0; i < NAME_MAX && slot->d_name[i]; ++i) { 1.367 + if (!isdigit(slot->d_name[i])) { 1.368 + notprocess = true; 1.369 + break; 1.370 + } 1.371 + } 1.372 + if (i == NAME_MAX || notprocess) { 1.373 + skipped++; 1.374 + continue; 1.375 + } 1.376 + 1.377 + // Read the process's status. 1.378 + char buf[NAME_MAX + 12]; 1.379 + sprintf(buf, "/proc/%s/stat", slot->d_name); 1.380 + FILE *fp = fopen(buf, "r"); 1.381 + if (!fp) 1.382 + return false; 1.383 + const char* result = fgets(buf, sizeof(buf), fp); 1.384 + fclose(fp); 1.385 + if (!result) 1.386 + return false; 1.387 + 1.388 + // Parse the status. It is formatted like this: 1.389 + // %d (%s) %c %d ... 1.390 + // pid (name) runstate ppid 1.391 + // To avoid being fooled by names containing a closing paren, scan 1.392 + // backwards. 1.393 + openparen = strchr(buf, '('); 1.394 + closeparen = strrchr(buf, ')'); 1.395 + if (!openparen || !closeparen) 1.396 + return false; 1.397 + char runstate = closeparen[2]; 1.398 + 1.399 + // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped? 1.400 + // Allowed values: D R S T Z 1.401 + if (runstate != 'Z') 1.402 + break; 1.403 + 1.404 + // Nope, it's a zombie; somebody isn't cleaning up after their children. 1.405 + // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.) 1.406 + // There could be a lot of zombies, can't really decrement i here. 1.407 + } 1.408 + if (skipped >= kSkipLimit) { 1.409 + NOTREACHED(); 1.410 + return false; 1.411 + } 1.412 + 1.413 + entry_.pid = atoi(slot->d_name); 1.414 + entry_.ppid = atoi(closeparen + 3); 1.415 + 1.416 + // TODO(port): read pid's commandline's $0, like killall does. Using the 1.417 + // short name between openparen and closeparen won't work for long names! 1.418 + int len = closeparen - openparen - 1; 1.419 + if (len > NAME_MAX) 1.420 + len = NAME_MAX; 1.421 + memcpy(entry_.szExeFile, openparen + 1, len); 1.422 + entry_.szExeFile[len] = 0; 1.423 + 1.424 + return true; 1.425 +} 1.426 + 1.427 +bool NamedProcessIterator::IncludeEntry() { 1.428 + // TODO(port): make this also work for non-ASCII filenames 1.429 + if (WideToASCII(executable_name_) != entry_.szExeFile) 1.430 + return false; 1.431 + if (!filter_) 1.432 + return true; 1.433 + return filter_->Includes(entry_.pid, entry_.ppid); 1.434 +} 1.435 + 1.436 +} // namespace base