OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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>(); |
jsbell
2017/04/04 16:42:33
Aside: I wonder if we test this code path?
pwnall
2017/04/04 22:41:42
Nope.
I looked at it... then I thought of what I'
jsbell
2017/04/05 17:56:07
Tracking bug is fine.
(Without exploring all of
| |
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. | |
jsbell
2017/04/04 16:42:33
maybe append "or undetected version skew."
pwnall
2017/04/04 22:41:42
Done.
| |
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 Loading... | |
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 |
OLD | NEW |