bool EventHook::RunInterceptHandler(ActRec* ar) { const Func* func = ar->m_func; if (LIKELY(func->maybeIntercepted() == 0)) return true; // Intercept only original generator / async function calls, not resumption. if (ar->inGenerator()) return true; Variant *h = get_intercept_handler(func->fullNameRef(), &func->maybeIntercepted()); if (!h) return true; JIT::VMRegAnchor _; PC savePc = g_context->m_pc; Variant doneFlag = true; Variant called_on; if (ar->hasThis()) { called_on = Variant(ar->getThis()); } else if (ar->hasClass()) { // For static methods, give handler the name of called class called_on = Variant(const_cast<StringData*>(ar->getClass()->name())); } Variant intArgs = PackedArrayInit(5) .append(ar->m_func->fullNameRef()) .append(called_on) .append(get_frame_args_with_ref(ar)) .append(h->asCArrRef()[1]) .appendRef(doneFlag) .toArray(); Variant ret = vm_call_user_func(h->asCArrRef()[0], intArgs); if (doneFlag.toBoolean()) { Offset pcOff; ActRec* outer = g_context->getPrevVMState(ar, &pcOff); frame_free_locals_inl_no_hook<true>(ar, ar->m_func->numLocals()); Stack& stack = g_context->getStack(); stack.top() = (Cell*)(ar + 1); cellDup(*ret.asCell(), *stack.allocTV()); g_context->m_fp = outer; g_context->m_pc = outer ? outer->m_func->unit()->at(pcOff) : nullptr; return false; } g_context->m_fp = ar; g_context->m_pc = savePc; return true; }
bool EventHook::RunInterceptHandler(ActRec* ar) { const Func* func = ar->m_func; if (LIKELY(func->maybeIntercepted() == 0)) return true; Variant *h = get_intercept_handler(func->fullNameRef(), &func->maybeIntercepted()); if (!h) return true; Transl::VMRegAnchor _; PC savePc = g_vmContext->m_pc; Variant doneFlag = true; Variant called_on; if (ar->hasThis()) { called_on = Variant(ar->getThis()); } else if (ar->hasClass()) { // For static methods, give handler the name of called class called_on = Variant(const_cast<StringData*>(ar->getClass()->name())); } Array intArgs = CREATE_VECTOR5(ar->m_func->fullNameRef(), called_on, get_frame_args_with_ref(ar), h->asCArrRef()[1], ref(doneFlag)); Variant ret = vm_call_user_func(h->asCArrRef()[0], intArgs); if (doneFlag.toBoolean()) { Offset pcOff; ActRec* outer = g_vmContext->getPrevVMState(ar, &pcOff); assert(outer); frame_free_locals_inl_no_hook<true>(ar, ar->m_func->numLocals()); Stack& stack = g_vmContext->getStack(); stack.top() = (Cell*)(ar + 1); tvDup(ret.asTypedValue(), stack.allocTV()); g_vmContext->m_fp = outer; g_vmContext->m_pc = outer->m_func->unit()->at(pcOff); return false; } g_vmContext->m_fp = ar; g_vmContext->m_pc = savePc; return true; }
bool EventHook::RunInterceptHandler(ActRec* ar) { const Func* func = ar->m_func; if (LIKELY(func->maybeIntercepted() == 0)) return true; Variant *h = get_intercept_handler(func->fullNameRef(), &func->maybeIntercepted()); if (!h) return true; Transl::VMRegAnchor _; PC savePc = g_vmContext->m_pc; Offset pcOff; ActRec* outer = g_vmContext->getPrevVMState(ar, &pcOff); assert(outer); g_vmContext->m_fp = outer; g_vmContext->m_pc = outer->m_func->unit()->at(pcOff); try { Variant doneFlag = true; Variant obj = ar->hasThis() ? Variant(Object(ar->getThis())) : uninit_null(); Array intArgs = CREATE_VECTOR5(ar->m_func->fullNameRef(), obj, get_frame_args_with_ref(ar), h->asCArrRef()[1], ref(doneFlag)); Variant ret = vm_call_user_func(h->asCArrRef()[0], intArgs); if (doneFlag.toBoolean()) { frame_free_locals_inl_no_hook(ar, ar->m_func->numLocals()); Stack& stack = g_vmContext->getStack(); stack.top() = (Cell*)(ar + 1); tvDup(ret.asTypedValue(), stack.allocTV()); return false; } g_vmContext->m_fp = ar; g_vmContext->m_pc = savePc; } catch (...) { g_vmContext->m_fp = ar; g_vmContext->m_pc = savePc; g_vmContext->m_stack.top() = Stack::frameStackBase(ar); throw; } return true; }
static Variant HHVM_FUNCTION(mysql_fetch_object, const Variant& var_result, const String& class_name /* = "stdClass" */, const Variant& params /* = null */) { Resource result = var_result.isResource() ? var_result.toResource() : null_resource; Variant properties = php_mysql_fetch_hash(result, PHP_MYSQL_ASSOC); if (!same(properties, false)) { Object obj; const auto paramsArray = params.isArray() ? params.asCArrRef() : Array(); // We need to create an object without initialization (constructor call), // and set the fetched fields as dynamic properties on the object prior // calling the constructor. obj = create_object_only(class_name); // Set the fields. obj->o_setArray(properties.toArray()); // And finally initialize the object by calling the constructor. obj = init_object(class_name, paramsArray, obj.get()); return obj; } return false; }
TypedValue HHVM_FUNCTION(dummy_arraylike_builtin, const Variant& var) { if (var.isArray()) { auto const& arr = var.asCArrRef(); return tvReturn(arr); } return tvReturn(staticEmptyKeysetArray()); }
TypedValue HHVM_FUNCTION(dummy_varr_or_darr_builtin, const Variant& var) { if (var.isArray()) { auto const& arr = var.asCArrRef(); if (arr.isVecOrVArray() || arr.isDictOrDArray()) return tvReturn(arr); } return tvReturn(staticEmptyVArray()); }
// The default operator called from parallel_for void operator() (const tbb::blocked_range<int64> &range) const { callerContext.EnteringOperator(); // Build arguments array Array args = Array::Create(); // Pass the range start and end as parameters args.append(range.begin()); args.append(range.end()); // If an input array is defined, copy it and pass as a parameter if(inputArrayOfVariant.size() > 0) { Array inputPHPArray = Array::Create(); for(size_t ai=0; ai<inputArrayOfVariant.size(); ai++) inputPHPArray.append(inputArrayOfVariant[ai]); Variant inputArrayArg(inputPHPArray.getArrayData()); args.append(inputArrayArg); } // Call user supplied callback with arguments // This is a PHP function of the form worker($begin, $end, $array), returning an array or nothing // If an array is returned, it is expected to have elements which will be comingled into the output // array in the elements defined by the input range. Variant vres = f_call_user_func_array(this->callback, args); callerContext.ExitingOperator(); // Call this straight after the callback completes // Return if no result to pass back if(vres.isNull() || !vres.isArray()) return; // Now we take the output array [0..N) and assign it into the overall output array at [begin..begin+N) // Extract output array from result variant const Array aOutputArray = vres.asCArrRef(); ArrayData *pdOutputArray = aOutputArray.getArrayData(); Variant v = pdOutputArray->reset(); // Check the output array is the same size or smaller than the range passed size_t outIdx = range.begin(); if(pdOutputArray->size() > (range.end()-range.begin())) { raise_warning("Callback function returned array larger than passed range size"); return; } // Copy each row while(!v.isBoolean() || v.toBoolean()) { // printf(" outIdx=%d, v=%s\n", (int)outIdx, v.toString().c_str()); ( *pOutputArrayOfVariant ) [outIdx++] = v; v = pdOutputArray->next(); } }
static int php_count_recursive(CArrRef array) { long cnt = array.size(); for (ArrayIter iter(array); iter; ++iter) { Variant value = iter.second(); if (value.isArray()) { CArrRef arr_value = value.asCArrRef(); cnt += php_count_recursive(arr_value); } } return cnt; }
Variant f_array_merge(int _argc, CVarRef array1, CArrRef _argv /* = null_array */) { getCheckedArray(array1); Array ret = Array::Create(); php_array_merge(ret, arr_array1); for (ArrayIter iter(_argv); iter; ++iter) { Variant v = iter.second(); if (!v.isArray()) { throw_bad_array_exception(); return uninit_null(); } CArrRef arr_v = v.asCArrRef(); php_array_merge(ret, arr_v); } return ret; }
void RepoQuery::getTypedValue(int iCol, TypedValue& tv) { const void* blob; size_t size; getBlob(iCol, blob, size); tvWriteUninit(&tv); if (size > 0) { String s = String((const char*)blob, size, CopyString); Variant v = unserialize_from_string(s); if (v.isString()) { v = String(StringData::GetStaticString(v.asCStrRef().get())); } else if (v.isArray()) { v = Array(ArrayData::GetScalarArray(v.asCArrRef().get())); } else { // Serialized variants and objects shouldn't ever make it into the repo. assert(!IS_REFCOUNTED_TYPE(v.getType())); } tvAsVariant(&tv) = v; } }
Variant f_array_merge_recursive(int _argc, CVarRef array1, CArrRef _argv /* = null_array */) { getCheckedArray(array1); Array ret = Array::Create(); PointerSet seen; php_array_merge_recursive(seen, false, ret, arr_array1); assert(seen.empty()); for (ArrayIter iter(_argv); iter; ++iter) { Variant v = iter.second(); if (!v.isArray()) { throw_bad_array_exception(); return uninit_null(); } CArrRef arr_v = v.asCArrRef(); php_array_merge_recursive(seen, false, ret, arr_v); assert(seen.empty()); } return ret; }
static Variant HHVM_FUNCTION(mysql_fetch_object, const Variant& var_result, const String& class_name /* = "stdClass" */, const Variant& params /* = null */) { Resource result = var_result.isResource() ? var_result.toResource() : null_resource; Variant properties = php_mysql_fetch_hash(result, PHP_MYSQL_ASSOC); if (!same(properties, false)) { Object obj; if (params.isArray()) { obj = create_object(class_name, params.asCArrRef()); } else { obj = create_object(class_name, Array()); } obj->o_setArray(properties.toArray()); return obj; } return false; }
bool EventHook::RunInterceptHandler(ActRec* ar) { const Func* func = ar->func(); if (LIKELY(func->maybeIntercepted() == 0)) return true; // Intercept only original generator / async function calls, not resumption. if (ar->resumed()) return true; Variant* h = get_intercept_handler(func->fullNameStr(), &func->maybeIntercepted()); if (!h) return true; /* * In production mode, only functions that we have assumed can be * intercepted during static analysis should actually be * intercepted. */ if (RuntimeOption::RepoAuthoritative && !RuntimeOption::EvalJitEnableRenameFunction) { if (!(func->attrs() & AttrInterceptable)) { raise_error("fb_intercept was used on a non-interceptable function (%s) " "in RepoAuthoritative mode", func->fullName()->data()); } } VMRegAnchor _; PC savePc = vmpc(); Variant doneFlag = true; Variant called_on; if (ar->hasThis()) { called_on = Variant(ar->getThis()); } else if (ar->hasClass()) { // For static methods, give handler the name of called class called_on = Variant(const_cast<StringData*>(ar->getClass()->name())); } Variant intArgs = PackedArrayInit(5) .append(VarNR(ar->func()->fullName())) .append(called_on) .append(get_frame_args_with_ref(ar)) .append(h->asCArrRef()[1]) .appendRef(doneFlag) .toArray(); Variant ret = vm_call_user_func(h->asCArrRef()[0], intArgs); if (doneFlag.toBoolean()) { Offset pcOff; ActRec* outer = g_context->getPrevVMState(ar, &pcOff); frame_free_locals_inl_no_hook<true>(ar, ar->func()->numLocals()); Stack& stack = vmStack(); stack.top() = (Cell*)(ar + 1); cellDup(*ret.asCell(), *stack.allocTV()); vmfp() = outer; vmpc() = outer ? outer->func()->unit()->at(pcOff) : nullptr; return false; } vmfp() = ar; vmpc() = savePc; return true; }
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 }