Fix: Cannot deserialize an object previously serialized by v8

977 views
Skip to first unread message

Lei Wang

unread,
Sep 18, 2021, 4:33:10 AM9/18/21
to v8-dev
Hi all,

This issue comes from https://github.com/nodejs/node/issues/40059

As I commented in that thread, currently I can make serializable objects from requiring buffer size 2GB to 4GB (the max buffer size allowed, defined by `v8::TypedArray::kMaxLength`), and fix issue that some objects can be serialized but not be deserialized. Now all objects requiring buffers less than 4GB could be serialized and deserialized.

I'm still downloading XCode. Before done testing and submit PR, I'm glad to paste my code to get some advices. Thank you!

```diff
diff --git a/src/api/api.cc b/src/api/api.cc
index 59bd76c154..82c54c8e10 100644
--- a/src/api/api.cc
+++ b/src/api/api.cc
@@ -3380,10 +3380,10 @@ ValueDeserializer::ValueDeserializer(Isolate* isolate, const uint8_t* data,

 ValueDeserializer::ValueDeserializer(Isolate* isolate, const uint8_t* data,
                                      size_t size, Delegate* delegate) {
-  if (base::IsValueInRangeForNumericType<int>(size)) {
+  if (base::IsValueInRangeForNumericType<size_t>(size)) {
     private_ = new PrivateData(
         reinterpret_cast<i::Isolate*>(isolate),
-        base::Vector<const uint8_t>(data, static_cast<int>(size)), delegate);
+        base::Vector<const uint8_t>(data, static_cast<size_t>(size)), delegate);
   } else {
     private_ =
         new PrivateData(reinterpret_cast<i::Isolate*>(isolate),
diff --git a/src/common/message-template.h b/src/common/message-template.h
index a925300c5c..6da25ccec5 100644
--- a/src/common/message-template.h
+++ b/src/common/message-template.h
@@ -602,6 +602,8 @@ namespace internal {
   T(DataCloneErrorSharedArrayBufferTransferred,                                \
     "A SharedArrayBuffer could not be cloned. SharedArrayBuffer must not be "  \
     "transferred.")                                                            \
+  T(DataCloneErrorObjectTooLarge,                                              \
+    "Unable to serialize object, requested buffer too large.")                 \
   T(DataCloneDeserializationError, "Unable to deserialize cloned data.")       \
   T(DataCloneDeserializationVersionError,                                      \
     "Unable to deserialize cloned data due to invalid or unsupported "         \
diff --git a/src/objects/value-serializer.cc b/src/objects/value-serializer.cc
index a84cf4e2c4..6c0c6f0a62 100644
--- a/src/objects/value-serializer.cc
+++ b/src/objects/value-serializer.cc
@@ -359,6 +359,10 @@ Maybe<bool> ValueSerializer::ExpandBuffer(size_t required_capacity) {
       std::max(required_capacity, buffer_capacity_ * 2) + 64;
   size_t provided_capacity = 0;
   void* new_buffer = nullptr;
+  if (requested_capacity > v8::TypedArray::kMaxLength) {
+    object_too_large_ = true;
+    return Nothing<bool>();
+  }
   if (delegate_) {
     new_buffer = delegate_->ReallocateBufferMemory(buffer_, requested_capacity,
                                                    &provided_capacity);
@@ -401,6 +405,9 @@ void ValueSerializer::TransferArrayBuffer(uint32_t transfer_id,
 }

 Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) {
+  // Length of node::Buffer should be no larger than TypedArray::kMaxLength.
+  if (V8_UNLIKELY(object_too_large_)) return ThrowIfObjectTooLarge();
+
   // There is no sense in trying to proceed if we've previously run out of
   // memory. Bail immediately, as this likely implies that some write has
   // previously failed and so the buffer is corrupt.
@@ -1099,6 +1106,14 @@ Maybe<bool> ValueSerializer::ThrowIfOutOfMemory() {
   return Just(true);
 }

+Maybe<bool> ValueSerializer::ThrowIfObjectTooLarge() {
+  if (object_too_large_) {
+    ThrowDataCloneError(MessageTemplate::kDataCloneErrorObjectTooLarge);
+    return Nothing<bool>();
+  }
+  return Just(true);
+}
+
 void ValueSerializer::ThrowDataCloneError(MessageTemplate index,
                                           Handle<Object> arg0) {
   Handle<String> message = MessageFormatter::Format(isolate_, index, arg0);
@@ -1119,7 +1134,7 @@ ValueDeserializer::ValueDeserializer(Isolate* isolate,
     : isolate_(isolate),
       delegate_(delegate),
       position_(data.begin()),
-      end_(data.begin() + data.length()),
+      end_(data.begin() + data.size()),
       id_map_(isolate->global_handles()->Create(
           ReadOnlyRoots(isolate_).empty_fixed_array())) {}

diff --git a/src/objects/value-serializer.h b/src/objects/value-serializer.h
index c6363e67c6..88a58ab419 100644
--- a/src/objects/value-serializer.h
+++ b/src/objects/value-serializer.h
@@ -157,6 +157,7 @@ class ValueSerializer {
                                        Handle<Object> arg0);

   Maybe<bool> ThrowIfOutOfMemory();
+  Maybe<bool> ThrowIfObjectTooLarge();

   Isolate* const isolate_;
   v8::ValueSerializer::Delegate* const delegate_;
@@ -165,6 +166,7 @@ class ValueSerializer {
   size_t buffer_capacity_ = 0;
   bool treat_array_buffer_views_as_host_objects_ = false;
   bool out_of_memory_ = false;
+  bool object_too_large_ = false;
   Zone zone_;

   // To avoid extra lookups in the identity map, ID+1 is actually stored in the
```

Reply all
Reply to author
Forward
0 new messages