static Exception* generate_request_timeout_exception(c_WaitableWaitHandle* wh) { auto exceptionMsg = folly::sformat( !RuntimeOption::ServerExecutionMode() || is_cli_mode() ? "Maximum execution time of {} seconds exceeded" : "entire web request took longer than {} seconds and timed out", RID().getTimeout()); auto exceptionStack = createBacktrace(BacktraceArgs() .fromWaitHandle(wh) .withSelf() .withThis() .withMetadata()); return new RequestTimeoutException(exceptionMsg, exceptionStack); }
int64_t HHVM_FUNCTION(pcntl_fork) { if (is_cli_mode()) { raise_error("forking not available via server CLI execution"); return -1; } if (RuntimeOption::ServerExecutionMode()) { raise_error("forking is disallowed in server mode"); return -1; } if (Repo::prefork()) { raise_error("forking is disallowed in multi-threaded mode"); return -1; } std::cout.flush(); std::cerr.flush(); pid_t pid = fork(); Repo::postfork(pid); return pid; }
req::ptr<File> HttpStreamWrapper::open(const String& filename, const String& mode, int options, const req::ptr<StreamContext>& context) { if (RuntimeOption::ServerHttpSafeMode && !is_cli_mode()) { return nullptr; } if (strncmp(filename.data(), "http://", sizeof("http://") - 1) && strncmp(filename.data(), "https://", sizeof("https://") - 1)) { return nullptr; } Array headers; String method = s_GET; String post_data = null_string; String proxy_host; String proxy_user; String proxy_pass; int proxy_port = -1; int max_redirs = 20; int timeout = -1; bool ignore_errors = false; if (context && !context->getOptions().isNull() && !context->getOptions()[s_http].isNull()) { Array opts = context->getOptions()[s_http].toArray(); if (opts.exists(s_method)) { method = opts[s_method].toString(); } if (opts.exists(s_header)) { Array lines; if (opts[s_header].isString()) { lines = StringUtil::Explode( opts[s_header].toString(), "\r\n").toArray(); } else if (opts[s_header].isArray()) { lines = opts[s_header]; } for (ArrayIter it(lines); it; ++it) { Array parts = StringUtil::Explode( it.second().toString(), ":", 2).toArray(); headers.set(parts.rvalAt(0), parts.rvalAt(1)); } } if (opts.exists(s_user_agent) && !headers.exists(s_User_Agent)) { headers.set(s_User_Agent, opts[s_user_agent]); } if (opts.exists(s_max_redirects)) { max_redirs = opts[s_max_redirects].toInt64(); } if (opts.exists(s_timeout)) { timeout = opts[s_timeout].toInt64(); } if (opts.exists(s_ignore_errors)) { ignore_errors = opts[s_ignore_errors].toBoolean(); } if (opts.exists(s_proxy)) { Variant host = f_parse_url(opts[s_proxy].toString(), k_PHP_URL_HOST); Variant port = f_parse_url(opts[s_proxy].toString(), k_PHP_URL_PORT); if (!same(host, false) && !same(port, false)) { proxy_host = host.toString(); proxy_port = port.toInt64(); Variant user = f_parse_url(opts[s_proxy].toString(), k_PHP_URL_USER); Variant pass = f_parse_url(opts[s_proxy].toString(), k_PHP_URL_PASS); if (!same(user, false) && !same(pass, false)) { proxy_user = user.toString(); proxy_pass = pass.toString(); } } } post_data = opts[s_content].toString(); } if (!headers.exists(s_User_Agent)) { auto default_user_agent = ThreadInfo::s_threadInfo.getNoCheck() ->m_reqInjectionData.getUserAgent(); if (!default_user_agent.empty()) { headers.set(s_User_Agent, default_user_agent); } } auto file = req::make<UrlFile>(method.data(), headers, post_data, max_redirs, timeout, ignore_errors); file->setStreamContext(context); file->setProxy(proxy_host, proxy_port, proxy_user, proxy_pass); bool ret = file->open(filename, mode); if (!ret) { raise_warning("Failed to open %s (%s)", filename.data(), file->getLastError().c_str()); return nullptr; } return file; }
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 }