Commit message: None
CVE: CVE-2017-5008
CWE: CWE-79
Vulnerability description: Blink in Google Chrome prior to 56.0.2924.76 for Linux, Windows and Mac, and 56.0.2924.87 for Android, allowed attacker controlled JavaScript to be run during the invocation of a private script method, which allowed a remote attacker to inject arbitrary scripts or HTML (UXSS) via a crafted HTML page.

Project: Chrome

static v8::Local<v8::Object> classObjectOfPrivateScript(
    ScriptState* scriptState,
    String className) {
  ASSERT(scriptState->perContextData());
  ASSERT(scriptState->getExecutionContext());
  v8::Isolate* isolate = scriptState->isolate();
  v8::Local<v8::Value> compiledClass =
      scriptState->perContextData()->compiledPrivateScript(className);
  if (compiledClass.IsEmpty()) {
    v8::Local<v8::Value> installedClasses =
        scriptState->perContextData()->compiledPrivateScript(
            "PrivateScriptRunner");
    if (installedClasses.IsEmpty()) {
      installedClasses = installPrivateScriptRunner(isolate);
      scriptState->perContextData()->setCompiledPrivateScript(
          "PrivateScriptRunner", installedClasses);
    }
    RELEASE_ASSERT(!installedClasses.IsEmpty());
    RELEASE_ASSERT(installedClasses->IsObject());

    installPrivateScript(isolate, className);
    compiledClass =
        v8::Local<v8::Object>::Cast(installedClasses)
            ->Get(scriptState->context(), v8String(isolate, className))
            .ToLocalChecked();
    RELEASE_ASSERT(compiledClass->IsObject());
    scriptState->perContextData()->setCompiledPrivateScript(className,
                                                            compiledClass);
  }
  return v8::Local<v8::Object>::Cast(compiledClass);
}

Project: Chrome

static void initializeHolderIfNeeded(ScriptState* scriptState,
                                     v8::Local<v8::Object> classObject,
                                     v8::Local<v8::Value> holder) {
  RELEASE_ASSERT(!holder.IsEmpty());
  RELEASE_ASSERT(holder->IsObject());
  v8::Local<v8::Object> holderObject = v8::Local<v8::Object>::Cast(holder);
  v8::Isolate* isolate = scriptState->isolate();
  v8::Local<v8::Context> context = scriptState->context();
  auto privateIsInitialized =
      V8PrivateProperty::getPrivateScriptRunnerIsInitialized(isolate);
  if (privateIsInitialized.hasValue(context, holderObject))
    return;  // Already initialized.

  v8::TryCatch block(isolate);
  v8::Local<v8::Value> initializeFunction;
  if (classObject->Get(scriptState->context(), v8String(isolate, "initialize"))
          .ToLocal(&initializeFunction) &&
      initializeFunction->IsFunction()) {
    v8::TryCatch block(isolate);
    v8::Local<v8::Value> result;
    if (!V8ScriptRunner::callInternalFunction(
             v8::Local<v8::Function>::Cast(initializeFunction), holder, 0, 0,
             isolate)
             .ToLocal(&result)) {
      dumpV8Message(context, block.Message());
      LOG(FATAL)
          << "Private script error: Object constructor threw an exception.";
    }
  }

  if (classObject->GetPrototype() != holderObject->GetPrototype()) {
    if (!v8CallBoolean(
            classObject->SetPrototype(context, holderObject->GetPrototype()))) {
      dumpV8Message(context, block.Message());
      LOG(FATAL) << "Private script error: SetPrototype failed.";
    }
  }
  if (!v8CallBoolean(holderObject->SetPrototype(context, classObject))) {
    dumpV8Message(context, block.Message());
    LOG(FATAL) << "Private script error: SetPrototype failed.";
  }

  privateIsInitialized.set(context, holderObject, v8Boolean(true, isolate));
}

Project: Chrome

static void installPrivateScript(v8::Isolate* isolate, String className) {
  ScriptState* scriptState = ScriptState::current(isolate);
  int compiledScriptCount = 0;
#ifndef NDEBUG
  for (size_t index = 0;
       index < WTF_ARRAY_LENGTH(kPrivateScriptSourcesForTesting); index++) {
    if (className == kPrivateScriptSourcesForTesting[index].className) {
      compileAndRunPrivateScript(
          scriptState, kPrivateScriptSourcesForTesting[index].scriptClassName,
          kPrivateScriptSourcesForTesting[index].source,
          kPrivateScriptSourcesForTesting[index].size);
      compiledScriptCount++;
    }
  }
#endif

  for (size_t index = 0; index < WTF_ARRAY_LENGTH(kPrivateScriptSources);
       index++) {
    if (className == kPrivateScriptSources[index].className) {
      String resourceData =
          loadResourceAsASCIIString(kPrivateScriptSources[index].resourceFile);
      compileAndRunPrivateScript(
          scriptState, kPrivateScriptSources[index].scriptClassName,
          resourceData.utf8().data(), resourceData.length());
      compiledScriptCount++;
    }
  }

  if (!compiledScriptCount) {
    LOG(FATAL)
        << "Private script error: Target source code was not found. (Class "
           "name = "
        << className.utf8().data() << ")";
  }
}

Project: Chrome

static v8::Local<v8::Value> installPrivateScriptRunner(v8::Isolate* isolate) {
  const String className = "PrivateScriptRunner";
  size_t index;
  for (index = 0; index < WTF_ARRAY_LENGTH(kPrivateScriptSources); index++) {
    if (className == kPrivateScriptSources[index].className)
      break;
  }
  if (index == WTF_ARRAY_LENGTH(kPrivateScriptSources)) {
    LOG(FATAL)
        << "Private script error: Target source code was not found. (Class "
           "name = "
        << className.utf8().data() << ")";
  }
  String resourceData =
      loadResourceAsASCIIString(kPrivateScriptSources[index].resourceFile);
  return compileAndRunPrivateScript(ScriptState::current(isolate), className,
                                    resourceData.utf8().data(),
                                    resourceData.length());
}

Project: Chrome

v8::Local<v8::Value> PrivateScriptRunner::runDOMAttributeGetter(
    ScriptState* scriptState,
    ScriptState* scriptStateInUserScript,
    const char* className,
    const char* attributeName,
    v8::Local<v8::Value> holder) {
  v8::Isolate* isolate = scriptState->isolate();
  v8::Local<v8::Object> classObject =
      classObjectOfPrivateScript(scriptState, className);
  v8::Local<v8::Value> descriptor;
  if (!classObject
           ->GetOwnPropertyDescriptor(scriptState->context(),
                                      v8String(isolate, attributeName))
           .ToLocal(&descriptor) ||
      !descriptor->IsObject()) {
    LOG(FATAL)
        << "Private script error: Target DOM attribute getter was not found. "
           "(Class name = "
        << className << ", Attribute name = " << attributeName << ")";
  }
  v8::Local<v8::Value> getter;
  if (!v8::Local<v8::Object>::Cast(descriptor)
           ->Get(scriptState->context(), v8String(isolate, "get"))
           .ToLocal(&getter) ||
      !getter->IsFunction()) {
    LOG(FATAL)
        << "Private script error: Target DOM attribute getter was not found. "
           "(Class name = "
        << className << ", Attribute name = " << attributeName << ")";
  }
  initializeHolderIfNeeded(scriptState, classObject, holder);
  v8::TryCatch block(isolate);
  v8::Local<v8::Value> result;
  if (!V8ScriptRunner::callInternalFunction(
           v8::Local<v8::Function>::Cast(getter), holder, 0, 0, isolate)
           .ToLocal(&result)) {
    rethrowExceptionInPrivateScript(isolate, block, scriptStateInUserScript,
                                    ExceptionState::GetterContext,
                                    attributeName, className);
    block.ReThrow();
    return v8::Local<v8::Value>();
  }
  return result;
}

Project: Chrome

bool PrivateScriptRunner::runDOMAttributeSetter(
    ScriptState* scriptState,
    ScriptState* scriptStateInUserScript,
    const char* className,
    const char* attributeName,
    v8::Local<v8::Value> holder,
    v8::Local<v8::Value> v8Value) {
  v8::Isolate* isolate = scriptState->isolate();
  v8::Local<v8::Object> classObject =
      classObjectOfPrivateScript(scriptState, className);
  v8::Local<v8::Value> descriptor;
  if (!classObject
           ->GetOwnPropertyDescriptor(scriptState->context(),
                                      v8String(isolate, attributeName))
           .ToLocal(&descriptor) ||
      !descriptor->IsObject()) {
    LOG(FATAL)
        << "Private script error: Target DOM attribute setter was not found. "
           "(Class name = "
        << className << ", Attribute name = " << attributeName << ")";
  }
  v8::Local<v8::Value> setter;
  if (!v8::Local<v8::Object>::Cast(descriptor)
           ->Get(scriptState->context(), v8String(isolate, "set"))
           .ToLocal(&setter) ||
      !setter->IsFunction()) {
    LOG(FATAL) << "Private script error: Target DOM attribute setter was not "
                  "found. (Class name = "
               << className << ", Attribute name = " << attributeName << ")";
  }
  initializeHolderIfNeeded(scriptState, classObject, holder);
  v8::Local<v8::Value> argv[] = {v8Value};
  v8::TryCatch block(isolate);
  v8::Local<v8::Value> result;
  if (!V8ScriptRunner::callInternalFunction(
           v8::Local<v8::Function>::Cast(setter), holder,
           WTF_ARRAY_LENGTH(argv), argv, isolate)
           .ToLocal(&result)) {
    rethrowExceptionInPrivateScript(isolate, block, scriptStateInUserScript,
                                    ExceptionState::SetterContext,
                                    attributeName, className);
    block.ReThrow();
    return false;
  }
  return true;
}

Project: Chrome

v8::Local<v8::Value> PrivateScriptRunner::runDOMMethod(
    ScriptState* scriptState,
    ScriptState* scriptStateInUserScript,
    const char* className,
    const char* methodName,
    v8::Local<v8::Value> holder,
    int argc,
    v8::Local<v8::Value> argv[]) {
  v8::Local<v8::Object> classObject =
      classObjectOfPrivateScript(scriptState, className);
  v8::Local<v8::Value> method;
  if (!classObject
           ->Get(scriptState->context(),
                 v8String(scriptState->isolate(), methodName))
           .ToLocal(&method) ||
      !method->IsFunction()) {
    LOG(FATAL)
        << "Private script error: Target DOM method was not found. (Class "
           "name = "
        << className << ", Method name = " << methodName << ")";
  }
  initializeHolderIfNeeded(scriptState, classObject, holder);
  v8::TryCatch block(scriptState->isolate());
  v8::Local<v8::Value> result;
  if (!V8ScriptRunner::callInternalFunction(
           v8::Local<v8::Function>::Cast(method), holder, argc, argv,
           scriptState->isolate())
           .ToLocal(&result)) {
    rethrowExceptionInPrivateScript(
        scriptState->isolate(), block, scriptStateInUserScript,
        ExceptionState::ExecutionContext, methodName, className);
    block.ReThrow();
    return v8::Local<v8::Value>();
  }
  return result;
}