diff options
author | Daniel Bertalan <dani@danielbertalan.dev> | 2022-09-20 18:09:33 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-09-21 11:55:57 +0200 |
commit | 2b69af2dfe9ae2abaadee320878742a5c216deb4 (patch) | |
tree | 74d394d2f4c3e192d4dbfa01f1078f3947d43197 /Userland | |
parent | 62fed2a31d9ff4de881deb0cf92d6061b09a7696 (diff) | |
download | serenity-2b69af2dfe9ae2abaadee320878742a5c216deb4.zip |
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
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibJS/Heap/Heap.cpp | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/Value.h | 28 |
2 files changed, 20 insertions, 10 deletions
diff --git a/Userland/Libraries/LibJS/Heap/Heap.cpp b/Userland/Libraries/LibJS/Heap/Heap.cpp index 1fef87d439..97b416980a 100644 --- a/Userland/Libraries/LibJS/Heap/Heap.cpp +++ b/Userland/Libraries/LibJS/Heap/Heap.cpp @@ -142,7 +142,7 @@ __attribute__((no_sanitize("address"))) void Heap::gather_conservative_roots(Has // match any pointer-backed tag, in that case we have to extract the pointer to its // canonical form and add that as a possible pointer. if ((data & SHIFTED_IS_CELL_PATTERN) == SHIFTED_IS_CELL_PATTERN) - possible_pointers.set((u64)(((i64)data << 16) >> 16)); + possible_pointers.set(Value::extract_pointer_bits(data)); else possible_pointers.set(data); } else { diff --git a/Userland/Libraries/LibJS/Runtime/Value.h b/Userland/Libraries/LibJS/Runtime/Value.h index d53cbf3ecb..aadf403302 100644 --- a/Userland/Libraries/LibJS/Runtime/Value.h +++ b/Userland/Libraries/LibJS/Runtime/Value.h @@ -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); |