From 79978063b9954efcb9a8106c83b4128ea85028c1 Mon Sep 17 00:00:00 2001 From: Kenneth Myhra Date: Sat, 2 Mar 2024 20:39:45 +0100 Subject: [PATCH] LibWeb: Extract deserialization logic for {,object} primitives To avoid differing logic for deserializing similar types, move the logic into separate helpers. Also, adds security checks like VERIFY to avoid reading past the end of the serialized data. If we try to read past the end of the serialized data, either our program logic is wrong or our serialized data has somehow been corrupted. Therefore, at least currently, it is better to crash by VERIFYing. --- .../LibWeb/HTML/StructuredSerialize.cpp | 97 +++++++++++++------ .../LibWeb/HTML/StructuredSerialize.h | 9 ++ 2 files changed, 76 insertions(+), 30 deletions(-) diff --git a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp index 84811b897f..90439b0792 100644 --- a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp +++ b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp @@ -701,16 +701,12 @@ public: break; } case ValueTag::BooleanPrimitive: { - value = JS::Value(static_cast(m_serialized[m_position++])); + value = JS::Value { deserialize_boolean_primitive(m_serialized, m_position) }; is_primitive = true; break; } case ValueTag::NumberPrimitive: { - u32 bits[2] = {}; - bits[0] = m_serialized[m_position++]; - bits[1] = m_serialized[m_position++]; - auto double_value = *bit_cast(&bits); - value = JS::Value(double_value); + value = JS::Value { deserialize_number_primitive(m_serialized, m_position) }; is_primitive = true; break; } @@ -728,51 +724,33 @@ public: } // 6. Otherwise, if serialized.[[Type]] is "Boolean", then set value to a new Boolean object in targetRealm whose [[BooleanData]] internal slot value is serialized.[[BooleanData]]. case BooleanObject: { - auto* realm = m_vm.current_realm(); - auto bool_value = static_cast(m_serialized[m_position++]); - value = JS::BooleanObject::create(*realm, bool_value); + value = deserialize_boolean_object(*m_vm.current_realm(), m_serialized, m_position); break; } // 7. Otherwise, if serialized.[[Type]] is "Number", then set value to a new Number object in targetRealm whose [[NumberData]] internal slot value is serialized.[[NumberData]]. case ValueTag::NumberObject: { - auto* realm = m_vm.current_realm(); - u32 bits[2]; - bits[0] = m_serialized[m_position++]; - bits[1] = m_serialized[m_position++]; - auto double_value = *bit_cast(&bits); - value = JS::NumberObject::create(*realm, double_value); + value = deserialize_number_object(*m_vm.current_realm(), m_serialized, m_position); break; } // 8. Otherwise, if serialized.[[Type]] is "BigInt", then set value to a new BigInt object in targetRealm whose [[BigIntData]] internal slot value is serialized.[[BigIntData]]. case ValueTag::BigIntObject: { - auto* realm = m_vm.current_realm(); - auto big_int = TRY(deserialize_big_int_primitive(m_vm, m_serialized, m_position)); - value = JS::BigIntObject::create(*realm, big_int); + value = TRY(deserialize_big_int_object(*m_vm.current_realm(), m_serialized, m_position)); break; } // 9. Otherwise, if serialized.[[Type]] is "String", then set value to a new String object in targetRealm whose [[StringData]] internal slot value is serialized.[[StringData]]. case ValueTag::StringObject: { - auto* realm = m_vm.current_realm(); - auto string = TRY(deserialize_string_primitive(m_vm, m_serialized, m_position)); - value = JS::StringObject::create(*realm, string, realm->intrinsics().string_prototype()); + value = TRY(deserialize_string_object(*m_vm.current_realm(), m_serialized, m_position)); break; } // 10. Otherwise, if serialized.[[Type]] is "Date", then set value to a new Date object in targetRealm whose [[DateValue]] internal slot value is serialized.[[DateValue]]. case ValueTag::DateObject: { - auto* realm = m_vm.current_realm(); - u32 bits[2]; - bits[0] = m_serialized[m_position++]; - bits[1] = m_serialized[m_position++]; - auto double_value = *bit_cast(&bits); - value = JS::Date::create(*realm, double_value); + value = deserialize_date_object(*m_vm.current_realm(), m_serialized, m_position); break; } // 11. Otherwise, if serialized.[[Type]] is "RegExp", then set value to a new RegExp object in targetRealm whose [[RegExpMatcher]] internal slot value is serialized.[[RegExpMatcher]], // whose [[OriginalSource]] internal slot value is serialized.[[OriginalSource]], and whose [[OriginalFlags]] internal slot value is serialized.[[OriginalFlags]]. case ValueTag::RegExpObject: { - auto pattern = TRY(deserialize_string_primitive(m_vm, m_serialized, m_position)); - auto flags = TRY(deserialize_string_primitive(m_vm, m_serialized, m_position)); - value = TRY(JS::regexp_create(m_vm, move(pattern), move(flags))); + value = TRY(deserialize_reg_exp_object(*m_vm.current_realm(), m_serialized, m_position)); break; } // FIXME: 12. Otherwise, if serialized.[[Type]] is "SharedArrayBuffer", then: @@ -1008,8 +986,67 @@ private: } }; +bool deserialize_boolean_primitive(ReadonlySpan const& serialized, size_t& position) +{ + VERIFY(position < serialized.size()); + return static_cast(serialized[position++]); +} + +double deserialize_number_primitive(ReadonlySpan const& serialized, size_t& position) +{ + VERIFY(position + 2 <= serialized.size()); + u32 const bits[2] { + serialized[position++], + serialized[position++], + }; + return *bit_cast(&bits); +} + +JS::NonnullGCPtr deserialize_boolean_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position) +{ + auto boolean_primitive = deserialize_boolean_primitive(serialized, position); + return JS::BooleanObject::create(realm, boolean_primitive); +} + +JS::NonnullGCPtr deserialize_number_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position) +{ + auto number_primitive = deserialize_number_primitive(serialized, position); + return JS::NumberObject::create(realm, number_primitive); +} + +WebIDL::ExceptionOr> deserialize_big_int_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position) +{ + auto big_int_primitive = TRY(deserialize_big_int_primitive(realm.vm(), serialized, position)); + return JS::BigIntObject::create(realm, big_int_primitive); +} + +WebIDL::ExceptionOr> deserialize_string_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position) +{ + auto string_primitive = TRY(deserialize_string_primitive(realm.vm(), serialized, position)); + return JS::StringObject::create(realm, string_primitive, realm.intrinsics().string_prototype()); +} + +JS::NonnullGCPtr deserialize_date_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position) +{ + VERIFY(position + 2 <= serialized.size()); + u32 const bits[2] { + serialized[position++], + serialized[position++], + }; + auto double_value = *bit_cast(&bits); + return JS::Date::create(realm, double_value); +} + +WebIDL::ExceptionOr> deserialize_reg_exp_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position) +{ + auto pattern = TRY(deserialize_string_primitive(realm.vm(), serialized, position)); + auto flags = TRY(deserialize_string_primitive(realm.vm(), serialized, position)); + return TRY(JS::regexp_create(realm.vm(), move(pattern), move(flags))); +} + WebIDL::ExceptionOr deserialize_bytes(JS::VM& vm, ReadonlySpan vector, size_t& position) { + VERIFY(position + 2 <= vector.size()); u32 size_bits[2]; size_bits[0] = vector[position++]; size_bits[1] = vector[position++]; diff --git a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.h b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.h index dc0b5c45e4..74f549cd6e 100644 --- a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.h +++ b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.h @@ -70,6 +70,15 @@ WebIDL::ExceptionOr serialize_array_buffer(JS::VM& vm, Vector& vector template ViewType> WebIDL::ExceptionOr serialize_viewed_array_buffer(JS::VM& vm, Vector& vector, ViewType const& view, bool for_storage, SerializationMemory& memory); +bool deserialize_boolean_primitive(ReadonlySpan const& serialized, size_t& position); +double deserialize_number_primitive(ReadonlySpan const& serialized, size_t& position); +JS::NonnullGCPtr deserialize_boolean_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position); +JS::NonnullGCPtr deserialize_number_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position); +WebIDL::ExceptionOr> deserialize_big_int_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position); +WebIDL::ExceptionOr> deserialize_string_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position); +JS::NonnullGCPtr deserialize_date_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position); +WebIDL::ExceptionOr> deserialize_reg_exp_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position); + WebIDL::ExceptionOr deserialize_bytes(JS::VM& vm, ReadonlySpan vector, size_t& position); WebIDL::ExceptionOr deserialize_string(JS::VM& vm, ReadonlySpan vector, size_t& position); WebIDL::ExceptionOr> deserialize_string_primitive(JS::VM& vm, ReadonlySpan vector, size_t& position);