mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-01 13:49:16 +00:00
LibJS: Fix pointer authentication failure in TypedArray
`TypedArray` objects need to know their own constructor objects to allow copying. This was implemented by storing a function pointer to the `Intrinsic` object's method which returns the constructor object. The problem is that function pointers aren't polymorphic, we can't legally just cast e.g. a `Derived* (*ptr)(void)` to `Base* (*ptr)(void)` (this is why the code needed a `bit_cast` to even compile). But this wasn't actually a problem in practice because their ABIs were the same. But with pointer authentication (Apple's `arm64e` ABI) this signature mismatch becomes a hard failure and crashes the process. Fix this by adding a virtual function that returns the intrinsic constructor (actually, a `NativeFunction`, as typed arrays constructors don't inherit from the base `TypedArray` constructor) instead of the function pointer. With this, test-js passes and Ladybird launches correctly when built (with a lot of vcpkg hacks) for arm64e.
This commit is contained in:
parent
460b257449
commit
edeef940b6
Notes:
github-actions[bot]
2025-06-17 21:46:01 +00:00
Author: https://github.com/BertalanD
Commit: edeef940b6
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5116
Reviewed-by: https://github.com/ADKaster ✅
3 changed files with 14 additions and 13 deletions
|
@ -351,7 +351,7 @@ ThrowCompletionOr<TypedArrayBase*> typed_array_create_same_type(VM& vm, TypedArr
|
|||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Let constructor be the intrinsic object associated with the constructor name exemplar.[[TypedArrayName]] in Table 68.
|
||||
auto constructor = (realm.intrinsics().*exemplar.intrinsic_constructor())();
|
||||
auto constructor = exemplar.intrinsic_constructor(realm);
|
||||
|
||||
// 2. Let result be ? TypedArrayCreate(constructor, argumentList).
|
||||
auto* result = TRY(typed_array_create(vm, constructor, move(arguments)));
|
||||
|
@ -468,9 +468,7 @@ void TypedArrayBase::visit_edges(Visitor& visitor)
|
|||
} \
|
||||
\
|
||||
ClassName::ClassName(Object& prototype, u32 length, ArrayBuffer& array_buffer) \
|
||||
: TypedArray(prototype, \
|
||||
bit_cast<TypedArrayBase::IntrinsicConstructor>(&Intrinsics::snake_name##_constructor), \
|
||||
length, array_buffer, Kind::ClassName) \
|
||||
: TypedArray(prototype, length, array_buffer, Kind::ClassName) \
|
||||
{ \
|
||||
if constexpr (#ClassName##sv.is_one_of("BigInt64Array", "BigUint64Array")) \
|
||||
m_content_type = ContentType::BigInt; \
|
||||
|
@ -487,6 +485,11 @@ void TypedArrayBase::visit_edges(Visitor& visitor)
|
|||
return vm().names.ClassName.as_string(); \
|
||||
} \
|
||||
\
|
||||
GC::Ref<NativeFunction> ClassName::intrinsic_constructor(Realm& realm) const \
|
||||
{ \
|
||||
return realm.intrinsics().snake_name##_constructor(); \
|
||||
} \
|
||||
\
|
||||
PrototypeName::PrototypeName(Object& prototype) \
|
||||
: Object(ConstructWithPrototypeTag::Tag, prototype, MayInterfereWithIndexedPropertyAccess::Yes) \
|
||||
{ \
|
||||
|
|
|
@ -35,14 +35,11 @@ public:
|
|||
#undef __JS_ENUMERATE
|
||||
};
|
||||
|
||||
using IntrinsicConstructor = GC::Ref<TypedArrayConstructor> (Intrinsics::*)();
|
||||
|
||||
ByteLength const& array_length() const { return m_array_length; }
|
||||
ByteLength const& byte_length() const { return m_byte_length; }
|
||||
u32 byte_offset() const { return m_byte_offset; }
|
||||
ContentType content_type() const { return m_content_type; }
|
||||
ArrayBuffer* viewed_array_buffer() const { return m_viewed_array_buffer; }
|
||||
IntrinsicConstructor intrinsic_constructor() const { return m_intrinsic_constructor; }
|
||||
|
||||
void set_array_length(ByteLength length) { m_array_length = move(length); }
|
||||
void set_byte_length(ByteLength length) { m_byte_length = move(length); }
|
||||
|
@ -65,12 +62,13 @@ public:
|
|||
// 25.1.3.19 GetModifySetValueInBuffer ( arrayBuffer, byteIndex, type, value, op ), https://tc39.es/ecma262/#sec-getmodifysetvalueinbuffer
|
||||
virtual Value get_modify_set_value_in_buffer(size_t byte_index, Value value, ReadWriteModifyFunction operation, bool is_little_endian = true) = 0;
|
||||
|
||||
virtual GC::Ref<NativeFunction> intrinsic_constructor(Realm&) const = 0;
|
||||
|
||||
protected:
|
||||
TypedArrayBase(Object& prototype, IntrinsicConstructor intrinsic_constructor, Kind kind, u32 element_size)
|
||||
TypedArrayBase(Object& prototype, Kind kind, u32 element_size)
|
||||
: Object(ConstructWithPrototypeTag::Tag, prototype, MayInterfereWithIndexedPropertyAccess::Yes)
|
||||
, m_element_size(element_size)
|
||||
, m_kind(kind)
|
||||
, m_intrinsic_constructor(intrinsic_constructor)
|
||||
{
|
||||
set_is_typed_array();
|
||||
}
|
||||
|
@ -82,7 +80,6 @@ protected:
|
|||
ContentType m_content_type { ContentType::Number };
|
||||
Kind m_kind {};
|
||||
GC::Ptr<ArrayBuffer> m_viewed_array_buffer;
|
||||
IntrinsicConstructor m_intrinsic_constructor { nullptr };
|
||||
|
||||
private:
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
@ -498,8 +495,8 @@ public:
|
|||
Value get_modify_set_value_in_buffer(size_t byte_index, Value value, ReadWriteModifyFunction operation, bool is_little_endian = true) override { return viewed_array_buffer()->template get_modify_set_value<T>(byte_index, value, move(operation), is_little_endian); }
|
||||
|
||||
protected:
|
||||
TypedArray(Object& prototype, IntrinsicConstructor intrinsic_constructor, u32 array_length, ArrayBuffer& array_buffer, Kind kind)
|
||||
: TypedArrayBase(prototype, intrinsic_constructor, kind, sizeof(UnderlyingBufferDataType))
|
||||
TypedArray(Object& prototype, u32 array_length, ArrayBuffer& array_buffer, Kind kind)
|
||||
: TypedArrayBase(prototype, kind, sizeof(UnderlyingBufferDataType))
|
||||
{
|
||||
VERIFY(!Checked<u32>::multiplication_would_overflow(array_length, sizeof(UnderlyingBufferDataType)));
|
||||
m_viewed_array_buffer = &array_buffer;
|
||||
|
@ -527,6 +524,7 @@ ThrowCompletionOr<double> compare_typed_array_elements(VM&, Value x, Value y, Fu
|
|||
static ThrowCompletionOr<GC::Ref<ClassName>> create(Realm&, u32 length); \
|
||||
static GC::Ref<ClassName> create(Realm&, u32 length, ArrayBuffer& buffer); \
|
||||
virtual FlyString const& element_name() const override; \
|
||||
virtual GC::Ref<NativeFunction> intrinsic_constructor(Realm&) const override; \
|
||||
\
|
||||
protected: \
|
||||
ClassName(Object& prototype, u32 length, ArrayBuffer& array_buffer); \
|
||||
|
|
|
@ -97,7 +97,7 @@ static ThrowCompletionOr<TypedArrayBase*> typed_array_species_create(VM& vm, Typ
|
|||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Let defaultConstructor be the intrinsic object listed in column one of Table 72 for exemplar.[[TypedArrayName]].
|
||||
auto default_constructor = (realm.intrinsics().*exemplar.intrinsic_constructor())();
|
||||
auto default_constructor = exemplar.intrinsic_constructor(realm);
|
||||
|
||||
// 2. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
|
||||
auto* constructor = TRY(species_constructor(vm, exemplar, *default_constructor));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue