Commit d70f7882 authored by marja@chromium.org's avatar marja@chromium.org

Fail the compilation if the cached data is invalid.

R=svenpanne@chromium.org
BUG=

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20703 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 9843789d
...@@ -1694,40 +1694,44 @@ Local<UnboundScript> ScriptCompiler::CompileUnbound( ...@@ -1694,40 +1694,44 @@ Local<UnboundScript> ScriptCompiler::CompileUnbound(
CompileOptions options) { CompileOptions options) {
i::ScriptData* script_data_impl = NULL; i::ScriptData* script_data_impl = NULL;
i::CachedDataMode cached_data_mode = i::NO_CACHED_DATA; i::CachedDataMode cached_data_mode = i::NO_CACHED_DATA;
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ON_BAILOUT(isolate, "v8::ScriptCompiler::CompileUnbound()",
return Local<UnboundScript>());
if (options & kProduceDataToCache) { if (options & kProduceDataToCache) {
cached_data_mode = i::PRODUCE_CACHED_DATA; cached_data_mode = i::PRODUCE_CACHED_DATA;
ASSERT(source->cached_data == NULL); ASSERT(source->cached_data == NULL);
if (source->cached_data) { if (source->cached_data) {
// Asked to produce cached data even though there is some already -> not // Asked to produce cached data even though there is some already -> not
// good. In release mode, try to do the right thing: Just regenerate the // good. Fail the compilation.
// data. EXCEPTION_PREAMBLE(isolate);
delete source->cached_data; i::Handle<i::Object> result = isolate->factory()->NewSyntaxError(
source->cached_data = NULL; "invalid_cached_data", isolate->factory()->NewJSArray(0));
isolate->Throw(*result);
isolate->ReportPendingMessages();
has_pending_exception = true;
EXCEPTION_BAILOUT_CHECK(isolate, Local<UnboundScript>());
} }
} else if (source->cached_data) { } else if (source->cached_data) {
cached_data_mode = i::CONSUME_CACHED_DATA;
// ScriptData takes care of aligning, in case the data is not aligned // ScriptData takes care of aligning, in case the data is not aligned
// correctly. // correctly.
script_data_impl = i::ScriptData::New( script_data_impl = i::ScriptData::New(
reinterpret_cast<const char*>(source->cached_data->data), reinterpret_cast<const char*>(source->cached_data->data),
source->cached_data->length); source->cached_data->length);
// We assert that the pre-data is sane, even though we can actually // If the cached data is not valid, fail the compilation.
// handle it if it turns out not to be in release mode. if (script_data_impl == NULL || !script_data_impl->SanityCheck()) {
ASSERT(script_data_impl->SanityCheck()); EXCEPTION_PREAMBLE(isolate);
if (script_data_impl->SanityCheck()) { i::Handle<i::Object> result = isolate->factory()->NewSyntaxError(
cached_data_mode = i::CONSUME_CACHED_DATA; "invalid_cached_data", isolate->factory()->NewJSArray(0));
} else { isolate->Throw(*result);
// If the pre-data isn't sane we simply ignore it. isolate->ReportPendingMessages();
delete script_data_impl; delete script_data_impl;
script_data_impl = NULL; has_pending_exception = true;
delete source->cached_data; EXCEPTION_BAILOUT_CHECK(isolate, Local<UnboundScript>());
source->cached_data = NULL;
} }
} }
i::Handle<i::String> str = Utils::OpenHandle(*(source->source_string)); i::Handle<i::String> str = Utils::OpenHandle(*(source->source_string));
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ON_BAILOUT(isolate, "v8::ScriptCompiler::CompileUnbound()",
return Local<UnboundScript>());
LOG_API(isolate, "ScriptCompiler::CompileUnbound"); LOG_API(isolate, "ScriptCompiler::CompileUnbound");
ENTER_V8(isolate); ENTER_V8(isolate);
i::SharedFunctionInfo* raw_result = NULL; i::SharedFunctionInfo* raw_result = NULL;
......
...@@ -154,7 +154,8 @@ var kMessages = { ...@@ -154,7 +154,8 @@ var kMessages = {
array_indexof_not_defined: ["Array.getIndexOf: Argument undefined"], array_indexof_not_defined: ["Array.getIndexOf: Argument undefined"],
object_not_extensible: ["Can't add property ", "%0", ", object is not extensible"], object_not_extensible: ["Can't add property ", "%0", ", object is not extensible"],
illegal_access: ["Illegal access"], illegal_access: ["Illegal access"],
invalid_preparser_data: ["Invalid preparser data for function ", "%0"], invalid_cached_data_function: ["Invalid cached data for function ", "%0"],
invalid_cached_data: ["Invalid cached data"],
strict_mode_with: ["Strict mode code may not include a with statement"], strict_mode_with: ["Strict mode code may not include a with statement"],
strict_eval_arguments: ["Unexpected eval or arguments in strict mode"], strict_eval_arguments: ["Unexpected eval or arguments in strict mode"],
too_many_arguments: ["Too many arguments in function call (only 65535 allowed)"], too_many_arguments: ["Too many arguments in function call (only 65535 allowed)"],
......
...@@ -227,15 +227,13 @@ Handle<String> Parser::LookupCachedSymbol(int symbol_id) { ...@@ -227,15 +227,13 @@ Handle<String> Parser::LookupCachedSymbol(int symbol_id) {
ScriptData* ScriptData::New(const char* data, int length) { ScriptData* ScriptData::New(const char* data, int length) {
// The length is obviously invalid. // The length is obviously invalid.
if (length % sizeof(unsigned) != 0) { if (length % sizeof(unsigned) != 0) {
return new ScriptData(); return NULL;
} }
int deserialized_data_length = length / sizeof(unsigned); int deserialized_data_length = length / sizeof(unsigned);
unsigned* deserialized_data; unsigned* deserialized_data;
ScriptData* script_data = new ScriptData(); bool owns_store = reinterpret_cast<intptr_t>(data) % sizeof(unsigned) != 0;
script_data->owns_store_ = if (owns_store) {
reinterpret_cast<intptr_t>(data) % sizeof(unsigned) != 0;
if (script_data->owns_store_) {
// Copy the data to align it. // Copy the data to align it.
deserialized_data = i::NewArray<unsigned>(deserialized_data_length); deserialized_data = i::NewArray<unsigned>(deserialized_data_length);
i::CopyBytes(reinterpret_cast<char*>(deserialized_data), i::CopyBytes(reinterpret_cast<char*>(deserialized_data),
...@@ -244,9 +242,9 @@ ScriptData* ScriptData::New(const char* data, int length) { ...@@ -244,9 +242,9 @@ ScriptData* ScriptData::New(const char* data, int length) {
// If aligned, don't create a copy of the data. // If aligned, don't create a copy of the data.
deserialized_data = reinterpret_cast<unsigned*>(const_cast<char*>(data)); deserialized_data = reinterpret_cast<unsigned*>(const_cast<char*>(data));
} }
script_data->store_ = return new ScriptData(
Vector<unsigned>(deserialized_data, deserialized_data_length); Vector<unsigned>(deserialized_data, deserialized_data_length),
return script_data; owns_store);
} }
...@@ -3138,10 +3136,10 @@ DebuggerStatement* Parser::ParseDebuggerStatement(bool* ok) { ...@@ -3138,10 +3136,10 @@ DebuggerStatement* Parser::ParseDebuggerStatement(bool* ok) {
} }
void Parser::ReportInvalidPreparseData(Handle<String> name, bool* ok) { void Parser::ReportInvalidCachedData(Handle<String> name, bool* ok) {
SmartArrayPointer<char> name_string = name->ToCString(DISALLOW_NULLS); SmartArrayPointer<char> name_string = name->ToCString(DISALLOW_NULLS);
const char* element[1] = { name_string.get() }; const char* element[1] = { name_string.get() };
ReportMessage("invalid_preparser_data", ReportMessage("invalid_cached_data_function",
Vector<const char*>(element, 1)); Vector<const char*>(element, 1));
*ok = false; *ok = false;
} }
...@@ -3402,7 +3400,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -3402,7 +3400,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
if (entry.end_pos() <= function_block_pos) { if (entry.end_pos() <= function_block_pos) {
// End position greater than end of stream is safe, and hard // End position greater than end of stream is safe, and hard
// to check. // to check.
ReportInvalidPreparseData(function_name, CHECK_OK); ReportInvalidCachedData(function_name, CHECK_OK);
} }
scanner()->SeekForward(entry.end_pos() - 1); scanner()->SeekForward(entry.end_pos() - 1);
...@@ -3415,13 +3413,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -3415,13 +3413,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
scope_->SetStrictMode(entry.strict_mode()); scope_->SetStrictMode(entry.strict_mode());
} else { } else {
// This case happens when we have preparse data but it doesn't contain // This case happens when we have preparse data but it doesn't contain
// an entry for the function. As a safety net, fall back to eager // an entry for the function. Fail the compilation.
// parsing. It is unclear whether PreParser's laziness analysis can ReportInvalidCachedData(function_name, CHECK_OK);
// produce different results than the Parser's laziness analysis (see
// https://codereview.chromium.org/7565003 ). In this case, we must
// discard all the preparse data, since the symbol data will be wrong.
is_lazily_parsed = false;
cached_data_mode_ = NO_CACHED_DATA;
} }
} else { } else {
// With no cached data, we partially parse the function, without // With no cached data, we partially parse the function, without
......
...@@ -88,9 +88,9 @@ class ScriptData { ...@@ -88,9 +88,9 @@ class ScriptData {
: store_(store), : store_(store),
owns_store_(true) { } owns_store_(true) { }
// Create an empty ScriptData that is guaranteed to not satisfy ScriptData(Vector<unsigned> store, bool owns_store)
// a SanityCheck. : store_(store),
ScriptData() : owns_store_(false) { } owns_store_(owns_store) { }
// The created ScriptData won't take ownership of the data. If the alignment // The created ScriptData won't take ownership of the data. If the alignment
// is not correct, this will copy the data (and the created ScriptData will // is not correct, this will copy the data (and the created ScriptData will
...@@ -667,7 +667,7 @@ class Parser : public ParserBase<ParserTraits> { ...@@ -667,7 +667,7 @@ class Parser : public ParserBase<ParserTraits> {
Handle<String> source); Handle<String> source);
// Report syntax error // Report syntax error
void ReportInvalidPreparseData(Handle<String> name, bool* ok); void ReportInvalidCachedData(Handle<String> name, bool* ok);
void SetCachedData(ScriptData** data, void SetCachedData(ScriptData** data,
CachedDataMode cached_data_mode) { CachedDataMode cached_data_mode) {
......
...@@ -14858,10 +14858,7 @@ TEST(PreCompileDeserializationError) { ...@@ -14858,10 +14858,7 @@ TEST(PreCompileDeserializationError) {
const char* data = "DONT CARE"; const char* data = "DONT CARE";
int invalid_size = 3; int invalid_size = 3;
i::ScriptData* sd = i::ScriptData::New(data, invalid_size); i::ScriptData* sd = i::ScriptData::New(data, invalid_size);
CHECK_EQ(NULL, sd);
CHECK_EQ(0, sd->Length());
delete sd;
} }
...@@ -14911,16 +14908,18 @@ TEST(CompileWithInvalidCachedData) { ...@@ -14911,16 +14908,18 @@ TEST(CompileWithInvalidCachedData) {
v8::ScriptCompiler::CompileUnbound(isolate, &source2); v8::ScriptCompiler::CompileUnbound(isolate, &source2);
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
String::Utf8Value exception_value(try_catch.Message()->Get()); {
CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar", String::Utf8Value exception_value(try_catch.Message()->Get());
*exception_value); CHECK_EQ("Uncaught SyntaxError: Invalid cached data for function bar",
*exception_value);
}
try_catch.Reset(); try_catch.Reset();
delete sd; delete sd;
// Overwrite function bar's start position with 200. The function entry // Overwrite function bar's start position with 200. The function entry will
// will not be found when searching for it by position and we should fall // not be found when searching for it by position, and the compilation fails.
// back on eager compilation.
// ScriptData does not take ownership of the buffers passed to it. // ScriptData does not take ownership of the buffers passed to it.
sd = i::ScriptData::New(reinterpret_cast<const char*>(cd->data), cd->length); sd = i::ScriptData::New(reinterpret_cast<const char*>(cd->data), cd->length);
sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data())); sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
...@@ -14934,7 +14933,34 @@ TEST(CompileWithInvalidCachedData) { ...@@ -14934,7 +14933,34 @@ TEST(CompileWithInvalidCachedData) {
reinterpret_cast<const uint8_t*>(sd->Data()), sd->Length())); reinterpret_cast<const uint8_t*>(sd->Data()), sd->Length()));
compiled_script = compiled_script =
v8::ScriptCompiler::CompileUnbound(isolate, &source3); v8::ScriptCompiler::CompileUnbound(isolate, &source3);
CHECK(!try_catch.HasCaught()); CHECK(try_catch.HasCaught());
{
String::Utf8Value exception_value(try_catch.Message()->Get());
CHECK_EQ("Uncaught SyntaxError: Invalid cached data for function bar",
*exception_value);
}
CHECK(compiled_script.IsEmpty());
try_catch.Reset();
delete sd;
// Try passing in cached data which is obviously invalid (wrong length).
sd = i::ScriptData::New(reinterpret_cast<const char*>(cd->data), cd->length);
const char* script4 =
"function foo(){ return 8;}\n"
"function bar(){ return 6 + 7;} foo();";
v8::ScriptCompiler::Source source4(
v8_str(script4),
new v8::ScriptCompiler::CachedData(
reinterpret_cast<const uint8_t*>(sd->Data()), sd->Length() - 1));
compiled_script =
v8::ScriptCompiler::CompileUnbound(isolate, &source4);
CHECK(try_catch.HasCaught());
{
String::Utf8Value exception_value(try_catch.Message()->Get());
CHECK_EQ("Uncaught SyntaxError: Invalid cached data",
*exception_value);
}
CHECK(compiled_script.IsEmpty());
delete sd; delete sd;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment