Commit message: None
CVE: CVE-2016-1672
CWE: CWE-284
Vulnerability description: The ModuleSystem::RequireForJsInner function in extensions/renderer/module_system.cc in the extension bindings in Google Chrome before 51.0.2704.63 mishandles properties, which allows remote attackers to conduct bindings-interception attacks and bypass the Same Origin Policy via unspecified vectors.

Project: Chrome

v8::Local<v8::Value> ModuleSystem::CallModuleMethod(
    const std::string& module_name,
    const std::string& method_name,
    std::vector<v8::Local<v8::Value>>* args) {
  return CallModuleMethod(module_name, method_name, args->size(), args->data());
}

Project: Chrome

void ModuleSystem::ClobberExistingNativeHandler(const std::string& name) {
  NativeHandlerMap::iterator existing_handler = native_handler_map_.find(name);
  if (existing_handler != native_handler_map_.end()) {
    clobbered_native_handlers_.push_back(std::move(existing_handler->second));
    native_handler_map_.erase(existing_handler);
  }
}

Project: Chrome

void ModuleSystem::Invalidate() {
  {
    v8::HandleScope scope(GetIsolate());
    v8::Local<v8::Object> global = context()->v8_context()->Global();
    DeletePrivate(global, kModulesField);
    DeletePrivate(global, kModuleSystem);
  }

  for (const auto& handler : native_handler_map_)
    handler.second->Invalidate();
  for (const auto& clobbered_handler : clobbered_native_handlers_)
    clobbered_handler->Invalidate();

  ObjectBackedNativeHandler::Invalidate();
}

Project: Chrome

void ModuleSystem::LazyFieldGetterInner(
    v8::Local<v8::String> property,
    const v8::PropertyCallbackInfo<v8::Value>& info,
    RequireFunction require_function) {
  CHECK(!info.Data().IsEmpty());
  CHECK(info.Data()->IsObject());
  v8::Isolate* isolate = info.GetIsolate();
  v8::HandleScope handle_scope(isolate);
  v8::Local<v8::Object> parameters = v8::Local<v8::Object>::Cast(info.Data());
  v8::Local<v8::Context> context = parameters->CreationContext();
  v8::Local<v8::Object> global(context->Global());
  v8::Local<v8::Value> module_system_value;
  if (!GetPrivate(context, global, kModuleSystem, &module_system_value) ||
      !module_system_value->IsExternal()) {
    Warn(isolate,
         "Module system has been deleted, does extension view exist?");
    return;
  }

  ModuleSystem* module_system = static_cast<ModuleSystem*>(
      v8::Local<v8::External>::Cast(module_system_value)->Value());

  v8::Local<v8::Value> v8_module_name;
  if (!GetProperty(context, parameters, kModuleName, &v8_module_name)) {
    Warn(isolate, "Cannot find module.");
    return;
  }
  std::string name = *v8::String::Utf8Value(v8_module_name);

  v8::Context::Scope context_scope(context);
  NativesEnabledScope natives_enabled_scope(module_system);

  v8::TryCatch try_catch(isolate);
  v8::Local<v8::Value> module_value;
  if (!(module_system->*require_function)(name).ToLocal(&module_value)) {
    module_system->HandleException(try_catch);
    return;
  }

  v8::Local<v8::Object> module = v8::Local<v8::Object>::Cast(module_value);
  v8::Local<v8::Value> field_value;
  if (!GetProperty(context, parameters, kModuleField, &field_value)) {
    module_system->HandleException(try_catch);
    return;
  }
  v8::Local<v8::String> field;
  if (!field_value->ToString(context).ToLocal(&field)) {
    module_system->HandleException(try_catch);
    return;
  }

  if (!IsTrue(module->Has(context, field))) {
    std::string field_str = *v8::String::Utf8Value(field);
    Fatal(module_system->context_,
          "Lazy require of " + name + "." + field_str + " did not set the " +
              field_str + " field");
    return;
  }

  v8::Local<v8::Value> new_field;
  if (!GetProperty(context, module, field, &new_field)) {
    module_system->HandleException(try_catch);
    return;
  }

  CHECK(!new_field.IsEmpty());

  v8::Local<v8::Value> val = info.This();
  if (val->IsObject()) {
    v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(val);
    object->Delete(context, property);
    SetProperty(context, object, property, new_field);
  } else {
    NOTREACHED();
  }
  info.GetReturnValue().Set(new_field);
}

Project: Chrome

v8::Local<v8::Value> ModuleSystem::LoadModule(const std::string& module_name) {
  v8::EscapableHandleScope handle_scope(GetIsolate());
  v8::Local<v8::Context> v8_context = context()->v8_context();
  v8::Context::Scope context_scope(v8_context);

  v8::Local<v8::Value> source(GetSource(module_name));
  if (source.IsEmpty() || source->IsUndefined()) {
    Fatal(context_, "No source for require(" + module_name + ")");
    return v8::Undefined(GetIsolate());
  }
  v8::Local<v8::String> wrapped_source(
      WrapSource(v8::Local<v8::String>::Cast(source)));
  v8::Local<v8::String> v8_module_name;
  if (!ToV8String(GetIsolate(), module_name.c_str(), &v8_module_name)) {
    NOTREACHED() << "module_name is too long";
    return v8::Undefined(GetIsolate());
  }
  v8::Local<v8::Value> func_as_value =
      RunString(wrapped_source, v8_module_name);
  if (func_as_value.IsEmpty() || func_as_value->IsUndefined()) {
    Fatal(context_, "Bad source for require(" + module_name + ")");
    return v8::Undefined(GetIsolate());
  }

  v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(func_as_value);

  v8::Local<v8::Object> define_object = v8::Object::New(GetIsolate());
  gin::ModuleRegistry::InstallGlobals(GetIsolate(), define_object);

  v8::Local<v8::Object> exports = v8::Object::New(GetIsolate());

  v8::Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(
      GetIsolate(),
      &SetExportsProperty);
  v8::Local<v8::String> v8_key;
  if (!v8_helpers::ToV8String(GetIsolate(), "$set", &v8_key)) {
    NOTREACHED();
    return v8::Undefined(GetIsolate());
  }

  v8::Local<v8::Function> function;
  if (!tmpl->GetFunction(v8_context).ToLocal(&function)) {
    NOTREACHED();
    return v8::Undefined(GetIsolate());
  }

  exports->DefineOwnProperty(v8_context, v8_key, function, v8::ReadOnly)
      .FromJust();

  v8::Local<v8::Object> natives(NewInstance());
  CHECK(!natives.IsEmpty());  // this can fail if v8 has issues

  v8::Local<v8::Value> args[] = {
      GetPropertyUnsafe(v8_context, define_object, "define"),
      GetPropertyUnsafe(v8_context, natives, "require",
                        v8::NewStringType::kInternalized),
      GetPropertyUnsafe(v8_context, natives, "requireNative",
                        v8::NewStringType::kInternalized),
      GetPropertyUnsafe(v8_context, natives, "requireAsync",
                        v8::NewStringType::kInternalized),
      exports,
      console::AsV8Object(GetIsolate()),
      GetPropertyUnsafe(v8_context, natives, "privates",
                        v8::NewStringType::kInternalized),
      context_->safe_builtins()->GetArray(),
      context_->safe_builtins()->GetFunction(),
      context_->safe_builtins()->GetJSON(),
      context_->safe_builtins()->GetObjekt(),
      context_->safe_builtins()->GetRegExp(),
      context_->safe_builtins()->GetString(),
      context_->safe_builtins()->GetError(),
  };
  {
    v8::TryCatch try_catch(GetIsolate());
    try_catch.SetCaptureMessage(true);
    context_->CallFunction(func, arraysize(args), args);
    if (try_catch.HasCaught()) {
      HandleException(try_catch);
      return v8::Undefined(GetIsolate());
    }
  }
  return handle_scope.Escape(exports);
}

Project: Chrome

void ModuleSystem::Private(const v8::FunctionCallbackInfo<v8::Value>& args) {
  CHECK_EQ(1, args.Length());
  if (!args[0]->IsObject() || args[0]->IsNull()) {
    GetIsolate()->ThrowException(
        v8::Exception::TypeError(ToV8StringUnsafe(GetIsolate(),
            args[0]->IsUndefined()
                ? "Method called without a valid receiver (this). "
                  "Did you forget to call .bind()?"
                : "Invalid invocation: receiver is not an object!")));
    return;
  }
  v8::Local<v8::Object> obj = args[0].As<v8::Object>();
  v8::Local<v8::Value> privates;
  if (!GetPrivate(obj, "privates", &privates) || !privates->IsObject()) {
    privates = v8::Object::New(args.GetIsolate());
    if (privates.IsEmpty()) {
      GetIsolate()->ThrowException(
          ToV8StringUnsafe(GetIsolate(), "Failed to create privates"));
      return;
    }
    SetPrivate(obj, "privates", privates);
  }
  args.GetReturnValue().Set(privates);
}

Project: Chrome

void ModuleSystem::RegisterNativeHandler(
    const std::string& name,
    scoped_ptr<NativeHandler> native_handler) {
  ClobberExistingNativeHandler(name);
  native_handler_map_[name] = std::move(native_handler);
}

Project: Chrome

void SetExportsProperty(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Local<v8::Object> obj = args.This();
  CHECK_EQ(2, args.Length());
  CHECK(args[0]->IsString());
  v8::Maybe<bool> result =
      obj->DefineOwnProperty(args.GetIsolate()->GetCurrentContext(),
                             args[0]->ToString(), args[1], v8::ReadOnly);
  if (!result.FromMaybe(false))
    LOG(ERROR) << "Failed to set private property on the export.";
}