[Extensions] Don't allow gin::Define to be overridden
Use DefineOwnProperty instead of Set in for gin, including gin::Define.
Replace Set in v8_helpers as well, to avoid the same problem.
Also update callsites from JS to CHECK expected arguments, rather
than DCHECK (since receiving unexpected arguments likely means
executing untrusted code).
BUG=549986
Review URL: https://codereview.chromium.org/1433293004
Cr-Commit-Position: refs/heads/master@{#359460}
diff --git a/chrome/browser/extensions/extension_bindings_apitest.cc b/chrome/browser/extensions/extension_bindings_apitest.cc
index 81e9b92..d99e574 100644
--- a/chrome/browser/extensions/extension_bindings_apitest.cc
+++ b/chrome/browser/extensions/extension_bindings_apitest.cc
@@ -138,6 +138,32 @@
EXPECT_EQ("success", result);
}
+IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, NoGinDefineOverriding) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+
+ // We need to create runtime bindings in the web page. An extension that's
+ // externally connectable will do that for us.
+ ASSERT_TRUE(LoadExtension(
+ test_data_dir_.AppendASCII("bindings")
+ .AppendASCII("externally_connectable_everywhere")));
+
+ ui_test_utils::NavigateToURL(
+ browser(),
+ embedded_test_server()->GetURL(
+ "/extensions/api_test/bindings/override_gin_define.html"));
+ ASSERT_FALSE(
+ browser()->tab_strip_model()->GetActiveWebContents()->IsCrashed());
+
+ // See chrome/test/data/extensions/api_test/bindings/override_gin_define.html.
+ std::string result;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractString(
+ browser()->tab_strip_model()->GetActiveWebContents(),
+ "window.domAutomationController.send("
+ "document.getElementById('status').textContent.trim());",
+ &result));
+ EXPECT_EQ("success", result);
+}
+
IN_PROC_BROWSER_TEST_F(ExtensionBindingsApiTest, HandlerFunctionTypeChecking) {
ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
ui_test_utils::NavigateToURL(
diff --git a/chrome/test/data/extensions/api_test/bindings/override_gin_define.html b/chrome/test/data/extensions/api_test/bindings/override_gin_define.html
new file mode 100644
index 0000000..3aaf8eeb
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/bindings/override_gin_define.html
@@ -0,0 +1,69 @@
+<!doctype html>
+<html>
+<body>
+<span id="status"></span>
+</body>
+<script>
+var error = '';
+var addError = function(newError) {
+ error += newError;
+ document.getElementById('status').textContent = error;
+};
+
+var succeed = function() {
+ if (error != '')
+ return; // Don't overwrite an existing error.
+ document.getElementById('status').textContent = 'success';
+}
+
+// Repro from crbug.com/549986.
+Object.prototype.__defineSetter__('define', function(v) {
+ if (typeof v == 'function') {
+ addError('Leaked gin define');
+ leakedDefine = v;
+ }
+ Object.defineProperty(this, 'define', {value: v});
+});
+
+var leakedBinding;
+Object.defineProperty(Object.prototype, 'create', {set: function(v) {
+ if (typeof(v) == 'function') {
+ Object.defineProperty(this, 'create', {value: function(name) {
+ result = v(name);
+ if (name == 'runtime') {
+ try {
+ leakedDefine('foo', ['test'], function(){} );
+ } catch (e) { }
+ } else if (name == 'test') {
+ addError('Leaked test');
+ leakedBinding = result;
+ }
+ return result;
+ }, configurable: true});
+ }
+}});
+
+// Bindings are lazily initialized. Poke it.
+chrome.runtime;
+// If the runtime bindings aren't created, we didn't test anything.
+if (!chrome.runtime)
+ addError('chrome.runtime was not created.\n');
+
+if (leakedBinding) {
+ leakedFunctions = {};
+ leakedBinding.customHooks_[0](
+ {apiFunctions: {setHandleRequest: function(name, fun) {
+ leakedFunctions[name] = fun;
+ } }, compiledApi: {} });
+
+ leakedFunctions.runWithNativesEnabled(function() {
+ addError('Calling activityLogger.LogEvent');
+ leakedFunctions.getModuleSystem(window).requireNative('activityLogger')
+ .LogEvent('', '', 0xDEADBAD);
+ });
+}
+
+// All's well.
+succeed();
+</script>
+</html>
diff --git a/extensions/renderer/api_activity_logger.cc b/extensions/renderer/api_activity_logger.cc
index 826abb0c..f92ce53 100644
--- a/extensions/renderer/api_activity_logger.cc
+++ b/extensions/renderer/api_activity_logger.cc
@@ -38,10 +38,10 @@
void APIActivityLogger::LogInternal(
const CallType call_type,
const v8::FunctionCallbackInfo<v8::Value>& args) {
- DCHECK_GT(args.Length(), 2);
- DCHECK(args[0]->IsString());
- DCHECK(args[1]->IsString());
- DCHECK(args[2]->IsArray());
+ CHECK_GT(args.Length(), 2);
+ CHECK(args[0]->IsString());
+ CHECK(args[1]->IsString());
+ CHECK(args[2]->IsArray());
std::string ext_id = *v8::String::Utf8Value(args[0]);
ExtensionHostMsg_APIActionOrEvent_Params params;
diff --git a/extensions/renderer/blob_native_handler.cc b/extensions/renderer/blob_native_handler.cc
index d6e757a3..12068c3 100644
--- a/extensions/renderer/blob_native_handler.cc
+++ b/extensions/renderer/blob_native_handler.cc
@@ -14,7 +14,7 @@
// Expects a single Blob argument. Returns the Blob's UUID.
void GetBlobUuid(const v8::FunctionCallbackInfo<v8::Value>& args) {
- DCHECK_EQ(1, args.Length());
+ CHECK_EQ(1, args.Length());
blink::WebBlob blob = blink::WebBlob::fromV8Value(args[0]);
args.GetReturnValue().Set(
v8::String::NewFromUtf8(args.GetIsolate(), blob.uuid().utf8().data()));
@@ -38,10 +38,10 @@
// a separate flow to avoid leaking Blobs if the script context is destroyed.
void BlobNativeHandler::TakeBrowserProcessBlob(
const v8::FunctionCallbackInfo<v8::Value>& args) {
- DCHECK_EQ(3, args.Length());
- DCHECK(args[0]->IsString());
- DCHECK(args[1]->IsString());
- DCHECK(args[2]->IsInt32());
+ CHECK_EQ(3, args.Length());
+ CHECK(args[0]->IsString());
+ CHECK(args[1]->IsString());
+ CHECK(args[2]->IsInt32());
std::string uuid(*v8::String::Utf8Value(args[0]));
std::string type(*v8::String::Utf8Value(args[1]));
blink::WebBlob blob =
diff --git a/extensions/renderer/file_system_natives.cc b/extensions/renderer/file_system_natives.cc
index d3b23af..12745096 100644
--- a/extensions/renderer/file_system_natives.cc
+++ b/extensions/renderer/file_system_natives.cc
@@ -31,8 +31,8 @@
void FileSystemNatives::GetIsolatedFileSystem(
const v8::FunctionCallbackInfo<v8::Value>& args) {
- DCHECK(args.Length() == 1 || args.Length() == 2);
- DCHECK(args[0]->IsString());
+ CHECK(args.Length() == 1 || args.Length() == 2);
+ CHECK(args[0]->IsString());
std::string file_system_id(*v8::String::Utf8Value(args[0]));
blink::WebLocalFrame* webframe =
blink::WebLocalFrame::frameForContext(context()->v8_context());
@@ -49,7 +49,7 @@
// system at which to root the DOMFileSystem we're returning to the caller.
std::string optional_root_name;
if (args.Length() == 2) {
- DCHECK(args[1]->IsString());
+ CHECK(args[1]->IsString());
optional_root_name = *v8::String::Utf8Value(args[1]);
}
@@ -66,8 +66,8 @@
void FileSystemNatives::GetFileEntry(
const v8::FunctionCallbackInfo<v8::Value>& args) {
- DCHECK(args.Length() == 5);
- DCHECK(args[0]->IsString());
+ CHECK_EQ(5, args.Length());
+ CHECK(args[0]->IsString());
std::string type_string = *v8::String::Utf8Value(args[0]);
blink::WebFileSystemType type;
bool is_valid_type = storage::GetFileSystemPublicType(type_string, &type);
@@ -76,16 +76,16 @@
return;
}
- DCHECK(args[1]->IsString());
- DCHECK(args[2]->IsString());
- DCHECK(args[3]->IsString());
+ CHECK(args[1]->IsString());
+ CHECK(args[2]->IsString());
+ CHECK(args[3]->IsString());
std::string file_system_name(*v8::String::Utf8Value(args[1]));
GURL file_system_root_url(*v8::String::Utf8Value(args[2]));
std::string file_path_string(*v8::String::Utf8Value(args[3]));
base::FilePath file_path = base::FilePath::FromUTF8Unsafe(file_path_string);
DCHECK(storage::VirtualPath::IsAbsolute(file_path.value()));
- DCHECK(args[4]->IsBoolean());
+ CHECK(args[4]->IsBoolean());
blink::WebDOMFileSystem::EntryType entry_type =
args[4]->BooleanValue() ? blink::WebDOMFileSystem::EntryTypeDirectory
: blink::WebDOMFileSystem::EntryTypeFile;
diff --git a/extensions/renderer/module_system.cc b/extensions/renderer/module_system.cc
index 3e3da6a..e84f0e0 100644
--- a/extensions/renderer/module_system.cc
+++ b/extensions/renderer/module_system.cc
@@ -101,8 +101,8 @@
void SetExportsProperty(
const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Local<v8::Object> obj = args.This();
- DCHECK_EQ(2, args.Length());
- DCHECK(args[0]->IsString());
+ 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);
diff --git a/extensions/renderer/v8_helpers.h b/extensions/renderer/v8_helpers.h
index 2bfeee8..0c9a47c 100644
--- a/extensions/renderer/v8_helpers.h
+++ b/extensions/renderer/v8_helpers.h
@@ -7,6 +7,7 @@
#include <string.h>
+#include "base/strings/string_number_conversions.h"
#include "v8/include/v8.h"
namespace extensions {
@@ -56,19 +57,13 @@
return value.IsEmpty() || value->IsUndefined();
}
-// SetProperty() family wraps V8::Object::Set(). Returns true on success.
+// SetProperty() family wraps V8::Object::DefineOwnProperty().
+// Returns true on success.
inline bool SetProperty(v8::Local<v8::Context> context,
v8::Local<v8::Object> object,
- v8::Local<v8::Value> key,
+ v8::Local<v8::String> key,
v8::Local<v8::Value> value) {
- return IsTrue(object->Set(context, key, value));
-}
-
-inline bool SetProperty(v8::Local<v8::Context> context,
- v8::Local<v8::Object> object,
- uint32_t index,
- v8::Local<v8::Value> value) {
- return IsTrue(object->Set(context, index, value));
+ return IsTrue(object->DefineOwnProperty(context, key, value));
}
inline bool SetProperty(v8::Local<v8::Context> context,
@@ -81,6 +76,13 @@
return SetProperty(context, object, v8_key, value);
}
+inline bool SetProperty(v8::Local<v8::Context> context,
+ v8::Local<v8::Object> object,
+ uint32_t index,
+ v8::Local<v8::Value> value) {
+ return SetProperty(context, object, base::UintToString(index).c_str(), value);
+}
+
// GetProperty() family calls V8::Object::Get() and extracts a value from
// returned MaybeLocal. Returns true on success.
template <typename Key>
diff --git a/gin/converter.h b/gin/converter.h
index 8d17d41..12348e2 100644
--- a/gin/converter.h
+++ b/gin/converter.h
@@ -20,7 +20,8 @@
v8::Local<v8::Object> object,
KeyType key,
v8::Local<v8::Value> value) {
- auto maybe = object->Set(isolate->GetCurrentContext(), key, value);
+ auto maybe =
+ object->DefineOwnProperty(isolate->GetCurrentContext(), key, value);
return !maybe.IsNothing() && maybe.FromJust();
}