pid_t do_proc_open_helper(int afdt_fd) { std::string cmd, cwd; std::vector<std::string> env; std::vector<int> pvals; lwp_read(afdt_fd, cmd, cwd, env, pvals); std::vector<int> pkeys; for (int i = 0; i < pvals.size(); i++) { int fd = recv_fd(afdt_fd); if (fd < 0) { lwp_write(afdt_fd, "error", (int32_t)EPROTO); close_fds(pkeys); return -1; } pkeys.push_back(fd); } // indicate error if an empty command was received if (cmd.length() == 0) { lwp_write(afdt_fd, "error", (int32_t)ENOENT); return -1; } // now ready to start the child process pid_t child = fork(); if (child == 0) { mprotect_1g_pages(PROT_READ); Process::OOMScoreAdj(1000); for (int i = 0; i < pvals.size(); i++) { dup2(pkeys[i], pvals[i]); } if (!cwd.empty() && chdir(cwd.c_str())) { // non-zero for error // chdir failed, the working directory remains unchanged } if (!env.empty()) { char **envp = build_envp(env); execle("/bin/sh", "sh", "-c", cmd.c_str(), nullptr, envp); free(envp); } else { execl("/bin/sh", "sh", "-c", cmd.c_str(), nullptr); } _Exit(HPHP_EXIT_FAILURE); } if (child > 0) { // successfully created the child process lwp_write(afdt_fd, "success", child); } else { // failed creating the child process lwp_write(afdt_fd, "error", errno); } close_fds(pkeys); return child; }
void HHVM_FUNCTION(pcntl_exec, const String& path, const Array& args /* = null_array */, const Array& envs /* = null_array */) { if (RuntimeOption::WhitelistExec && !check_cmd(path.data())) { return; } if (Repo::prefork()) { raise_error("execing is disallowed in multi-threaded mode"); return; } // build argumnent list std::vector<String> sargs; // holding those char * int size = args.size(); char **argv = (char **)malloc((size + 2) * sizeof(char *)); *argv = (char *)path.data(); int i = 1; if (size) { sargs.reserve(size); for (ArrayIter iter(args); iter; ++iter, ++i) { String arg = iter.second().toString(); sargs.push_back(arg); *(argv + i) = (char *)arg.data(); } } *(argv + i) = NULL; // build environment pair list std::vector<String> senvs; // holding those char * char **envp = build_envp(envs, senvs); if (execve(path.c_str(), argv, envp) == -1) { raise_warning("Error has occurred: (errno %d) %s", errno, folly::errnoStr(errno).c_str()); } free(envp); free(argv); }
static void do_proc_open(FILE *fin, FILE *fout, int afdt_fd) { char cmd[BUFFER_SIZE]; read_buf(fin, cmd); if (strlen(cmd) == 0) { fprintf(fout, "error\n%d\n", ENOENT); fflush(fout); return; } char cwd[BUFFER_SIZE]; read_buf(fin, cwd); char buf[BUFFER_SIZE]; int env_size = 0; std::vector<std::string> env; read_buf(fin, buf); sscanf(buf, "%d", &env_size); for (int i = 0; i < env_size; i++) { read_buf(fin, buf); env.push_back(buf); } int pipe_size = 0; read_buf(fin, buf); sscanf(buf, "%d", &pipe_size); std::vector<int> pvals; for (int i = 0; i < pipe_size; i++) { int fd_value; read_buf(fin, buf); sscanf(buf, "%d", &fd_value); pvals.push_back(fd_value); } std::vector<int> pkeys; for (int i = 0; i < pipe_size; i++) { int fd = recv_fd(afdt_fd); if (fd < 0) { fprintf(fout, "error\n%d\n", EPROTO); fflush(fout); close_fds(pkeys); return; } pkeys.push_back(fd); } // now ready to start the child process pid_t child = fork(); if (child == 0) { for (int i = 0; i < pipe_size; i++) { dup2(pkeys[i], pvals[i]); } if (strlen(cwd) > 0 && chdir(cwd)) { // non-zero for error // chdir failed, the working directory remains unchanged } if (!env.empty()) { char **envp = build_envp(env); execle("/bin/sh", "sh", "-c", cmd, nullptr, envp); free(envp); } else { execl("/bin/sh", "sh", "-c", cmd, nullptr); } _exit(127); } else if (child > 0) { // successfully created the child process fprintf(fout, "%" PRId64 "\n", (int64_t)child); fflush(fout); } else { // failed creating the child process fprintf(fout, "error\n%d\n", errno); fflush(fout); } close_fds(pkeys); }
static void do_proc_open(int afdt_fd) { std::string cmd; lwp_read(afdt_fd, cmd); std::string cwd; lwp_read(afdt_fd, cwd); std::string buf; int env_size = 0; std::vector<std::string> env; lwp_read_int32(afdt_fd, env_size); for (int i = 0; i < env_size; i++) { lwp_read(afdt_fd, buf); env.push_back(buf); } int pipe_size = 0; lwp_read_int32(afdt_fd, pipe_size); std::vector<int> pvals; for (int i = 0; i < pipe_size; i++) { int fd_value; lwp_read_int32(afdt_fd, fd_value); pvals.push_back(fd_value); } std::vector<int> pkeys; for (int i = 0; i < pipe_size; i++) { int fd = recv_fd(afdt_fd); if (fd < 0) { lwp_write(afdt_fd, "error"); lwp_write_int32(afdt_fd, EPROTO); close_fds(pkeys); return; } pkeys.push_back(fd); } // indicate error if an empty command was received if (cmd.length() == 0) { lwp_write(afdt_fd, "error"); lwp_write_int32(afdt_fd, ENOENT); return; } // now ready to start the child process pid_t child = fork(); if (child == 0) { for (int i = 0; i < pipe_size; i++) { dup2(pkeys[i], pvals[i]); } if (!cwd.empty() && chdir(cwd.c_str())) { // non-zero for error // chdir failed, the working directory remains unchanged } if (!env.empty()) { char **envp = build_envp(env); execle("/bin/sh", "sh", "-c", cmd.c_str(), nullptr, envp); free(envp); } else { execl("/bin/sh", "sh", "-c", cmd.c_str(), nullptr); } _Exit(HPHP_EXIT_FAILURE); } else if (child > 0) { // successfully created the child process lwp_write(afdt_fd, "success"); lwp_write_int64(afdt_fd, (int64_t)child); } else { // failed creating the child process lwp_write(afdt_fd, "error"); lwp_write_int32(afdt_fd, errno); } close_fds(pkeys); }
Variant HHVM_FUNCTION(proc_open, const String& cmd, const Array& descriptorspec, VRefParam pipesParam, const String& cwd /* = null_string */, const Variant& env /* = null_variant */, const Variant& other_options /* = null_variant */) { if (RuntimeOption::WhitelistExec && !check_cmd(cmd.data())) { return false; } if (cmd.size() != strlen(cmd.c_str())) { raise_warning("NULL byte detected. Possible attack"); return false; } Variant pipes(pipesParam, Variant::WithRefBind{}); std::vector<DescriptorItem> items; std::string scwd = ""; if (!cwd.empty()) { scwd = cwd.c_str(); } else if (!g_context->getCwd().empty()) { scwd = g_context->getCwd().c_str(); } Array enva; if (env.isNull()) { // Build out an environment that conceptually matches what we'd // see if we were to iterate the environment and call getenv() // for each name. // Env vars defined in the hdf file go in first for (const auto& envvar : RuntimeOption::EnvVariables) { enva.set(String(envvar.first), String(envvar.second)); } // global environment overrides the hdf for (char **env = environ; env && *env; env++) { char *p = strchr(*env, '='); if (p) { String name(*env, p - *env, CopyString); String val(p + 1, CopyString); enva.set(name, val); } } // and then any putenv() changes take precedence for (ArrayIter iter(g_context->getEnvs()); iter; ++iter) { enva.set(iter.first(), iter.second()); } } else { enva = env.toArray(); } pid_t child; if (LightProcess::Available()) { // light process available // there is no need to do any locking, because the forking is delegated // to the light process if (!pre_proc_open(descriptorspec, items)) return false; const int item_size = items.size(); std::vector<int> created; created.reserve(item_size); std::vector<int> intended; intended.reserve(item_size); for (int i = 0; i < item_size; i++) { const auto& item = items[i]; created.push_back(item.childend); intended.push_back(item.index); } std::vector<std::string> envs; for (ArrayIter iter(enva); iter; ++iter) { StringBuffer nvpair; nvpair.append(iter.first().toString()); nvpair.append('='); nvpair.append(iter.second().toString()); std::string tmp = nvpair.detach().c_str(); if (tmp.find('\n') == std::string::npos) { envs.push_back(tmp); } } child = LightProcess::proc_open(cmd.c_str(), created, intended, scwd.c_str(), envs); assert(child); return post_proc_open(cmd, pipes, enva, items, child); } else { /* the unix way */ Lock lock(DescriptorItem::s_mutex); if (!pre_proc_open(descriptorspec, items)) return false; child = fork(); if (child) { // the parent process return post_proc_open(cmd, pipes, enva, items, child); } } assert(child == 0); /* this is the child process */ /* close those descriptors that we just opened for the parent stuff, * dup new descriptors into required descriptors and close the original * cruft */ for (auto& item : items) { item.dupChild(); } if (scwd.length() > 0 && chdir(scwd.c_str())) { // chdir failed, the working directory remains unchanged } std::vector<String> senvs; // holding those char * char **envp = build_envp(enva, senvs); execle("/bin/sh", "sh", "-c", cmd.data(), NULL, envp); free(envp); _exit(127); }
Variant HHVM_FUNCTION(proc_open, const String& cmd, const Array& descriptorspec, VRefParam pipesParam, const Variant& cwd /* = uninit_variant */, const Variant& env /* = uninit_variant */, const Variant& other_options /* = uninit_variant */) { if (RuntimeOption::WhitelistExec && !check_cmd(cmd.data())) { return false; } if (cmd.size() != strlen(cmd.c_str())) { raise_warning("NULL byte detected. Possible attack"); return false; } Variant pipes(pipesParam, Variant::WithRefBind{}); std::vector<DescriptorItem> items; std::string scwd = ""; if (!cwd.isNull() && cwd.isString() && !cwd.asCStrRef().empty()) { scwd = cwd.asCStrRef().c_str(); } else if (!g_context->getCwd().empty()) { scwd = g_context->getCwd().c_str(); } Array enva; if (env.isNull()) { if (is_cli_mode()) { enva = cli_env(); } else { // Build out an environment that conceptually matches what we'd // see if we were to iterate the environment and call getenv() // for each name. // Env vars defined in the hdf file go in first for (const auto& envvar : RuntimeOption::EnvVariables) { enva.set(String(envvar.first), String(envvar.second)); } // global environment overrides the hdf for (char **env = environ; env && *env; env++) { char *p = strchr(*env, '='); if (p) { String name(*env, p - *env, CopyString); String val(p + 1, CopyString); enva.set(name, val); } } } // and then any putenv() changes take precedence for (ArrayIter iter(g_context->getEnvs()); iter; ++iter) { enva.set(iter.first(), iter.second()); } } else { enva = env.toArray(); } #ifdef _WIN32 PROCESS_INFORMATION pi; HANDLE childHandle; STARTUPINFO si; BOOL newprocok; SECURITY_ATTRIBUTES security; DWORD dwCreateFlags = 0; char *command_with_cmd; UINT old_error_mode; char cur_cwd[MAXPATHLEN]; bool suppress_errors = false; bool bypass_shell = false; if (!other_options.isNull() && other_options.isArray()) { auto arr = other_options.asCArrRef(); if (arr.exists(String("suppress_errors", CopyString), true)) { auto v = arr[String("suppress_errors", CopyString)]; if ((v.isBoolean() && v.asBooleanVal()) || (v.isInteger() && v.asInt64Val())) { suppress_errors = true; } } if (arr.exists(String("bypass_shell", CopyString), true)) { auto v = arr[String("bypass_shell", CopyString)]; if ((v.isBoolean() && v.asBooleanVal()) || (v.isInteger() && v.asInt64Val())) { bypass_shell = true; } } } /* we use this to allow the child to inherit handles */ memset(&security, 0, sizeof(security)); security.nLength = sizeof(security); security.bInheritHandle = true; security.lpSecurityDescriptor = nullptr; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); si.hStdError = GetStdHandle(STD_ERROR_HANDLE); if (!pre_proc_open(descriptorspec, items)) return false; /* redirect stdin/stdout/stderr if requested */ for (size_t i = 0; i < items.size(); i++) { switch (items[i].index) { case 0: si.hStdInput = items[i].childend; break; case 1: si.hStdOutput = items[i].childend; break; case 2: si.hStdError = items[i].childend; break; } } memset(&pi, 0, sizeof(pi)); if (suppress_errors) { old_error_mode = SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); } dwCreateFlags = NORMAL_PRIORITY_CLASS; if (!RuntimeOption::ServerExecutionMode()) { dwCreateFlags |= CREATE_NO_WINDOW; } char *envp = build_envp(enva); if (bypass_shell) { newprocok = CreateProcess( nullptr, strdup(cmd.c_str()), &security, &security, TRUE, dwCreateFlags, envp, scwd.c_str(), &si, &pi); } else { std::string command_with = "cmd.exe /c "; command_with += cmd.toCppString(); newprocok = CreateProcess( nullptr, strdup(command_with.c_str()), &security, &security, TRUE, dwCreateFlags, envp, scwd.c_str(), &si, &pi); } free(envp); if (suppress_errors) { SetErrorMode(old_error_mode); } if (newprocok == FALSE) { DWORD dw = GetLastError(); char* msg; FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg, 0, nullptr); /* clean up all the descriptors */ for (size_t i = 0; i < items.size(); i++) { CloseHandle(items[i].childend); if (items[i].parentend) { CloseHandle(items[i].parentend); } } raise_warning("CreateProcess failed, error code - %u: %s", dw, msg); LocalFree(msg); return false; } childHandle = pi.hProcess; DWORD child = pi.dwProcessId; CloseHandle(pi.hThread); return post_proc_open(cmd, pipes, enva, items, (pid_t)child, childHandle); #else pid_t child; if (LightProcess::Available()) { // light process available // there is no need to do any locking, because the forking is delegated // to the light process if (!pre_proc_open(descriptorspec, items)) return false; const int item_size = items.size(); std::vector<int> created; created.reserve(item_size); std::vector<int> intended; intended.reserve(item_size); for (int i = 0; i < item_size; i++) { const auto& item = items[i]; created.push_back(item.childend); intended.push_back(item.index); } std::vector<std::string> envs; for (ArrayIter iter(enva); iter; ++iter) { StringBuffer nvpair; nvpair.append(iter.first().toString()); nvpair.append('='); nvpair.append(iter.second().toString()); std::string tmp = nvpair.detach().c_str(); if (tmp.find('\n') == std::string::npos) { envs.push_back(tmp); } } child = LightProcess::proc_open(cmd.c_str(), created, intended, scwd.c_str(), envs); assert(child); return post_proc_open(cmd, pipes, enva, items, child); } else { /* the unix way */ Lock lock(DescriptorItem::s_mutex); if (!pre_proc_open(descriptorspec, items)) return false; child = fork(); if (child) { // the parent process return post_proc_open(cmd, pipes, enva, items, child); } } assert(child == 0); /* this is the child process */ /* close those descriptors that we just opened for the parent stuff, * dup new descriptors into required descriptors and close the original * cruft */ for (auto& item : items) { item.dupChild(); } if (scwd.length() > 0 && chdir(scwd.c_str())) { // chdir failed, the working directory remains unchanged } std::vector<String> senvs; // holding those char * char **envp = build_envp(enva, senvs); execle("/bin/sh", "sh", "-c", cmd.data(), nullptr, envp); free(envp); _exit(127); #endif }