[rab/gsab] Fix flag mismatch in serialized data

Bug: v8:11111,chromium:1339648
Change-Id: I3b472f74f37a4e1514ce20635b16970e95a36e15
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3735162
Reviewed-by: Shu-yu Guo <syg@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81598}
This commit is contained in:
Marja Hölttä 2022-07-05 10:01:42 +02:00 committed by V8 LUCI CQ
parent 3f7c53b017
commit 3483b970eb
3 changed files with 70 additions and 4 deletions

View File

@ -65,6 +65,17 @@ static const uint32_t kLatestVersion = 15;
static_assert(kLatestVersion == v8::CurrentValueSerializerFormatVersion(),
"Exported format version must match latest version.");
namespace {
// For serializing JSArrayBufferView flags. Instead of serializing /
// deserializing the flags directly, we serialize them bit by bit. This is for
// ensuring backwards compatilibity in the case where the representation
// changes. Note that the ValueSerializer data can be stored on disk.
using JSArrayBufferViewIsLengthTracking = base::BitField<bool, 0, 1>;
using JSArrayBufferViewIsBackedByRab =
JSArrayBufferViewIsLengthTracking::Next<bool, 1>;
} // namespace
template <typename T>
static size_t BytesNeededForVarint(T value) {
static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value,
@ -934,6 +945,8 @@ Maybe<bool> ValueSerializer::WriteJSArrayBuffer(
if (byte_length > std::numeric_limits<uint32_t>::max()) {
return ThrowDataCloneError(MessageTemplate::kDataCloneError, array_buffer);
}
// TODO(v8:11111): Support RAB / GSAB. The wire version will need to be
// bumped.
WriteTag(SerializationTag::kArrayBuffer);
WriteVarint<uint32_t>(byte_length);
WriteRawBytes(array_buffer->backing_store(), byte_length);
@ -962,7 +975,10 @@ Maybe<bool> ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView view) {
WriteVarint(static_cast<uint8_t>(tag));
WriteVarint(static_cast<uint32_t>(view.byte_offset()));
WriteVarint(static_cast<uint32_t>(view.byte_length()));
WriteVarint(static_cast<uint32_t>(view.bit_field()));
uint32_t flags =
JSArrayBufferViewIsLengthTracking::encode(view.is_length_tracking()) |
JSArrayBufferViewIsBackedByRab::encode(view.is_backed_by_rab());
WriteVarint(flags);
return ThrowIfOutOfMemory();
}
@ -2003,7 +2019,7 @@ MaybeHandle<JSArrayBuffer> ValueDeserializer::ReadTransferredJSArrayBuffer() {
MaybeHandle<JSArrayBufferView> ValueDeserializer::ReadJSArrayBufferView(
Handle<JSArrayBuffer> buffer) {
uint32_t buffer_byte_length = static_cast<uint32_t>(buffer->byte_length());
uint32_t buffer_byte_length = static_cast<uint32_t>(buffer->GetByteLength());
uint8_t tag = 0;
uint32_t byte_offset = 0;
uint32_t byte_length = 0;
@ -2028,7 +2044,9 @@ MaybeHandle<JSArrayBufferView> ValueDeserializer::ReadJSArrayBufferView(
Handle<JSDataView> data_view =
isolate_->factory()->NewJSDataView(buffer, byte_offset, byte_length);
AddObjectWithID(id, data_view);
data_view->set_bit_field(flags);
if (!ValidateAndSetJSArrayBufferViewFlags(*data_view, *buffer, flags)) {
return MaybeHandle<JSArrayBufferView>();
}
return data_view;
}
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
@ -2045,11 +2063,39 @@ MaybeHandle<JSArrayBufferView> ValueDeserializer::ReadJSArrayBufferView(
}
Handle<JSTypedArray> typed_array = isolate_->factory()->NewJSTypedArray(
external_array_type, buffer, byte_offset, byte_length / element_size);
typed_array->set_bit_field(flags);
if (!ValidateAndSetJSArrayBufferViewFlags(*typed_array, *buffer, flags)) {
return MaybeHandle<JSArrayBufferView>();
}
AddObjectWithID(id, typed_array);
return typed_array;
}
bool ValueDeserializer::ValidateAndSetJSArrayBufferViewFlags(
JSArrayBufferView view, JSArrayBuffer buffer, uint32_t serialized_flags) {
bool is_length_tracking =
JSArrayBufferViewIsLengthTracking::decode(serialized_flags);
bool is_backed_by_rab =
JSArrayBufferViewIsBackedByRab::decode(serialized_flags);
// TODO(marja): When the version number is bumped the next time, check that
// serialized_flags doesn't contain spurious 1-bits.
if (is_backed_by_rab || is_length_tracking) {
if (!FLAG_harmony_rab_gsab) {
return false;
}
if (!buffer.is_resizable()) {
return false;
}
if (is_backed_by_rab && buffer.is_shared()) {
return false;
}
}
view.set_is_length_tracking(is_length_tracking);
view.set_is_backed_by_rab(is_backed_by_rab);
return true;
}
MaybeHandle<Object> ValueDeserializer::ReadJSError() {
uint32_t id = next_id_++;

View File

@ -300,6 +300,9 @@ class ValueDeserializer {
V8_WARN_UNUSED_RESULT;
MaybeHandle<JSArrayBufferView> ReadJSArrayBufferView(
Handle<JSArrayBuffer> buffer) V8_WARN_UNUSED_RESULT;
bool ValidateAndSetJSArrayBufferViewFlags(
JSArrayBufferView view, JSArrayBuffer buffer,
uint32_t serialized_flags) V8_WARN_UNUSED_RESULT;
MaybeHandle<Object> ReadJSError() V8_WARN_UNUSED_RESULT;
#if V8_ENABLE_WEBASSEMBLY
MaybeHandle<JSObject> ReadWasmModuleTransfer() V8_WARN_UNUSED_RESULT;

View File

@ -0,0 +1,17 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-rab-gsab
"use strict";
(function FlagMismatch() {
// Length tracking TA, buffer not resizable.
const data1 = new Uint8Array([255, 15, 66, 4, 3, 5, 7, 11, 86, 66, 1, 2, 1]);
assertThrows(() => { d8.serializer.deserialize(data1.buffer); });
// RAB backed TA, buffer not resizable.
const data2 = new Uint8Array([255, 15, 66, 4, 3, 5, 7, 11, 86, 66, 1, 2, 2]);
assertThrows(() => { d8.serializer.deserialize(data2.buffer); });
})();