Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LibJS: JS::Value NaN-boxing only works correctly on x86 #15290

Closed
3 tasks done
BertalanD opened this issue Sep 19, 2022 · 4 comments · Fixed by #15292
Closed
3 tasks done

LibJS: JS::Value NaN-boxing only works correctly on x86 #15290

BertalanD opened this issue Sep 19, 2022 · 4 comments · Fixed by #15292
Assignees
Labels
bug Something isn't working

Comments

@BertalanD
Copy link
Member

BertalanD commented Sep 19, 2022

On 64-bit platforms, NaN payloads are sign-extended to get object pointers:

// 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);

This is an x86-specific behavior, and we might not have to do this for other architectures.

Action items:

  • Verify that this is the cause of Segmentation fault on ARM64 LadybirdBrowser/ancient-history#56
  • Make the compilation #error out on unsupported architectures instead of silently making incorrect assumptions
  • Add a correct ARM64 implementation (which would be to just extract the bottom 48 bits and keep the top 16 zeroed)

cc @davidot

@davidot
Copy link
Member

davidot commented Sep 19, 2022

Yes if ARM doesn't work like x86 in that regard this is indeed incorrect.
At the time I thought that the only use of ARM was Serenity with the minimal kernel I had forgotten about the new Macs and anywhere else you can build lagom.

Make sure that you also make a special check in the heap stack scan at https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibJS/Heap/Heap.cpp#L145

(I can also have a look at this but I see you have already assigned yourself, and I don't have an ARM machine to test this)

@Lubrsi Lubrsi added the bug Something isn't working label Sep 20, 2022
BertalanD added a commit to BertalanD/serenity that referenced this issue Sep 20, 2022
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
choose the correct behavior when porting to a new architecture.

Fixes SerenityOS#15290
Fixes LadybirdBrowser/ancient-history#56
@FireFox317
Copy link
Collaborator

It's interesting to know that this wasn't causing an issue on a M1 (also aarch64!), I was able to ran ladybird fine on that platform. Maybe Linux uses a different virtual memory layout than MacOS? Or the M1 supports a larger address space than the machine of the person reporting the issue?

@davidot
Copy link
Member

davidot commented Sep 20, 2022

It's interesting to know that this wasn't causing an issue on a M1 (also aarch64!), I was able to ran ladybird fine on that platform. Maybe Linux uses a different virtual memory layout than MacOS? Or the M1 supports a larger address space than the machine of the person reporting the issue?

It's possible that the M1 just tolerates these incorrect pointers, or doesn't give out much memory with the 47th bit set 🤷.

@BertalanD
Copy link
Member Author

Maybe Linux uses a different virtual memory layout than MacOS?

This is what I'm suspecting. Linux places the stack at the top of the address space, that's where the high pointers are coming from.

BertalanD added a commit to BertalanD/serenity that referenced this issue Sep 20, 2022
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 SerenityOS#15290
Fixes LadybirdBrowser/ancient-history#56
BertalanD added a commit to BertalanD/serenity that referenced this issue Sep 20, 2022
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 SerenityOS#15290
Fixes LadybirdBrowser/ancient-history#56
awesomekling pushed a commit that referenced this issue Sep 21, 2022
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 LadybirdBrowser/ancient-history#56
demostanis pushed a commit to demostanis/serenity that referenced this issue Sep 21, 2022
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 SerenityOS#15290
Fixes LadybirdBrowser/ancient-history#56
demostanis pushed a commit to demostanis/serenity that referenced this issue Sep 24, 2022
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 SerenityOS#15290
Fixes LadybirdBrowser/ancient-history#56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants