1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 07:58:11 +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

@ -39,25 +39,18 @@ TEST_NULLPTR_INPUT(Accessor);
#undef TEST_NULLPTR_INPUT
// Unfortunately we don't have a way to get the pointer without it being dereferenced
// so we just use the same logic, this is dangerous if Value is ever changed!
static u64 extract_pointer(u64 ptr)
{
return (u64)(((i64)(ptr << 16)) >> 16);
}
TEST_CASE(valid_pointer_in_gives_same_pointer_out)
{
if (sizeof(void*) < sizeof(double))
return;
#define EXPECT_POINTER_TO_SURVIVE(input) \
{ \
JS::Value value(reinterpret_cast<Object*>(static_cast<u64>(input))); \
EXPECT(value.is_object()); \
EXPECT(!value.is_null()); \
auto extracted_pointer = extract_pointer(value.encoded()); \
EXPECT_EQ(static_cast<u64>(input), extracted_pointer); \
#define EXPECT_POINTER_TO_SURVIVE(input) \
{ \
JS::Value value(reinterpret_cast<Object*>(static_cast<u64>(input))); \
EXPECT(value.is_object()); \
EXPECT(!value.is_null()); \
auto extracted_pointer = JS::Value::extract_pointer_bits(value.encoded()); \
EXPECT_EQ(static_cast<u64>(input), extracted_pointer); \
}
EXPECT_POINTER_TO_SURVIVE(0x1);
@ -66,9 +59,18 @@ TEST_CASE(valid_pointer_in_gives_same_pointer_out)
EXPECT_POINTER_TO_SURVIVE(0x00007fffffffffff);
EXPECT_POINTER_TO_SURVIVE(0x0000700000000000);
EXPECT_POINTER_TO_SURVIVE(0x0000100000000000);
#if ARCH(X86_64)
// On x86-64, the top 16 bits of pointers are equal to bit 47.
EXPECT_POINTER_TO_SURVIVE(0xffff800000000000);
EXPECT_POINTER_TO_SURVIVE(0xffff800000000001);
EXPECT_POINTER_TO_SURVIVE(0xffff800000000010);
#elif ARCH(AARCH64)
// ... but they should contain zeroes on AArch64.
EXPECT_POINTER_TO_SURVIVE(0x0000800000000000);
EXPECT_POINTER_TO_SURVIVE(0x0000800000000001);
EXPECT_POINTER_TO_SURVIVE(0x0000800000000010);
#endif
#undef EXPECT_POINTER_TO_SURVIVE
}