Beispiel #1
0
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;
}
Beispiel #2
0
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;
}
Beispiel #3
0
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;
}
Beispiel #4
0
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;
}
Beispiel #5
0
TypedValue HHVM_FUNCTION(dummy_arraylike_builtin, const Variant& var) {
  if (var.isArray()) {
    auto const& arr = var.asCArrRef();
    return tvReturn(arr);
  }
  return tvReturn(staticEmptyKeysetArray());
}
Beispiel #6
0
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();
                    	}

                }
Beispiel #8
0
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;
}
Beispiel #9
0
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;
}
Beispiel #10
0
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;
  }
}
Beispiel #11
0
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;
}
Beispiel #12
0
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;
}
Beispiel #13
0
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;
}
Beispiel #14
0
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
}