Commit bee6482b authored by Joshua Bell's avatar Joshua Bell Committed by Commit Bot

Bindings: Early exit in value->IDBKey conversion after exception

In cases where an array is being processed to generate a key (either a
JS array, or an array key path), if an exception occurs the conversion
needs to terminate immediately so that the exception can be delivered
to script.

Add early exits in a couple missing places, and add tests. Note that
these cases are not reachable from script, because the conversion code
is defined to run on "structured clones" of input data, so any getters
that throw (for example) will have been filtered out by the previous
serialize/deserialize process producing the clone.

Change-Id: I53576eb1d47d23b0be4b712bc12e9de47078a419
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1777060
Commit-Queue: Joshua Bell <jsbell@chromium.org>
Reviewed-by: default avatarKenichi Ishibashi <bashi@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#692282}
parent 8dc25af9
...@@ -182,6 +182,8 @@ static const size_t kMaximumDepth = 1000; ...@@ -182,6 +182,8 @@ static const size_t kMaximumDepth = 1000;
// returned but with an 'Invalid' entry; this is used for "multi-entry" // returned but with an 'Invalid' entry; this is used for "multi-entry"
// indexes where an array with invalid members is permitted will later be // indexes where an array with invalid members is permitted will later be
// sanitized. // sanitized.
// A V8 exception may be thrown on bad data or by script's getters; if so,
// callers should not make further V8 calls.
static std::unique_ptr<IDBKey> CreateIDBKeyFromValue( static std::unique_ptr<IDBKey> CreateIDBKeyFromValue(
v8::Isolate* isolate, v8::Isolate* isolate,
v8::Local<v8::Value> value, v8::Local<v8::Value> value,
...@@ -245,6 +247,10 @@ static std::unique_ptr<IDBKey> CreateIDBKeyFromValue( ...@@ -245,6 +247,10 @@ static std::unique_ptr<IDBKey> CreateIDBKeyFromValue(
} }
std::unique_ptr<IDBKey> subkey = std::unique_ptr<IDBKey> subkey =
CreateIDBKeyFromValue(isolate, item, stack, exception_state); CreateIDBKeyFromValue(isolate, item, stack, exception_state);
if (exception_state.HadException()) {
exception_state.RethrowV8Exception(block.Exception());
return nullptr;
}
if (!subkey) if (!subkey)
subkeys.emplace_back(IDBKey::CreateInvalid()); subkeys.emplace_back(IDBKey::CreateInvalid());
else else
...@@ -265,6 +271,8 @@ static std::unique_ptr<IDBKey> CreateIDBKeyFromValue( ...@@ -265,6 +271,8 @@ static std::unique_ptr<IDBKey> CreateIDBKeyFromValue(
// sanitized. This is used to implement both of the following spec algorithms: // sanitized. This is used to implement both of the following spec algorithms:
// https://w3c.github.io/IndexedDB/#convert-value-to-key // https://w3c.github.io/IndexedDB/#convert-value-to-key
// https://w3c.github.io/IndexedDB/#convert-a-value-to-a-multientry-key // https://w3c.github.io/IndexedDB/#convert-a-value-to-a-multientry-key
// A V8 exception may be thrown on bad data or by script's getters; if so,
// callers should not make further V8 calls.
static std::unique_ptr<IDBKey> CreateIDBKeyFromValue( static std::unique_ptr<IDBKey> CreateIDBKeyFromValue(
v8::Isolate* isolate, v8::Isolate* isolate,
v8::Local<v8::Value> value, v8::Local<v8::Value> value,
...@@ -310,6 +318,8 @@ static Vector<String> ParseKeyPath(const String& key_path) { ...@@ -310,6 +318,8 @@ static Vector<String> ParseKeyPath(const String& key_path) {
// to a key is representing by returning an 'Invalid' key. (An array with // to a key is representing by returning an 'Invalid' key. (An array with
// invalid members will be returned if needed.) // invalid members will be returned if needed.)
// https://w3c.github.io/IndexedDB/#evaluate-a-key-path-on-a-value // https://w3c.github.io/IndexedDB/#evaluate-a-key-path-on-a-value
// A V8 exception may be thrown on bad data or by script's getters; if so,
// callers should not make further V8 calls.
static std::unique_ptr<IDBKey> CreateIDBKeyFromValueAndKeyPath( static std::unique_ptr<IDBKey> CreateIDBKeyFromValueAndKeyPath(
v8::Isolate* isolate, v8::Isolate* isolate,
v8::Local<v8::Value> v8_value, v8::Local<v8::Value> v8_value,
...@@ -397,6 +407,8 @@ static std::unique_ptr<IDBKey> CreateIDBKeyFromValueAndKeyPath( ...@@ -397,6 +407,8 @@ static std::unique_ptr<IDBKey> CreateIDBKeyFromValueAndKeyPath(
// paths, nullptr is returned if evaluation of any sub-path fails, otherwise // paths, nullptr is returned if evaluation of any sub-path fails, otherwise
// an array key is returned (with potentially 'Invalid' members). // an array key is returned (with potentially 'Invalid' members).
// https://w3c.github.io/IndexedDB/#evaluate-a-key-path-on-a-value // https://w3c.github.io/IndexedDB/#evaluate-a-key-path-on-a-value
// A V8 exception may be thrown on bad data or by script's getters; if so,
// callers should not make further V8 calls.
static std::unique_ptr<IDBKey> CreateIDBKeyFromValueAndKeyPath( static std::unique_ptr<IDBKey> CreateIDBKeyFromValueAndKeyPath(
v8::Isolate* isolate, v8::Isolate* isolate,
v8::Local<v8::Value> value, v8::Local<v8::Value> value,
...@@ -411,6 +423,8 @@ static std::unique_ptr<IDBKey> CreateIDBKeyFromValueAndKeyPath( ...@@ -411,6 +423,8 @@ static std::unique_ptr<IDBKey> CreateIDBKeyFromValueAndKeyPath(
for (const String& path : array) { for (const String& path : array) {
auto key = CreateIDBKeyFromValueAndKeyPath(isolate, value, path, auto key = CreateIDBKeyFromValueAndKeyPath(isolate, value, path,
exception_state); exception_state);
if (exception_state.HadException())
return nullptr;
// Evaluation of path failed - overall failure. // Evaluation of path failed - overall failure.
if (!key) if (!key)
return nullptr; return nullptr;
......
...@@ -60,9 +60,8 @@ struct NativeValueTraits<std::unique_ptr<IDBKey>> { ...@@ -60,9 +60,8 @@ struct NativeValueTraits<std::unique_ptr<IDBKey>> {
// Note that an Array key may contain Invalid members, as the "multi-entry" // Note that an Array key may contain Invalid members, as the "multi-entry"
// index case allows these, and will filter them out later. Use IsValid() to // index case allows these, and will filter them out later. Use IsValid() to
// recursively check. // recursively check.
static std::unique_ptr<IDBKey> NativeValue(v8::Isolate*, MODULES_EXPORT static std::unique_ptr<IDBKey>
v8::Local<v8::Value>, NativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&);
ExceptionState&);
// Implementation for ScriptValue::To<std::unique_ptr<IDBKey>>(). // Implementation for ScriptValue::To<std::unique_ptr<IDBKey>>().
// //
......
...@@ -48,6 +48,15 @@ namespace blink { ...@@ -48,6 +48,15 @@ namespace blink {
namespace { namespace {
v8::Local<v8::Object> EvaluateScriptAsObject(V8TestingScope& scope,
const char* source) {
v8::Local<v8::Script> script =
v8::Script::Compile(scope.GetContext(),
V8String(scope.GetIsolate(), source))
.ToLocalChecked();
return script->Run(scope.GetContext()).ToLocalChecked().As<v8::Object>();
}
std::unique_ptr<IDBKey> CheckKeyFromValueAndKeyPathInternal( std::unique_ptr<IDBKey> CheckKeyFromValueAndKeyPathInternal(
v8::Isolate* isolate, v8::Isolate* isolate,
const ScriptValue& value, const ScriptValue& value,
...@@ -227,6 +236,71 @@ TEST(IDBKeyFromValueAndKeyPathTest, SubProperty) { ...@@ -227,6 +236,71 @@ TEST(IDBKeyFromValueAndKeyPathTest, SubProperty) {
CheckKeyPathNullValue(isolate, script_value, "bar"); CheckKeyPathNullValue(isolate, script_value, "bar");
} }
TEST(IDBKeyFromValue, Exceptions) {
V8TestingScope scope;
{
// Value is an array with a getter that throws.
ScriptValue script_value(
scope.GetScriptState(),
EvaluateScriptAsObject(
scope,
"(()=>{"
" const a = [0, 1, 2];"
" Object.defineProperty(a, 1, {get: () => { throw Error(); }});"
" return a;"
"})()"));
DummyExceptionStateForTesting exception_state;
auto key = ScriptValue::To<std::unique_ptr<IDBKey>>(
scope.GetIsolate(), script_value, exception_state);
EXPECT_FALSE(key->IsValid());
EXPECT_TRUE(exception_state.HadException());
}
{
// Value is an array containing an array with a getter that throws.
ScriptValue script_value(
scope.GetScriptState(),
EvaluateScriptAsObject(
scope,
"(()=>{"
" const a = [0, 1, 2];"
" Object.defineProperty(a, 1, {get: () => { throw Error(); }});"
" return ['x', a, 'z'];"
"})()"));
DummyExceptionStateForTesting exception_state;
auto key = ScriptValue::To<std::unique_ptr<IDBKey>>(
scope.GetIsolate(), script_value, exception_state);
EXPECT_FALSE(key->IsValid());
EXPECT_TRUE(exception_state.HadException());
}
}
TEST(IDBKeyFromValueAndKeyPathTest, Exceptions) {
V8TestingScope scope;
ScriptValue script_value(
scope.GetScriptState(),
EvaluateScriptAsObject(scope,
"({id:1, get throws() { throw Error(); }})"));
{
// Key path references a property that throws.
DummyExceptionStateForTesting exception_state;
EXPECT_FALSE(ScriptValue::To<std::unique_ptr<IDBKey>>(
scope.GetIsolate(), script_value, exception_state,
IDBKeyPath("throws")));
EXPECT_TRUE(exception_state.HadException());
}
{
// Compound key path references a property that throws.
DummyExceptionStateForTesting exception_state;
EXPECT_FALSE(ScriptValue::To<std::unique_ptr<IDBKey>>(
scope.GetIsolate(), script_value, exception_state,
IDBKeyPath(Vector<String>{"id", "throws"})));
EXPECT_TRUE(exception_state.HadException());
}
}
TEST(InjectIDBKeyTest, ImplicitValues) { TEST(InjectIDBKeyTest, ImplicitValues) {
V8TestingScope scope; V8TestingScope scope;
v8::Isolate* isolate = scope.GetIsolate(); v8::Isolate* isolate = scope.GetIsolate();
......
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