Closed Bug 1971581 (CVE-2025-8028) Opened 6 months ago Closed 5 months ago

Assertion failure: IsInt21(imm21), at jit/arm64/vixl/Assembler-vixl.h:4283

Categories

(Core :: JavaScript: WebAssembly, defect, P1)

ARM64
All
defect

Tracking

()

RESOLVED FIXED
142 Branch
Tracking Status
firefox-esr115 141+ fixed
firefox-esr128 141+ fixed
firefox-esr140 141+ fixed
firefox139 --- wontfix
firefox140 --- wontfix
firefox141 + fixed
firefox142 + fixed

People

(Reporter: gkw, Assigned: rhunt, NeedInfo)

References

(Blocks 1 open bug)

Details

(4 keywords, Whiteboard: [adv-main141+][adv-ESR140.1+][adv-ESR128.13+][adv-ESR115.26+])

Attachments

(9 files, 3 obsolete files)

2.38 MB, application/x-javascript
Details
7.53 KB, text/plain
Details
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
267 bytes, text/plain
Details
Attached file Testcase โ€”

See attachment.

(gdb) bt
#0  0x0000555558285a7d in MOZ_CrashSequence (aAddress=0x0, aLine=4283)
    at /home/msf1/shell-cache/js-dbg-64-armsim64-linux-x86_64-761c3d69bb90/objdir-js/dist/include/mozilla/Assertions.h:248
#1  vixl::Assembler::ImmPCRelAddress (imm21=-1048584) at /home/msf1/trees/mozilla-central/js/src/jit/arm64/vixl/Assembler-vixl.h:4283
#2  vixl::Assembler::adr (this=this@entry=0x7fffffff8948, rd=..., imm21=-1048584, doc=...)
    at /home/msf1/trees/mozilla-central/js/src/jit/arm64/vixl/MozAssembler-vixl.cpp:336
#3  0x0000555558285c3f in vixl::Assembler::adr (this=0x7fffffff8948, rd=..., label=0x7fffffff78f4)
    at /home/msf1/trees/mozilla-central/js/src/jit/arm64/vixl/MozAssembler-vixl.cpp:349
#4  0x0000555558229ada in vixl::MacroAssembler::Adr (this=0x7fffffff8948, rd=..., label=0x7fffffff78f4)
    at /home/msf1/trees/mozilla-central/js/src/jit/arm64/vixl/MacroAssembler-vixl.h:518
#5  0x00005555586dc710 in js::wasm::BaseCompiler::tableSwitch (this=this@entry=0x7fffffff7bd0, theTable=theTable@entry=0x7fffffff78f4,
    switchValue=switchValue@entry=..., dispatchCode=dispatchCode@entry=0x7fffffff78dc)
    at /home/msf1/trees/mozilla-central/js/src/wasm/WasmBaselineCompile.cpp:382
/snip

This seems to have occurred since m-c rev c1acdcb0c63c, gh rev 564a36e5a3aebd0c0d0645744cdc8d074c82b1c0 (late-Feb 2024, Firefox 125-approx).

Run with --fuzzing-safe --ion-offthread-compile=off --ion-eager, compile with AR=ar sh ../configure --enable-simulator=arm64 --enable-debug --enable-debug-symbols --with-ccache --enable-nspr-build --enable-ctypes --enable-gczeal --enable-rust-simd --disable-tests, tested on m-c rev 761c3d69bb90.

Setting needinfo? from our wasm gurus, Ryan and Yury, as a start.

Flags: sec-bounty?
Flags: needinfo?(ydelendik)
Flags: needinfo?(rhunt)

Actually cc'ing our JIT folks as well in case it's not a wasm-only thing. Note that this seems ARM64-only - frame 5 of the backtrace seems to occur through this region of code:

https://searchfox.org/mozilla-central/rev/240ca3fb4457621e155d039c7ea7055c0e22b374/js/src/wasm/WasmBaselineCompile.cpp#382

#elif defined(JS_CODEGEN_ARM64)
  AutoForbidPoolsAndNops afp(&masm,
                             /* number of instructions in scope = */ 4);

  ScratchI32 scratch(*this);

  ARMRegister s(scratch, 64);
  ARMRegister v(switchValue, 64);
  masm.Adr(s, theTable);                       // <-- here
  masm.Add(s, s, Operand(v, vixl::LSL, 3));
  masm.Ldr(s, MemOperand(s, 0));
  masm.Br(s);
#else
  MOZ_CRASH("BaseCompiler platform hook: tableSwitch");
#endif
Group: core-security → javascript-core-security

This test case has 131073 labels in the br_table. This appears to lead to the label theTable being too far away for the Adr instruction. This triggers the assertion the test case runs into (too large of an immediate to encode in the instruction). It's just a debug assertion though, so in release we'll continue on. We truncate the too large of immediate to fit in 21-bits [1].

So in effect we have:

theTable:
  codePointerToTableEntry
  codePointerToTableEntry
  codePointerToTableEntry
  ...
  codePointerToTableEntry

;; should be: adr entry, theTable
;; but actually is:
mov entry, pc - truncateInt21(offsetToTheTable)

add entry, tableIndex << 3
ldr entry, entry
br entry

So we get a pointer into JIT code that should be pointing at the beginning of the jump table, but actually points somewhere in the middle thanks to the truncate operation. Then we index into it and perform an indirect jump from the table.

If the attacker can control the JIT code well enough, they might be able to index into a constant pool and use that to get RCE.

[1] https://searchfox.org/mozilla-central/rev/3a3b69d4cc4c690092e04fa0bccf243309afa3ab/js/src/jit/arm64/vixl/Assembler-vixl.h#4284

Severity: -- → S2
Flags: needinfo?(ydelendik)
Flags: needinfo?(rhunt)
Keywords: sec-high
OS: Linux → All
Priority: -- → P1
Hardware: All → ARM64

This only affects ARM64, and from looking at the users of Adr, only wasm baseline seems vulnerable. All other uses don't have a user controlled amount of space between the Adr and the label.

We have an implementation limit for number of br_table entries, but it's set way too high (1 million). Lowering it would be the easiest fix here.

This seems like it would go all the way back to our first ARM64 build that supported wasm baseline. I can't find exactly which release that was, but I'm pretty sure it effects all releases.

Running in release, I can get the test case to crash by jumping to 'null' but nothing more interesting than that.

Here's the disassembly for the important part of the testcase:

jump table entries ...

0xab1475280d0  107fffcf  adr     x15, #+0xffff8 (addr 0xab1476280c8)
0xab1475280d4  8b000def  add     x15, x15, x0, lsl #3
0xab1475280d8  f94001ef  ldr     x15, [x15]
0xab1475280dc  d61f01e0  br      x15

The truncate is turning the effective address of the adr to actually be forwards instead of backwards, which is normally where the jump table is. That explains why I was always jumping into zero. There's no other code memory in use, so it's all zero.

I've been able to tweak the example to generate a bunch of other functions in the module so that we start jumping to non-null addresses.

So I think an attacker probably could place specially crafted wasm code in other functions in the module to generate the constants they want in JIT code, then choose the right br_table size and label index to trigger a load of that constant and jump to it. Unless I'm missing some mitigating factor.

Chrome has a limit on br_table sizes of 65_520 as opposed to our limit of 1 million (!) [1]. We should just adopt that to fix this bug. I don't know of a better way to obfuscate the fix then that. It would point to there being an issue with large br_table sizes, but nothing beyond that.

The alternative would be to update the VIXL assembler to emit a adrp + add immediate when the range is too large. However that's a difficult change to make, and I'd be more hesitant to uplift that widely. But at least it wouldn't point immediately at the br_table instruction.

[1] https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/wasm-limits.h;l=55;drc=d78d6f646dee0f68dcc59eed2d7de0f910e2fe2a

Attached file (secure) โ€”

Comment on attachment 9494641 [details]
Bug 1971581 - wasm: Align br_table limits with V8. r?yury

Security Approval Request

  • How easily could an exploit be constructed based on the patch?: Moderately difficult for a knowledgeable attacker? The patch points to a potential issue with very large br_table instructions. Constructing one is not too difficult, and if you do it with a debug build (for the assertion) on ARM64 you'll find that adr instruction is not being encoded with the right pc-relative offset. From there the attacker would need to find a way to embed their desired code pointer in a different wasm function and figure out the right combination of labels and jump index to jump to that code pointer. I don't know if another exploit is needed to leak an address of a code pointer to embed in the JIT code.

(test will land as a followup)

  • Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: No
  • Which branches (beta, release, and/or ESR) are affected by this flaw, and do the release status flags reflect this affected/unaffected state correctly?: All branches, flags are correct
  • If not all supported branches, which bug introduced the flaw?: None
  • Do you have backports for the affected branches?: Yes
  • If not, how different, hard to create, and risky will they be?: N/A
  • How likely is this patch to cause regressions; how much testing does it need?: Very low risk, the fix just changes the limit on br_table size to match Chrome. I don't expect web compat issues for this reason.
  • Is the patch ready to land after security approval is given?: Yes
  • Is Android affected?: Yes
Attachment #9494641 - Flags: sec-approval?

I have tested on the JS shell from 115 and can trigger the issue. So confirmed that all branches are vulnerable.

Comment on attachment 9494641 [details]
Bug 1971581 - wasm: Align br_table limits with V8. r?yury

Approved to request uplift and land

Attachment #9494641 - Flags: sec-approval? → sec-approval+
Whiteboard: [reminder-test 2025-09-02]
Group: javascript-core-security → core-security-release
Status: NEW → RESOLVED
Closed: 5 months ago
Resolution: --- → FIXED
Target Milestone: --- → 142 Branch
Attachment #9497014 - Flags: approval-mozilla-beta?

firefox-beta Uplift Approval Request

  • User impact if declined: Possible RCE in content process.
  • Code covered by automated testing: no
  • Fix verified in Nightly: yes
  • Needs manual QE test: no
  • Steps to reproduce for manual QE testing: N/A
  • Risk associated with taking this patch: Low
  • Explanation of risk level: It doesn't change any codegen, it just lowers a validation limit.
  • String changes made/needed: N/A
  • Is Android affected?: yes

Ryan, could you request uplift to all the branches affected? Thanks

Flags: needinfo?(rhunt)
Attachment #9497037 - Flags: approval-mozilla-release?

firefox-release Uplift Approval Request

  • User impact if declined: Possible RCE in content process
  • Code covered by automated testing: no
  • Fix verified in Nightly: yes
  • Needs manual QE test: no
  • Steps to reproduce for manual QE testing: N/A
  • Risk associated with taking this patch: Low
  • Explanation of risk level: It doesn't change any codegen, it just lowers a validation limit.
  • String changes made/needed: N/A
  • Is Android affected?: yes
Attachment #9497038 - Flags: approval-mozilla-esr128?

firefox-esr128 Uplift Approval Request

  • User impact if declined: Possible RCE in content process.
  • Code covered by automated testing: no
  • Fix verified in Nightly: yes
  • Needs manual QE test: no
  • Steps to reproduce for manual QE testing: N/A
  • Risk associated with taking this patch: Low
  • Explanation of risk level: It doesn't change any codegen, it just lowers a validation limit.
  • String changes made/needed: N/A
  • Is Android affected?: yes
Attachment #9497039 - Flags: approval-mozilla-esr140?

I've requested uplift for beta, release, esr 128, and esr 140. I didn't request one for esr 115 because my understanding is that's no longer active. If that's not the case I can request an uplift for that too.

Flags: needinfo?(rhunt)

firefox-esr140 Uplift Approval Request

  • User impact if declined: Possible RCE in content process
  • Code covered by automated testing: no
  • Fix verified in Nightly: yes
  • Needs manual QE test: no
  • Steps to reproduce for manual QE testing: N/A
  • Risk associated with taking this patch: Low
  • Explanation of risk level: It doesn't change any codegen, it just lowers a validation limit
  • String changes made/needed: N/A
  • Is Android affected?: yes

(In reply to Ryan Hunt [:rhunt] from comment #25)

I've requested uplift for beta, release, esr 128, and esr 140. I didn't request one for esr 115 because my understanding is that's no longer active. If that's not the case I can request an uplift for that too.

esr 115 is still active, we ship it for Windows 7-8.1 and macOS 10.12-10.14 until at least September (potentially longer)

Attachment #9497046 - Flags: approval-mozilla-esr115?

firefox-esr115 Uplift Approval Request

  • User impact if declined: Possible RCE in content process
  • Code covered by automated testing: no
  • Fix verified in Nightly: yes
  • Needs manual QE test: no
  • Steps to reproduce for manual QE testing: N/A
  • Risk associated with taking this patch: Low
  • Explanation of risk level: It doesn't change any codegen, it just lowers a validation limit
  • String changes made/needed: N/A
  • Is Android affected?: yes

This is not a suitable uplift for a dot release since ESRs are affected.

Attachment #9497037 - Flags: approval-mozilla-release? → approval-mozilla-release-
Attachment #9497014 - Flags: approval-mozilla-beta? → approval-mozilla-beta+
QA Whiteboard: [sec] [qa-triage-done-c142/b141]
Flags: qe-verify-
Attachment #9497038 - Flags: approval-mozilla-esr128? → approval-mozilla-esr128+
Attachment #9497046 - Flags: approval-mozilla-esr115? → approval-mozilla-esr115+

Ryan, could you rebase your patch for ESR and request uplift again? It failed to apply cleanly to the branch, thanks
https://lando.moz.tools/D255160/

Flags: needinfo?(rhunt)

Done.

Flags: needinfo?(rhunt)
Attachment #9497039 - Flags: approval-mozilla-esr140? → approval-mozilla-esr140+
Attachment #9497037 - Attachment is obsolete: true
Flags: sec-bounty? → sec-bounty+
Whiteboard: [reminder-test 2025-09-02] → [reminder-test 2025-09-02][adv-main141+]
Whiteboard: [reminder-test 2025-09-02][adv-main141+] → [reminder-test 2025-09-02][adv-main141+][adv-ESR115.26+]
Whiteboard: [reminder-test 2025-09-02][adv-main141+][adv-ESR115.26+] → [reminder-test 2025-09-02][adv-main141+][adv-ESR128.13+][adv-ESR115.26+]
Whiteboard: [reminder-test 2025-09-02][adv-main141+][adv-ESR128.13+][adv-ESR115.26+] → [reminder-test 2025-09-02][adv-main141+][adv-ESR140.1+][adv-ESR128.13+][adv-ESR115.26+]
Attached file advisory.txt (obsolete) โ€”
Attached file advisory.txt (obsolete) โ€”
Attachment #9500532 - Attachment is obsolete: true
Attached file advisory.txt โ€”
Attachment #9500653 - Attachment is obsolete: true
Alias: CVE-2025-8028

2 months ago, tjr placed a reminder on the bug using the whiteboard tag [reminder-test 2025-09-02] .

rhunt, please refer to the original comment to better understand the reason for the reminder.

Flags: needinfo?(rhunt)
Whiteboard: [reminder-test 2025-09-02][adv-main141+][adv-ESR140.1+][adv-ESR128.13+][adv-ESR115.26+] → [adv-main141+][adv-ESR140.1+][adv-ESR128.13+][adv-ESR115.26+]
Attachment #9494642 - Attachment description: Bug 1971581 - wasm: Add tests. r?yury → (secure)
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: