Pipe a script's CORS status through V8 during compilation.

In order to properly sanitize exception data during a 'window.onerror'
handler, we need to know whether a script was served with proper CORS
headers at the time it was loaded into V8. This patch adds a single bool
to ScriptOrigin, and pipes that through the compiler to land on the
Script object. We can then retrieve the parameter when calling the
embedder's exception callback.

BUG=crbug.com/159566
R=mstarzinger@chromium.org

Review URL: https://codereview.chromium.org/20646006

Patch from Mike West <mkwst@chromium.org>.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15963 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
mstarzinger@chromium.org 2013-07-30 17:05:50 +00:00
parent 8a019050ff
commit 96fc677d25
12 changed files with 186 additions and 7 deletions

View File

@ -942,17 +942,21 @@ class ScriptOrigin {
V8_INLINE(ScriptOrigin(
Handle<Value> resource_name,
Handle<Integer> resource_line_offset = Handle<Integer>(),
Handle<Integer> resource_column_offset = Handle<Integer>()))
Handle<Integer> resource_column_offset = Handle<Integer>(),
Handle<Boolean> resource_is_shared_cross_origin = Handle<Boolean>()))
: resource_name_(resource_name),
resource_line_offset_(resource_line_offset),
resource_column_offset_(resource_column_offset) { }
resource_column_offset_(resource_column_offset),
resource_is_shared_cross_origin_(resource_is_shared_cross_origin) { }
V8_INLINE(Handle<Value> ResourceName() const);
V8_INLINE(Handle<Integer> ResourceLineOffset() const);
V8_INLINE(Handle<Integer> ResourceColumnOffset() const);
V8_INLINE(Handle<Boolean> ResourceIsSharedCrossOrigin() const);
private:
Handle<Value> resource_name_;
Handle<Integer> resource_line_offset_;
Handle<Integer> resource_column_offset_;
Handle<Boolean> resource_is_shared_cross_origin_;
};
@ -1130,6 +1134,12 @@ class V8EXPORT Message {
*/
int GetEndColumn() const;
/**
* Passes on the value set by the embedder when it fed the script from which
* this Message was generated to V8.
*/
bool IsSharedCrossOrigin() const;
// TODO(1245381): Print to a string instead of on a FILE.
static void PrintCurrentStackTrace(FILE* out);
@ -5996,6 +6006,10 @@ Handle<Integer> ScriptOrigin::ResourceColumnOffset() const {
return resource_column_offset_;
}
Handle<Boolean> ScriptOrigin::ResourceIsSharedCrossOrigin() const {
return resource_is_shared_cross_origin_;
}
Handle<Boolean> Boolean::New(bool value) {
return value ? True() : False();

View File

@ -1918,6 +1918,7 @@ Local<Script> Script::New(v8::Handle<String> source,
i::Handle<i::Object> name_obj;
int line_offset = 0;
int column_offset = 0;
bool is_shared_cross_origin = false;
if (origin != NULL) {
if (!origin->ResourceName().IsEmpty()) {
name_obj = Utils::OpenHandle(*origin->ResourceName());
@ -1929,6 +1930,10 @@ Local<Script> Script::New(v8::Handle<String> source,
column_offset =
static_cast<int>(origin->ResourceColumnOffset()->Value());
}
if (!origin->ResourceIsSharedCrossOrigin().IsEmpty()) {
is_shared_cross_origin =
origin->ResourceIsSharedCrossOrigin() == v8::True();
}
}
EXCEPTION_PREAMBLE(isolate);
i::ScriptDataImpl* pre_data_impl =
@ -1945,6 +1950,7 @@ Local<Script> Script::New(v8::Handle<String> source,
name_obj,
line_offset,
column_offset,
is_shared_cross_origin,
isolate->global_context(),
NULL,
pre_data_impl,
@ -2412,6 +2418,20 @@ int Message::GetEndColumn() const {
}
bool Message::IsSharedCrossOrigin() const {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
if (IsDeadCheck(isolate, "v8::Message::IsSharedCrossOrigin()")) return 0;
ENTER_V8(isolate);
i::HandleScope scope(isolate);
i::Handle<i::JSMessageObject> message =
i::Handle<i::JSMessageObject>::cast(Utils::OpenHandle(this));
i::Handle<i::JSValue> script =
i::Handle<i::JSValue>::cast(i::Handle<i::Object>(message->script(),
isolate));
return i::Script::cast(script->value())->is_shared_cross_origin();
}
Local<String> Message::GetSourceLine() const {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ON_BAILOUT(isolate, "v8::Message::GetSourceLine()", return Local<String>());

View File

@ -1525,6 +1525,7 @@ bool Genesis::CompileScriptCached(Isolate* isolate,
script_name,
0,
0,
false,
top_context,
extension,
NULL,

View File

@ -144,7 +144,8 @@ bool CompilationCacheScript::HasOrigin(
Handle<SharedFunctionInfo> function_info,
Handle<Object> name,
int line_offset,
int column_offset) {
int column_offset,
bool is_shared_cross_origin) {
Handle<Script> script =
Handle<Script>(Script::cast(function_info->script()), isolate());
// If the script name isn't set, the boilerplate script should have
@ -157,6 +158,8 @@ bool CompilationCacheScript::HasOrigin(
if (column_offset != script->column_offset()->value()) return false;
// Check that both names are strings. If not, no match.
if (!name->IsString() || !script->name()->IsString()) return false;
// Were both scripts tagged by the embedder as being shared cross-origin?
if (is_shared_cross_origin != script->is_shared_cross_origin()) return false;
// Compare the two name strings for equality.
return String::cast(*name)->Equals(String::cast(script->name()));
}
@ -171,6 +174,7 @@ Handle<SharedFunctionInfo> CompilationCacheScript::Lookup(
Handle<Object> name,
int line_offset,
int column_offset,
bool is_shared_cross_origin,
Handle<Context> context) {
Object* result = NULL;
int generation;
@ -186,7 +190,11 @@ Handle<SharedFunctionInfo> CompilationCacheScript::Lookup(
Handle<SharedFunctionInfo>::cast(probe);
// Break when we've found a suitable shared function info that
// matches the origin.
if (HasOrigin(function_info, name, line_offset, column_offset)) {
if (HasOrigin(function_info,
name,
line_offset,
column_offset,
is_shared_cross_origin)) {
result = *function_info;
break;
}
@ -214,7 +222,11 @@ Handle<SharedFunctionInfo> CompilationCacheScript::Lookup(
if (result != NULL) {
Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result),
isolate());
ASSERT(HasOrigin(shared, name, line_offset, column_offset));
ASSERT(HasOrigin(shared,
name,
line_offset,
column_offset,
is_shared_cross_origin));
// If the script was found in a later generation, we promote it to
// the first generation to let it survive longer in the cache.
if (generation != 0) Put(source, context, shared);
@ -391,12 +403,18 @@ Handle<SharedFunctionInfo> CompilationCache::LookupScript(
Handle<Object> name,
int line_offset,
int column_offset,
bool is_shared_cross_origin,
Handle<Context> context) {
if (!IsEnabled()) {
return Handle<SharedFunctionInfo>::null();
}
return script_.Lookup(source, name, line_offset, column_offset, context);
return script_.Lookup(source,
name,
line_offset,
column_offset,
is_shared_cross_origin,
context);
}

View File

@ -99,6 +99,7 @@ class CompilationCacheScript : public CompilationSubCache {
Handle<Object> name,
int line_offset,
int column_offset,
bool is_shared_cross_origin,
Handle<Context> context);
void Put(Handle<String> source,
Handle<Context> context,
@ -119,7 +120,8 @@ class CompilationCacheScript : public CompilationSubCache {
bool HasOrigin(Handle<SharedFunctionInfo> function_info,
Handle<Object> name,
int line_offset,
int column_offset);
int column_offset,
bool is_shared_cross_origin);
void* script_histogram_;
bool script_histogram_initialized_;
@ -212,6 +214,7 @@ class CompilationCache {
Handle<Object> name,
int line_offset,
int column_offset,
bool is_shared_cross_origin,
Handle<Context> context);
// Finds the shared function info for a source string for eval in a

View File

@ -667,6 +667,7 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source,
Handle<Object> script_name,
int line_offset,
int column_offset,
bool is_shared_cross_origin,
Handle<Context> context,
v8::Extension* extension,
ScriptDataImpl* pre_data,
@ -689,6 +690,7 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source,
script_name,
line_offset,
column_offset,
is_shared_cross_origin,
context);
}
@ -712,6 +714,7 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source,
script->set_line_offset(Smi::FromInt(line_offset));
script->set_column_offset(Smi::FromInt(column_offset));
}
script->set_is_shared_cross_origin(is_shared_cross_origin);
script->set_data(script_data.is_null() ? HEAP->undefined_value()
: *script_data);

View File

@ -572,6 +572,7 @@ class Compiler : public AllStatic {
Handle<Object> script_name,
int line_offset,
int column_offset,
bool is_shared_cross_origin,
Handle<Context> context,
v8::Extension* extension,
ScriptDataImpl* pre_data,

View File

@ -786,6 +786,7 @@ bool Debug::CompileDebuggerScript(int index) {
function_info = Compiler::Compile(source_code,
script_name,
0, 0,
false,
context,
NULL, NULL,
Handle<String>::null(),

View File

@ -4499,6 +4499,7 @@ ACCESSORS(Script, eval_from_shared, Object, kEvalFromSharedOffset)
ACCESSORS_TO_SMI(Script, eval_from_instructions_offset,
kEvalFrominstructionsOffsetOffset)
ACCESSORS_TO_SMI(Script, flags, kFlagsOffset)
BOOL_ACCESSORS(Script, flags, is_shared_cross_origin, kIsSharedCrossOriginBit)
Script::CompilationType Script::compilation_type() {
return BooleanBit::get(flags(), kCompilationTypeBit) ?

View File

@ -5876,6 +5876,12 @@ class Script: public Struct {
inline CompilationState compilation_state();
inline void set_compilation_state(CompilationState state);
// [is_shared_cross_origin]: An opaque boolean set by the embedder via
// ScriptOrigin, and used by the embedder to make decisions about the
// script's level of privilege. V8 just passes this through. Encoded in
// the 'flags' field.
DECL_BOOLEAN_ACCESSORS(is_shared_cross_origin)
static inline Script* cast(Object* obj);
// If script source is an external string, check that the underlying
@ -5907,6 +5913,7 @@ class Script: public Struct {
// Bit positions in the flags field.
static const int kCompilationTypeBit = 0;
static const int kCompilationStateBit = 1;
static const int kIsSharedCrossOriginBit = 2;
DISALLOW_IMPLICIT_CONSTRUCTORS(Script);
};

View File

@ -3565,6 +3565,7 @@ static void check_message_0(v8::Handle<v8::Message> message,
CHECK_EQ(5.76, data->NumberValue());
CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
CHECK(!message->IsSharedCrossOrigin());
message_received = true;
}
@ -3591,6 +3592,7 @@ static void check_message_1(v8::Handle<v8::Message> message,
v8::Handle<Value> data) {
CHECK(data->IsNumber());
CHECK_EQ(1337, data->Int32Value());
CHECK(!message->IsSharedCrossOrigin());
message_received = true;
}
@ -3615,6 +3617,7 @@ static void check_message_2(v8::Handle<v8::Message> message,
v8::Local<v8::Value> hidden_property =
v8::Object::Cast(*data)->GetHiddenValue(v8_str("hidden key"));
CHECK(v8_str("hidden value")->Equals(hidden_property));
CHECK(!message->IsSharedCrossOrigin());
message_received = true;
}
@ -3636,6 +3639,112 @@ TEST(MessageHandler2) {
}
static void check_message_3(v8::Handle<v8::Message> message,
v8::Handle<Value> data) {
CHECK(message->IsSharedCrossOrigin());
CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
message_received = true;
}
TEST(MessageHandler3) {
message_received = false;
v8::HandleScope scope(v8::Isolate::GetCurrent());
CHECK(!message_received);
v8::V8::AddMessageListener(check_message_3);
LocalContext context;
v8::ScriptOrigin origin =
v8::ScriptOrigin(v8_str("6.75"),
v8::Integer::New(1),
v8::Integer::New(2),
v8::True());
v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
&origin);
script->Run();
CHECK(message_received);
// clear out the message listener
v8::V8::RemoveMessageListeners(check_message_3);
}
static void check_message_4(v8::Handle<v8::Message> message,
v8::Handle<Value> data) {
CHECK(!message->IsSharedCrossOrigin());
CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
message_received = true;
}
TEST(MessageHandler4) {
message_received = false;
v8::HandleScope scope(v8::Isolate::GetCurrent());
CHECK(!message_received);
v8::V8::AddMessageListener(check_message_4);
LocalContext context;
v8::ScriptOrigin origin =
v8::ScriptOrigin(v8_str("6.75"),
v8::Integer::New(1),
v8::Integer::New(2),
v8::False());
v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
&origin);
script->Run();
CHECK(message_received);
// clear out the message listener
v8::V8::RemoveMessageListeners(check_message_4);
}
static void check_message_5a(v8::Handle<v8::Message> message,
v8::Handle<Value> data) {
CHECK(message->IsSharedCrossOrigin());
CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
message_received = true;
}
static void check_message_5b(v8::Handle<v8::Message> message,
v8::Handle<Value> data) {
CHECK(!message->IsSharedCrossOrigin());
CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
message_received = true;
}
TEST(MessageHandler5) {
message_received = false;
v8::HandleScope scope(v8::Isolate::GetCurrent());
CHECK(!message_received);
v8::V8::AddMessageListener(check_message_5a);
LocalContext context;
v8::ScriptOrigin origin =
v8::ScriptOrigin(v8_str("6.75"),
v8::Integer::New(1),
v8::Integer::New(2),
v8::True());
v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
&origin);
script->Run();
CHECK(message_received);
// clear out the message listener
v8::V8::RemoveMessageListeners(check_message_5a);
message_received = false;
v8::V8::AddMessageListener(check_message_5b);
origin =
v8::ScriptOrigin(v8_str("6.75"),
v8::Integer::New(1),
v8::Integer::New(2),
v8::False());
script = Script::Compile(v8_str("throw 'error'"),
&origin);
script->Run();
CHECK(message_received);
// clear out the message listener
v8::V8::RemoveMessageListeners(check_message_5b);
}
THREADED_TEST(GetSetProperty) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());

View File

@ -105,6 +105,7 @@ static Handle<JSFunction> Compile(const char* source) {
Handle<String>(),
0,
0,
false,
Handle<Context>(isolate->native_context()),
NULL,
NULL,