1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 02:27:43 +00:00

AK+LibJS: Handle NaN-boxing pointers on AArch64

JS::Value stores 48 bit pointers to separately allocated objects in its
payload. On x86-64, canonical addresses have their top 16 bits set to
the same value as bit 47, effectively meaning that the value has to be
sign-extended to get the pointer. AArch64, however, expects the topmost
bits to be all zeros.

This commit gates sign extension behind `#if ARCH(X86_64)`, and adds an
`#error` for unsupported architectures, so that we do not forget to
think about pointer handling when porting to a new architecture.

Fixes #15290
Fixes SerenityOS/ladybird#56
This commit is contained in:
Daniel Bertalan 2022-09-20 18:09:33 +02:00 committed by Andreas Kling
parent 62fed2a31d
commit 2b69af2dfe
4 changed files with 42 additions and 24 deletions

View file

@ -398,6 +398,24 @@ public:
template<typename... Args>
[[nodiscard]] ALWAYS_INLINE ThrowCompletionOr<Value> invoke(VM&, PropertyKey const& property_key, Args... args);
static constexpr FlatPtr extract_pointer_bits(u64 encoded)
{
#ifdef AK_ARCH_32_BIT
// For 32-bit system the pointer fully fits so we can just return it directly.
static_assert(sizeof(void*) == sizeof(u32));
return static_cast<FlatPtr>(encoded & 0xffff'ffff);
#elif ARCH(X86_64)
// For x86_64 the top 16 bits should be sign extending the "real" top bit (47th).
// So first shift the top 16 bits away then using the right shift it sign extends the top 16 bits.
return static_cast<FlatPtr>((static_cast<i64>(encoded << 16)) >> 16);
#elif ARCH(AARCH64)
// For AArch64 the top 16 bits of the pointer should be zero.
return static_cast<FlatPtr>(encoded & 0xffff'ffff'ffffULL);
#else
# error "Unknown architecture. Don't know whether pointers need to be sign-extended."
#endif
}
private:
Value(u64 tag, u64 val)
{
@ -444,15 +462,7 @@ private:
PointerType* extract_pointer() const
{
VERIFY(is_cell());
// For 32-bit system the pointer fully fits so we can just return it directly.
if constexpr (sizeof(PointerType*) < sizeof(u64))
return reinterpret_cast<PointerType*>(static_cast<u32>(m_value.encoded & 0xffffffff));
// For x86_64 the top 16 bits should be sign extending the "real" top bit (47th).
// So first shift the top 16 bits away then using the right shift it sign extends the top 16 bits.
u64 ptr_val = (u64)(((i64)(m_value.encoded << 16)) >> 16);
return reinterpret_cast<PointerType*>(ptr_val);
return reinterpret_cast<PointerType*>(extract_pointer_bits(m_value.encoded));
}
[[nodiscard]] ThrowCompletionOr<Value> invoke_internal(VM&, PropertyKey const&, Optional<MarkedVector<Value>> arguments);