Commit 9b3f4e00 authored by Shu-yu Guo's avatar Shu-yu Guo Committed by V8 LUCI CQ

[shared-struct] Be lenient deserializing shared objects

ValueDeserializer should be lenient and not assume correct usage when
deserializing shared objects. This CL makes ValueDeserializer throw if
a shared object conveyor id or shared object id are not found.

Bug: v8:12547, chromium:1359227
Change-Id: I429a37dfadd95e42edca5d4870eb5188cb013bc7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3872549Reviewed-by: 's avatarDominik Inführ <dinfuehr@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83004}
parent 5d456727
...@@ -2002,8 +2002,11 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { ...@@ -2002,8 +2002,11 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
SharedObjectConveyors* GetSharedObjectConveyors() const { SharedObjectConveyors* GetSharedObjectConveyors() const {
if (is_shared()) return shared_object_conveyors_.get(); if (is_shared()) return shared_object_conveyors_.get();
if (shared_isolate()) {
return shared_isolate()->shared_object_conveyors_.get(); return shared_isolate()->shared_object_conveyors_.get();
} }
return nullptr;
}
bool log_object_relocation() const { return log_object_relocation_; } bool log_object_relocation() const { return log_object_relocation_; }
......
...@@ -21,7 +21,7 @@ uint32_t SharedObjectConveyorHandles::Persist(HeapObject shared_object) { ...@@ -21,7 +21,7 @@ uint32_t SharedObjectConveyorHandles::Persist(HeapObject shared_object) {
} }
HeapObject SharedObjectConveyorHandles::GetPersisted(uint32_t object_id) { HeapObject SharedObjectConveyorHandles::GetPersisted(uint32_t object_id) {
DCHECK_LT(object_id, shared_objects_.size()); DCHECK(HasPersisted(object_id));
return *shared_objects_[object_id]; return *shared_objects_[object_id];
} }
...@@ -55,23 +55,23 @@ SharedObjectConveyorHandles* SharedObjectConveyors::NewConveyor() { ...@@ -55,23 +55,23 @@ SharedObjectConveyorHandles* SharedObjectConveyors::NewConveyor() {
return conveyors_[id].get(); return conveyors_[id].get();
} }
SharedObjectConveyorHandles* SharedObjectConveyors::GetConveyor( SharedObjectConveyorHandles* SharedObjectConveyors::MaybeGetConveyor(
uint32_t conveyor_id) { uint32_t conveyor_id) {
base::MutexGuard guard(&conveyors_mutex_); base::MutexGuard guard(&conveyors_mutex_);
DcheckIsValidConveyorId(conveyor_id); if (HasConveyor(conveyor_id)) return conveyors_[conveyor_id].get();
return conveyors_[conveyor_id].get(); return nullptr;
} }
void SharedObjectConveyors::DeleteConveyor(uint32_t conveyor_id) { void SharedObjectConveyors::DeleteConveyor(uint32_t conveyor_id) {
base::MutexGuard guard(&conveyors_mutex_); base::MutexGuard guard(&conveyors_mutex_);
DcheckIsValidConveyorId(conveyor_id); DCHECK(HasConveyor(conveyor_id));
conveyors_[conveyor_id].reset(nullptr); conveyors_[conveyor_id].reset(nullptr);
} }
void SharedObjectConveyors::DcheckIsValidConveyorId(uint32_t conveyor_id) { bool SharedObjectConveyors::HasConveyor(uint32_t conveyor_id) const {
DCHECK_LT(conveyor_id, conveyors_.size()); return conveyor_id < conveyors_.size() &&
DCHECK_NOT_NULL(conveyors_[conveyor_id].get()); conveyors_[conveyor_id] != nullptr &&
DCHECK_EQ(conveyors_[conveyor_id]->id, conveyor_id); conveyors_[conveyor_id]->id == conveyor_id;
} }
} // namespace internal } // namespace internal
......
...@@ -37,11 +37,14 @@ class SharedObjectConveyorHandles { ...@@ -37,11 +37,14 @@ class SharedObjectConveyorHandles {
SharedObjectConveyorHandles& operator=(const SharedObjectConveyorHandles&) = SharedObjectConveyorHandles& operator=(const SharedObjectConveyorHandles&) =
delete; delete;
// Persist and GetPersisted are not threadsafe. A particular conveyor is used // Persist, GetPersisted, and HasPersisted are not threadsafe. A particular
// by a single thread at a time, either during sending a message or receiving // conveyor is used by a single thread at a time, either during sending a
// a message. // message or receiving a message.
uint32_t Persist(HeapObject shared_object); uint32_t Persist(HeapObject shared_object);
HeapObject GetPersisted(uint32_t object_id); HeapObject GetPersisted(uint32_t object_id);
bool HasPersisted(uint32_t object_id) const {
return object_id < shared_objects_.size();
}
// Deleting conveyors is threadsafe and may be called from multiple threads. // Deleting conveyors is threadsafe and may be called from multiple threads.
void Delete(); void Delete();
...@@ -60,15 +63,14 @@ class SharedObjectConveyors { ...@@ -60,15 +63,14 @@ class SharedObjectConveyors {
explicit SharedObjectConveyors(Isolate* isolate) : isolate_(isolate) {} explicit SharedObjectConveyors(Isolate* isolate) : isolate_(isolate) {}
SharedObjectConveyorHandles* NewConveyor(); SharedObjectConveyorHandles* NewConveyor();
SharedObjectConveyorHandles* GetConveyor(uint32_t conveyor_id); SharedObjectConveyorHandles* MaybeGetConveyor(uint32_t conveyor_id);
private: private:
friend class SharedObjectConveyorHandles; friend class SharedObjectConveyorHandles;
bool HasConveyor(uint32_t conveyor_id) const;
void DeleteConveyor(uint32_t conveyor_id); void DeleteConveyor(uint32_t conveyor_id);
void DcheckIsValidConveyorId(uint32_t conveyor_id);
Isolate* isolate_; Isolate* isolate_;
base::Mutex conveyors_mutex_; base::Mutex conveyors_mutex_;
std::vector<std::unique_ptr<SharedObjectConveyorHandles>> conveyors_; std::vector<std::unique_ptr<SharedObjectConveyorHandles>> conveyors_;
......
...@@ -2256,6 +2256,20 @@ MaybeHandle<WasmMemoryObject> ValueDeserializer::ReadWasmMemory() { ...@@ -2256,6 +2256,20 @@ MaybeHandle<WasmMemoryObject> ValueDeserializer::ReadWasmMemory() {
} }
#endif // V8_ENABLE_WEBASSEMBLY #endif // V8_ENABLE_WEBASSEMBLY
namespace {
// Throws a generic "deserialization failed" exception by default, unless a more
// specific exception has already been thrown.
void ThrowDeserializationExceptionIfNonePending(Isolate* isolate) {
if (!isolate->has_pending_exception()) {
isolate->Throw(*isolate->factory()->NewError(
MessageTemplate::kDataCloneDeserializationError));
}
DCHECK(isolate->has_pending_exception());
}
} // namespace
MaybeHandle<HeapObject> ValueDeserializer::ReadSharedObject() { MaybeHandle<HeapObject> ValueDeserializer::ReadSharedObject() {
STACK_CHECK(isolate_, MaybeHandle<HeapObject>()); STACK_CHECK(isolate_, MaybeHandle<HeapObject>());
DCHECK_GE(version_, 15); DCHECK_GE(version_, 15);
...@@ -2265,9 +2279,15 @@ MaybeHandle<HeapObject> ValueDeserializer::ReadSharedObject() { ...@@ -2265,9 +2279,15 @@ MaybeHandle<HeapObject> ValueDeserializer::ReadSharedObject() {
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate_, HeapObject); RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate_, HeapObject);
return MaybeHandle<HeapObject>(); return MaybeHandle<HeapObject>();
} }
// The conveyor must have already been gotten via the kSharedObjectConveyor // The conveyor must have already been gotten via the kSharedObjectConveyor
// tag. // tag.
DCHECK_NOT_NULL(shared_object_conveyor_); if (shared_object_conveyor_ == nullptr ||
!shared_object_conveyor_->HasPersisted(shared_object_id)) {
ThrowDeserializationExceptionIfNonePending(isolate_);
return MaybeHandle<HeapObject>();
}
Handle<HeapObject> shared_object( Handle<HeapObject> shared_object(
shared_object_conveyor_->GetPersisted(shared_object_id), isolate_); shared_object_conveyor_->GetPersisted(shared_object_id), isolate_);
DCHECK(shared_object->IsShared()); DCHECK(shared_object->IsShared());
...@@ -2277,15 +2297,27 @@ MaybeHandle<HeapObject> ValueDeserializer::ReadSharedObject() { ...@@ -2277,15 +2297,27 @@ MaybeHandle<HeapObject> ValueDeserializer::ReadSharedObject() {
bool ValueDeserializer::ReadSharedObjectConveyor() { bool ValueDeserializer::ReadSharedObjectConveyor() {
STACK_CHECK(isolate_, false); STACK_CHECK(isolate_, false);
DCHECK_GE(version_, 15); DCHECK_GE(version_, 15);
// This tag appears at most once per deserialization data.
DCHECK_NULL(shared_object_conveyor_);
uint32_t conveyor_id; uint32_t conveyor_id;
if (!ReadVarint<uint32_t>().To(&conveyor_id)) { if (!ReadVarint<uint32_t>().To(&conveyor_id)) {
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate_, false); RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate_, false);
return false; return false;
} }
// This tag appears at most once per deserialization data.
if (shared_object_conveyor_ != nullptr ||
isolate_->GetSharedObjectConveyors() == nullptr) {
ThrowDeserializationExceptionIfNonePending(isolate_);
return false;
}
shared_object_conveyor_ = shared_object_conveyor_ =
isolate_->GetSharedObjectConveyors()->GetConveyor(conveyor_id); isolate_->GetSharedObjectConveyors()->MaybeGetConveyor(conveyor_id);
if (shared_object_conveyor_ == nullptr) {
ThrowDeserializationExceptionIfNonePending(isolate_);
return false;
}
return true; return true;
} }
...@@ -2521,20 +2553,6 @@ static Maybe<bool> SetPropertiesFromKeyValuePairs(Isolate* isolate, ...@@ -2521,20 +2553,6 @@ static Maybe<bool> SetPropertiesFromKeyValuePairs(Isolate* isolate,
return Just(true); return Just(true);
} }
namespace {
// Throws a generic "deserialization failed" exception by default, unless a more
// specific exception has already been thrown.
void ThrowDeserializationExceptionIfNonePending(Isolate* isolate) {
if (!isolate->has_pending_exception()) {
isolate->Throw(*isolate->factory()->NewError(
MessageTemplate::kDataCloneDeserializationError));
}
DCHECK(isolate->has_pending_exception());
}
} // namespace
MaybeHandle<Object> MaybeHandle<Object>
ValueDeserializer::ReadObjectUsingEntireBufferForLegacyFormat() { ValueDeserializer::ReadObjectUsingEntireBufferForLegacyFormat() {
DCHECK_EQ(version_, 0u); DCHECK_EQ(version_, 0u);
......
// 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.
// The value deserializer should be lenient of malformed or malicious data and
// should throw instead of crash on non-existent shared objects in serialized
// data.
(function Conveyor() {
// Conveyor is 'q', ASCII 113.
const data = new Uint8Array([255, 15, 113, 0]);
assertThrows(() => { d8.serializer.deserialize(data.buffer); });
})();
(function SharedObjectNoConveyor() {
// Shared object is 'p', ASCII 112.
const data = new Uint8Array([255, 15, 112, 0]);
assertThrows(() => { d8.serializer.deserialize(data.buffer); });
})();
(function SharedObjectAndConveyor() {
const data = new Uint8Array([255, 15, 113, 0, 112, 0]);
assertThrows(() => { d8.serializer.deserialize(data.buffer); });
})();
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