summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorDaniel Bertalan <dani@danielbertalan.dev>2022-09-20 18:09:33 +0200
committerAndreas Kling <kling@serenityos.org>2022-09-21 11:55:57 +0200
commit2b69af2dfe9ae2abaadee320878742a5c216deb4 (patch)
tree74d394d2f4c3e192d4dbfa01f1078f3947d43197 /Userland
parent62fed2a31d9ff4de881deb0cf92d6061b09a7696 (diff)
downloadserenity-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.cpp2
-rw-r--r--Userland/Libraries/LibJS/Runtime/Value.h28
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);