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