Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(105)

Side by Side Diff: third_party/WebKit/Source/bindings/modules/v8/V8BindingForModules.cpp

Issue 2781273004: Graceful handling of new versions of IndexedDB serialized data. (Closed)
Patch Set: Addressed nits. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2011 Google Inc. All rights reserved. 2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 7 *
8 * 1. Redistributions of source code must retain the above copyright 8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright 10 * 2. Redistributions in binary form must reproduce the above copyright
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
50 #include "modules/indexeddb/IDBValue.h" 50 #include "modules/indexeddb/IDBValue.h"
51 #include "platform/RuntimeEnabledFeatures.h" 51 #include "platform/RuntimeEnabledFeatures.h"
52 #include "platform/SharedBuffer.h" 52 #include "platform/SharedBuffer.h"
53 #include "wtf/MathExtras.h" 53 #include "wtf/MathExtras.h"
54 #include "wtf/Vector.h" 54 #include "wtf/Vector.h"
55 55
56 namespace blink { 56 namespace blink {
57 57
58 static v8::Local<v8::Value> deserializeIDBValueData(v8::Isolate*, 58 static v8::Local<v8::Value> deserializeIDBValueData(v8::Isolate*,
59 const IDBValue*); 59 const IDBValue*);
60 static v8::Local<v8::Value> deserializeIDBValue(
61 v8::Isolate*,
62 v8::Local<v8::Object> creationContext,
63 const IDBValue*);
64 static v8::Local<v8::Value> deserializeIDBValueArray( 60 static v8::Local<v8::Value> deserializeIDBValueArray(
65 v8::Isolate*, 61 v8::Isolate*,
66 v8::Local<v8::Object> creationContext, 62 v8::Local<v8::Object> creationContext,
67 const Vector<RefPtr<IDBValue>>*); 63 const Vector<RefPtr<IDBValue>>*);
68 64
69 v8::Local<v8::Value> ToV8(const IDBKeyPath& value, 65 v8::Local<v8::Value> ToV8(const IDBKeyPath& value,
70 v8::Local<v8::Object> creationContext, 66 v8::Local<v8::Object> creationContext,
71 v8::Isolate* isolate) { 67 v8::Isolate* isolate) {
72 switch (value.getType()) { 68 switch (value.getType()) {
73 case IDBKeyPath::NullType: 69 case IDBKeyPath::NullType:
(...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after
371 } 367 }
372 return IDBKey::createArray(result); 368 return IDBKey::createArray(result);
373 } 369 }
374 370
375 ASSERT(keyPath.getType() == IDBKeyPath::StringType); 371 ASSERT(keyPath.getType() == IDBKeyPath::StringType);
376 return createIDBKeyFromValueAndKeyPath(isolate, value, keyPath.string(), 372 return createIDBKeyFromValueAndKeyPath(isolate, value, keyPath.string(),
377 exceptionState); 373 exceptionState);
378 } 374 }
379 375
380 // Deserialize just the value data & blobInfo from the given IDBValue. 376 // Deserialize just the value data & blobInfo from the given IDBValue.
381 // Does not deserialize the key & keypath. 377 //
378 // Primary key injection is performed in deserializeIDBValue() below.
382 static v8::Local<v8::Value> deserializeIDBValueData(v8::Isolate* isolate, 379 static v8::Local<v8::Value> deserializeIDBValueData(v8::Isolate* isolate,
383 const IDBValue* value) { 380 const IDBValue* value) {
384 ASSERT(isolate->InContext()); 381 ASSERT(isolate->InContext());
385 if (!value || value->isNull()) 382 if (!value || value->isNull())
386 return v8::Null(isolate); 383 return v8::Null(isolate);
387 384
388 RefPtr<SerializedScriptValue> serializedValue = 385 RefPtr<SerializedScriptValue> serializedValue =
389 value->createSerializedValue(); 386 value->createSerializedValue();
390 SerializedScriptValue::DeserializeOptions options; 387 SerializedScriptValue::DeserializeOptions options;
391 options.blobInfo = value->blobInfo(); 388 options.blobInfo = value->blobInfo();
392 options.readWasmFromStream = true; 389 options.readWasmFromStream = true;
390
391 // deserialize() returns null when serialization fails. This is sub-optimal
392 // because IndexedDB values can be null, so an application cannot distinguish
393 // between a de-serialization failure and a legitimately stored null value.
394 //
395 // TODO(crbug.com/703704): Ideally, SerializedScriptValue should return an
396 // empty handle on serialization errors, which should be handled by higher
397 // layers. For example, IndexedDB could throw an exception, abort the
398 // transaction, or close the database connection.
393 return serializedValue->deserialize(isolate, options); 399 return serializedValue->deserialize(isolate, options);
394 } 400 }
395 401
396 // Deserialize the entire IDBValue (injecting key & keypath if present). 402 // Deserialize the entire IDBValue.
397 static v8::Local<v8::Value> deserializeIDBValue( 403 //
398 v8::Isolate* isolate, 404 // On top of deserializeIDBValueData(), this handles the special case of having
399 v8::Local<v8::Object> creationContext, 405 // to inject a key into the de-serialized value. See injectV8KeyIntoV8Value()
400 const IDBValue* value) { 406 // for details.
407 v8::Local<v8::Value> deserializeIDBValue(v8::Isolate* isolate,
408 v8::Local<v8::Object> creationContext,
409 const IDBValue* value) {
401 ASSERT(isolate->InContext()); 410 ASSERT(isolate->InContext());
402 if (!value || value->isNull()) 411 if (!value || value->isNull())
403 return v8::Null(isolate); 412 return v8::Null(isolate);
404 413
405 v8::Local<v8::Value> v8Value = deserializeIDBValueData(isolate, value); 414 v8::Local<v8::Value> v8Value = deserializeIDBValueData(isolate, value);
406 if (value->primaryKey()) { 415 if (value->primaryKey()) {
407 v8::Local<v8::Value> key = 416 v8::Local<v8::Value> key =
408 ToV8(value->primaryKey(), creationContext, isolate); 417 ToV8(value->primaryKey(), creationContext, isolate);
409 if (key.IsEmpty()) 418 if (key.IsEmpty())
410 return v8::Local<v8::Value>(); 419 return v8::Local<v8::Value>();
411 bool injected = 420
412 injectV8KeyIntoV8Value(isolate, key, v8Value, value->keyPath()); 421 injectV8KeyIntoV8Value(isolate, key, v8Value, value->keyPath());
413 DCHECK(injected); 422
423 // TODO(crbug.com/703704): Throw an error here or at a higher layer if
424 // injectV8KeyIntoV8Value() returns false, which means that the serialized
425 // value got corrupted while on disk.
414 } 426 }
415 427
416 return v8Value; 428 return v8Value;
417 } 429 }
418 430
419 static v8::Local<v8::Value> deserializeIDBValueArray( 431 static v8::Local<v8::Value> deserializeIDBValueArray(
420 v8::Isolate* isolate, 432 v8::Isolate* isolate,
421 v8::Local<v8::Object> creationContext, 433 v8::Local<v8::Object> creationContext,
422 const Vector<RefPtr<IDBValue>>* values) { 434 const Vector<RefPtr<IDBValue>>* values) {
423 ASSERT(isolate->InContext()); 435 ASSERT(isolate->InContext());
424 436
425 v8::Local<v8::Context> context = isolate->GetCurrentContext(); 437 v8::Local<v8::Context> context = isolate->GetCurrentContext();
426 v8::Local<v8::Array> array = v8::Array::New(isolate, values->size()); 438 v8::Local<v8::Array> array = v8::Array::New(isolate, values->size());
427 for (size_t i = 0; i < values->size(); ++i) { 439 for (size_t i = 0; i < values->size(); ++i) {
428 v8::Local<v8::Value> v8Value = 440 v8::Local<v8::Value> v8Value =
429 deserializeIDBValue(isolate, creationContext, values->at(i).get()); 441 deserializeIDBValue(isolate, creationContext, values->at(i).get());
430 if (v8Value.IsEmpty()) 442 if (v8Value.IsEmpty())
431 v8Value = v8::Undefined(isolate); 443 v8Value = v8::Undefined(isolate);
432 if (!v8CallBoolean(array->CreateDataProperty(context, i, v8Value))) 444 if (!v8CallBoolean(array->CreateDataProperty(context, i, v8Value)))
433 return v8Undefined(); 445 return v8Undefined();
434 } 446 }
435 447
436 return array; 448 return array;
437 } 449 }
438 450
439 // This is only applied to deserialized values which were validated before 451 // Injects a primary key into a deserialized V8 value.
440 // serialization, so various assumptions/assertions can be made. 452 //
453 // In general, the value stored in IndexedDB is the serialized version of a
454 // value passed to the API. However, the specification has a special case of
455 // object stores that specify a key path and have a key generator. In this case,
456 // the conceptual description in the spec states that the key produced by the
457 // key generator is injected into the value before it is written to IndexedDB.
458 //
459 // We cannot implementing the spec's conceptual description. We need to assign
460 // primary keys in the browser process, to ensure that multiple renderer
461 // processes talking to the same database receive sequential keys. At the same
462 // time, we want the value serialization code to live in the renderer process,
463 // because this type of code is a likely victim to security exploits.
464 //
465 // We handle this special case by serializing and writing values without the
466 // corresponding keys. At read time, we obtain the keys and the values
467 // separately, and we inject the keys into values.
441 bool injectV8KeyIntoV8Value(v8::Isolate* isolate, 468 bool injectV8KeyIntoV8Value(v8::Isolate* isolate,
442 v8::Local<v8::Value> key, 469 v8::Local<v8::Value> key,
443 v8::Local<v8::Value> value, 470 v8::Local<v8::Value> value,
444 const IDBKeyPath& keyPath) { 471 const IDBKeyPath& keyPath) {
445 IDB_TRACE("injectIDBV8KeyIntoV8Value"); 472 IDB_TRACE("injectIDBV8KeyIntoV8Value");
446 ASSERT(isolate->InContext()); 473 ASSERT(isolate->InContext());
447 474
448 ASSERT(keyPath.getType() == IDBKeyPath::StringType); 475 ASSERT(keyPath.getType() == IDBKeyPath::StringType);
449 Vector<String> keyPathElements = parseKeyPath(keyPath.string()); 476 Vector<String> keyPathElements = parseKeyPath(keyPath.string());
450 477
451 // The conbination of a key generator and an empty key path is forbidden by 478 // The conbination of a key generator and an empty key path is forbidden by
452 // spec. 479 // spec.
453 if (!keyPathElements.size()) { 480 if (!keyPathElements.size()) {
454 ASSERT_NOT_REACHED(); 481 ASSERT_NOT_REACHED();
455 return false; 482 return false;
456 } 483 }
457 484
458 v8::HandleScope handleScope(isolate); 485 v8::HandleScope handleScope(isolate);
459 v8::Local<v8::Context> context = isolate->GetCurrentContext(); 486 v8::Local<v8::Context> context = isolate->GetCurrentContext();
460 487
461 // For an object o = {} which should have keypath 'a.b.c' and key k, this 488 // For an object o = {} which should have keypath 'a.b.c' and key k, this
462 // populates o to be {a:{b:{}}}. This is only applied to deserialized 489 // populates o to be {a:{b:{}}}. This is only applied to deserialized
463 // values which were validated before serialization, so various 490 // values, so we can assume that there are no getters/setters on the
464 // assumptions can be made, e.g. there are no getters/setters on the
465 // object itself (though there might be on the prototype chain). 491 // object itself (though there might be on the prototype chain).
492 //
493 // Previous versions of this code assumed that the deserialized value meets
494 // the constraints checked by the serialization validation code. For example,
495 // given a keypath of a.b.c, the code assumed that the de-serialized value
496 // cannot possibly be {a:{b:42}}. This is not a safe assumption.
497 //
498 // IndexedDB's backing store (LevelDB) does use CRC32C to protect against disk
499 // errors. However, this does not prevent corruption caused by bugs in the
500 // higher level code writing invalid values. The following cases are
501 // interesting here.
502 //
503 // (1) Deserialization failures, which are currently handled by returning
504 // null. Disk errors aside, deserialization errors can also occur when a user
505 // switches channels and receives an older build which does not support the
506 // serialization format used by the previous (more recent) build that the user
507 // had.
508 //
509 // (2) Bugs that write a value which is incompatible with the primary key
510 // injection required by the object store. The simplest example is writing
511 // numbers or booleans to an object store with an auto-incrementing primary
512 // keys.
466 for (size_t i = 0; i < keyPathElements.size() - 1; ++i) { 513 for (size_t i = 0; i < keyPathElements.size() - 1; ++i) {
514 if (!value->IsObject())
515 return false;
516
467 const String& keyPathElement = keyPathElements[i]; 517 const String& keyPathElement = keyPathElements[i];
468 ASSERT(value->IsObject());
469 ASSERT(!isImplicitProperty(isolate, value, keyPathElement)); 518 ASSERT(!isImplicitProperty(isolate, value, keyPathElement));
470 v8::Local<v8::Object> object = value.As<v8::Object>(); 519 v8::Local<v8::Object> object = value.As<v8::Object>();
471 v8::Local<v8::String> property = v8String(isolate, keyPathElement); 520 v8::Local<v8::String> property = v8String(isolate, keyPathElement);
472 bool hasOwnProperty; 521 bool hasOwnProperty;
473 if (!v8Call(object->HasOwnProperty(context, property), hasOwnProperty)) 522 if (!v8Call(object->HasOwnProperty(context, property), hasOwnProperty))
474 return false; 523 return false;
475 if (hasOwnProperty) { 524 if (hasOwnProperty) {
476 if (!object->Get(context, property).ToLocal(&value)) 525 if (!object->Get(context, property).ToLocal(&value))
477 return false; 526 return false;
478 } else { 527 } else {
479 value = v8::Object::New(isolate); 528 value = v8::Object::New(isolate);
480 if (!v8CallBoolean(object->CreateDataProperty(context, property, value))) 529 if (!v8CallBoolean(object->CreateDataProperty(context, property, value)))
481 return false; 530 return false;
482 } 531 }
483 } 532 }
484 533
485 // Implicit properties don't need to be set. The caller is not required to 534 // Implicit properties don't need to be set. The caller is not required to
486 // be aware of this, so this is an expected no-op. The caller can verify 535 // be aware of this, so this is an expected no-op. The caller can verify
487 // that the value is correct via assertPrimaryKeyValidOrInjectable. 536 // that the value is correct via assertPrimaryKeyValidOrInjectable.
488 if (isImplicitProperty(isolate, value, keyPathElements.back())) 537 if (isImplicitProperty(isolate, value, keyPathElements.back()))
489 return true; 538 return true;
490 539
491 // If it's not an implicit property of value, value must be an object. 540 // If the key path does not point to an implicit property, the value must be
541 // an object. Previous code versions DCHECKed this, which is unsafe in the
542 // event of database corruption or version skew in the serialization format.
543 if (!value->IsObject())
544 return false;
545
492 v8::Local<v8::Object> object = value.As<v8::Object>(); 546 v8::Local<v8::Object> object = value.As<v8::Object>();
493 v8::Local<v8::String> property = v8String(isolate, keyPathElements.back()); 547 v8::Local<v8::String> property = v8String(isolate, keyPathElements.back());
494 if (!v8CallBoolean(object->CreateDataProperty(context, property, key))) 548 if (!v8CallBoolean(object->CreateDataProperty(context, property, key)))
495 return false; 549 return false;
496 550
497 return true; 551 return true;
498 } 552 }
499 553
500 // Verify that an value can have an generated key inserted at the location 554 // Verify that an value can have an generated key inserted at the location
501 // specified by the key path (by injectV8KeyIntoV8Value) when the object is 555 // specified by the key path (by injectV8KeyIntoV8Value) when the object is
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
609 if (expectedKey && expectedKey->isEqual(value->primaryKey())) 663 if (expectedKey && expectedKey->isEqual(value->primaryKey()))
610 return; 664 return;
611 665
612 bool injected = injectV8KeyIntoV8Value( 666 bool injected = injectV8KeyIntoV8Value(
613 isolate, keyValue.v8Value(), scriptValue.v8Value(), value->keyPath()); 667 isolate, keyValue.v8Value(), scriptValue.v8Value(), value->keyPath());
614 DCHECK(injected); 668 DCHECK(injected);
615 } 669 }
616 #endif 670 #endif
617 671
618 } // namespace blink 672 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698