Closed Bug 1056027 Opened 6 years ago Closed 3 months ago

OdinMonkey: optimize heap access by placing the start of the buffer at absolute zero in the process address space.


(Core :: Javascript: WebAssembly, defect, P5)






(Reporter: dougc, Unassigned)


(Blocks 1 open bug)



(1 file, 4 obsolete files)

Placing the start of asm.js heap buffer at absolute address zero in the process address map avoids the need to add back the heap base on every heap access and makes an asm.js heap index equivalent to a raw pointer which will support further optimization of heap access.

For the x64 and ARM the pinned heap register becomes free to use as a general purpose register, reducing register pressure which can help improve performance.

For the x64 and ARM the base+index addressing mode becomes available to optimize asm.js heap access patterns.

For the ARM the index+offset and base+index addressing modes become available to optimize asm.js heap access patterns.

The address map of Firefox for Android currently has a large unused area from 0x00000000 to 0x3fffffff (on a Nexus 4 running Android 4.4 anyway). This is due to the kernel mmap address space split. Realistically only one significant asm.js application can run at a time due to limited memory, limited address space, and fragmentation. Management of a large available area based at zero may well give a better user experience than the status quo.

For B2G each browser tab and application runs in a separate process and it could be quite reasonable to make a large area available for a single asm.js heap.

The area could be shared by multiple threads to support multi-threaded code while limited to a single heap per process.

One problem is that ARM Linux kernels do not allow allocation of the first page, and the kernel mmap_min_addr is often 32k and not a parameter we could control. This would mean that although the asm.js heap starts at zero, the first few pages would not be directly accessible. We could relocate constant index references to these addresses, but would have to redirect and emulate dynamic references in a signal handler. There seems no reason why asm.js code could not be written to simply avoid accessing the low pages, and it could even be used to implement null-pointer reference detection of asm.js code, or asm.js code could dedicate it to data accessed only by constant index references.

Anther problem is controlling the compilation of code optimized for a heap placed at zero versus asm.js code using buffers at other addresses. This might be easily solved for b2g apps with a setting that dictates that the app uses only a single buffer at zero. However in general language extensions might be necessary. The work on shared asm.js buffers might be food for thought.
The flip side is that we've been considering doing what Safari does and reserving a large chunk of address space starting at 0 to make near-null reads/writes safe crashes as opposed to exploitable bugs.  That wouldn't be so compatible with this proposal, right?
And in particular, do make sure to bring this up outside the JIT team, since this affects all Gecko code, not just the JIT.
(In reply to Boriz Zbarsky [:bz] from comment #1)
> The flip side is that we've been considering doing what Safari does and
> reserving a large chunk of address space starting at 0 to make near-null
> reads/writes safe crashes as opposed to exploitable bugs.  That wouldn't be
> so compatible with this proposal, right?

From an IRC follow up, Safari is 64 bit only and reserves the lower 4GB so that loads and stores with 32 bit offsets from zero are fail safe.

The ARM instructions have only a small immediate offset range, compared with the x64's immediate offset range, and it's not possible to reserve 4GB on the x86.

We can still reserve a relatively large guard zone at zero, it's just a zone that the asm.js app can not use, or use efficiently, either.

The asm.js heap buffer is at least not executable memory, and is one layer removed from being a browser exploit. Malicious reads or writes into the asm.js buffer could exploit the asm.js applications state, not the browsers state directly, and the asm.js app is sandboxed by the browser. You would still be more secure running asm.js code rather than native code.
Here's the hack patch used to explore this, and it gives some idea of the scope of the changes needed.

This demonstrated asm.js code running with the buffer at zero in the address map on Linux x86 and x64, ARM Android on a Nexus 4 Android 4.4.4, and B2G on the Flame. All the regular big asm.js demos ran.

For the ARM, a modified kernel was required to allow mapping the first page which is obviously not practical to require.

For Linux, is was necessary to set the kernel parameter vm.mmap_min_addr to zero, and to disable a selinux restriction on mapping low pages. It is probably not practical to require these changes either.

However these are not show stoppers and so long as the low pages are not mapped then we can emulate access to the low pages in a sigsegv handler (slow), relocate constant index accesses which can still be fast, or just require asm.js to not touch the low pages. Catching asm.js null pointer dereferences might be useful too and consistent with not touching these low pages.
Assignee: nobody → dtc-moz
Attachment #8480344 - Attachment is obsolete: true
Rebase, plus some fixes.
Attachment #8493106 - Attachment is obsolete: true
Rebase. Using a null data pointer to represent a detached array buffer conflicts with placing the buffer at zero, but this has been worked around by assuming that buffers at zero are not detachable and that asm.js code that accepts a buffer at zero does not support changing the heap.
Attachment #8498876 - Attachment is obsolete: true
Are we still interested in doing this? It seems it'd be nice if we consider having content processes for wasm (aka "wasm workers"), but this would expose a big attack surface area (nullptr or near-nullptr accesses).
Priority: -- → P5
Assignee: dtc-moz → nobody
I thought this could work nicely for a monolithic application, such as a game, that had one main linear heap, and it did give a useful performance boost in past testing. It would require the first page be a guard page, and pages are now 64k so I think that might work out nicely, no kernel changes needed. With the first page guarded anyway, the only other security issue seems to be that the address space is fixed which might make some attacks more systematic. On 64-bit the entire low 4G might have been avoided for security reasons, but on 32-bit that is not an option anyway. Wasm now seems to have some infrastructure to support different memory types, and perhaps that could support an array-buffer-at-zero memory type. Other challenge is that the code might be specialized to the constraint, so this needs to be managed.
Per policy at If this bug is not an enhancement request or a bug not present in a supported release of Firefox, then it may be reopened.
Closed: 2 years ago
Resolution: --- → INACTIVE
Resolution: INACTIVE → ---
Component: JavaScript Engine: JIT → Javascript: WebAssembly

I'm going to close this because it is becoming an ever more remote possibility to do this for Firefox, where we're de facto biasing in favor of 64-bit systems all the time now. Stand-alone runtimes such as wasmtime that target IoT systems might find the idea more directly applicable.

Closed: 2 years ago3 months ago
Resolution: --- → WONTFIX
You need to log in before you can comment on or make changes to this bug.