While touring theFirefoxsource code to gather inspiration for a CTF challenge (Stay tuned forTRX CTF 2026!) I stumbled across quite an interesting, albeit simple, bug insideSpiderMonkey’s Wasm component. I was able to exploit it to gainCode Executioninside theFirefox renderer processand reported my findings toMozilla. Disclosure details are at the end of the post. The vulnerability was introduced by commitfcc2f20e35ec, which was a refactoring ofWasm GCarray metadata. The bug is actually a single character typo, can you spot it? As hinted by the comment, that&should actually have been a|. This causes the code to store 0 rather than the intended pointer with the LSB set (due to pointer alignmentuintptr_t(oolHeaderNew) & 1will always be equal to 0). A simple mistake, no? How much damage can one little typo create? Let’s first take a detour to understand the structure ofWasm GCarrays, more specifically, the difference betweenInlineandOut-of-lineones. Fromjs/src/wasm/WasmGcObject.h: The bug allows us to mistakenly tag anOOLarray asIL. This will cause problems… The typo lies insideWasmArrayObject::obj_moved(), which gets called when the GC moves a Wasm array (and its OOL buffer) in these two cases: When an OOL array moves, the GC must leave aforwarding pointerin the old buffer’s header so thatIon (SpiderMonkey’s JIT compiler)can find the data’s new location. To distinguish a forwarding pointer from a normal header, SpiderMonkey tags the address by setting its LSB to 1. The bug sets the forwarding pointer to 0, inadvertently satisfying the condition for an array to be considered Inline (SeeisDataInlineabove). It’s also worth noting that this bug is only triggerable inside a Wasm function optimized byIon, as this entire mechanism is not present in the Baseline compiler. Before proceeding with the exploitation, let’s validate our findings. Here was my first POC: Now let’s build a JS shell with a sanitizer and check for a crash: Nice!However this crash only happens due to extra GC poisoning the debug build performs to catch issues such as this one, if we run it again with--setpref extra_gc_poisoning=falsewe can no longer reproduce the crash. Can we do better? As any pwner will tell you, just spray some0x41414141s! (with a lot of trial and error…) This is essentially the original POC with a few tweaks: Let’s try it:
A remote code execution vulnerability exists in the Firefox renderer process due to a single-character typo in