Closed Bug 1895086 (CVE-2024-5688) Opened 1 year ago Closed 1 year ago

Wild deref in js::Shape::getObjectClass (this=0x37bb7837f080) at vm/Shape.h:391

Categories

(Core :: JavaScript: GC, defect, P2)

defect

Tracking

()

RESOLVED FIXED
128 Branch
Tracking Status
firefox-esr115 127+ fixed
firefox126 --- wontfix
firefox127 + fixed
firefox128 + fixed

People

(Reporter: lukas.bernhard, Assigned: jonco)

References

(Blocks 2 open bugs)

Details

(Keywords: csectype-uaf, reporter-external, sec-high, Whiteboard: [adv-main127+][adv-esr115.12+])

Attachments

(3 files)

Steps to reproduce:

On git commit 38377227b8f96fda8f418db614e6a8aa67d01c31 the attached sample crashes in the js-shell when invoked as obj-x86_64-pc-linux-gnu/dist/bin/js --fuzzing-safe crash.js.
The reproducer looks similar to bug 1894916, maybe the root cause is related. Attempting to bisect failed but commits from 2022 are affected already (need to fiddle a bit with the second parameter to gczeal).
The older commits, e.g., d48cc4ab1193ac2a8f4aa78743a69fb24a3595e6 assert with Assertion failure: IsBackgroundFinalizedWhenTenured(a) == IsBackgroundFinalizedWhenTenured(b), at vm/JSObject.cpp:1218

gczeal(8, 37);
function f67() {
    for (let i71 = 0; i71 < 50; i71++) {
        const v82 = this.transplantableObject();
        const v83 = v82.object;
        class C84 {
        }
        const o86 = {
            "sameZoneAs": C84,
            "immutablePrototype": false,
        };
        const t60 = newGlobal(o86);
        t60.__proto__ = v83;
        const v90 = newGlobal();
        v90.nukeAllCCWs();
        v82.transplant(v90);
    }
}
f67();
#0  js::Shape::getObjectClass (this=0x37bb7837f080) at js/src/vm/Shape.h:391
#1  JSObject::finalize (this=0x37bb783ac040, gcx=0x7ffff6700c20) at js/src/vm/JSObject-inl.h:97
#2  0x0000555557ffda3f in js::gc::Arena::finalize<JSObject> (this=this@entry=0x37bb783ac000, gcx=gcx@entry=0x7ffff6700c20, thingKind=<optimized out>, thingSize=thingSize@entry=56)
    at js/src/gc/Sweeping.cpp:133
#3  0x0000555557ff2648 in FinalizeTypedArenas<JSObject> (gcx=0x7ffff6700c20, src=..., dest=..., thingKind=<optimized out>, budget=...) at js/src/gc/Sweeping.cpp:200
#4  0x0000555557fcf6e3 in FinalizeArenas (gcx=0xaaaaaaaaaaaa0008, gcx@entry=0x7ffff6700c20, src=..., dest=..., thingKind=thingKind@entry=js::gc::AllocKind::OBJECT4_BACKGROUND, budget=...)
    at js/src/gc/Sweeping.cpp:231
#5  0x0000555557fcf137 in js::gc::GCRuntime::backgroundFinalize (this=this@entry=0x7ffff6b2f798, gcx=gcx@entry=0x7ffff6700c20, zone=zone@entry=0x7ffff631d000, kind=<optimized out>, 
    empty=empty@entry=0x7ffff6700b10) at js/src/gc/Sweeping.cpp:270
#6  0x0000555557fd2662 in js::gc::GCRuntime::sweepBackgroundThings (this=this@entry=0x7ffff6b2f798, zones=...) at js/src/gc/Sweeping.cpp:348
#7  0x0000555557fd3025 in js::gc::GCRuntime::sweepFromBackgroundThread (this=0x7ffff6b2f798, lock=...) at js/src/gc/Sweeping.cpp:425
#8  0x0000555557f47050 in js::GCParallelTask::runTask (this=this@entry=0x7ffff6b31768, gcx=gcx@entry=0x7ffff6700c20, lock=...) at js/src/gc/GCParallelTask.cpp:201
#9  0x0000555557f47429 in js::GCParallelTask::runHelperThreadTask (this=0x7ffff6b31768, lock=...) at js/src/gc/GCParallelTask.cpp:183
#10 0x0000555557569fb7 in js::GlobalHelperThreadState::runTaskLocked (this=this@entry=0x7ffff6b19400, task=0x7ffff6b31768, locked=...) at js/src/vm/HelperThreads.cpp:1728
#11 0x0000555557569c12 in js::GlobalHelperThreadState::runOneTask (this=0x7ffff6b19400, lock=...) at js/src/vm/HelperThreads.cpp:1697
#12 0x00005555575a0e4b in js::HelperThread::threadLoop (this=this@entry=0x7ffff6b27260, pool=pool@entry=0x7ffff6b23380) at js/src/vm/InternalThreadPool.cpp:282
#13 0x00005555575a0a18 in js::HelperThread::ThreadMain (pool=0x7ffff6b23380, helper=0x7ffff6b27260) at js/src/vm/InternalThreadPool.cpp:225
#14 0x00005555575bda74 in js::detail::ThreadTrampoline<void (&)(js::InternalThreadPool*, js::HelperThread*), js::InternalThreadPool*&, js::HelperThread*>::callMain<0ul, 1ul> (this=0x7ffff6b0f2e0)
    at js/src/threading/Thread.h:228
#15 js::detail::ThreadTrampoline<void (&)(js::InternalThreadPool*, js::HelperThread*), js::InternalThreadPool*&, js::HelperThread*>::Start (aPack=0x7ffff6b0f2e0)
    at js/src/threading/Thread.h:217
#16 0x00007ffff7897b5a in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:444
#17 0x00007ffff79285fc in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:78

(gdb) x/i $rip
=> 0x555557ffe2f4 <_ZN8JSObject8finalizeEPN2JS9GCContextE+148>: mov    (%rax),%rax
(gdb) i r rax 
rax            0xfffe4b4b00c000c0  -480164446207808
Group: firefox-core-security → core-security
Component: Untriaged → JavaScript: GC
Product: Firefox → Core
See Also: → 1894916
Group: core-security → javascript-core-security

Jon, any insight?
Sounds like a corner case of object transplant. Not sure this can be achieved in the browser but I'll let you judge that.

Severity: -- → S4
Flags: needinfo?(jcoppeard)
Priority: -- → P2
Duplicate of this bug: 1894916

During swap JSObject::setIsUsedAsPrototype is triggering a GC before we do the write barrier.

Assignee: nobody → jcoppeard
Flags: needinfo?(jcoppeard)

We already suppress GC for part of this, but not for the part where we call
JSObject::setIsUsedAsPrototype. This can GC (which was surprising to me) and so
we can sweep before the pre-write barrier which comes after this.

The simplest and safest thing is to suppress GC for the whole method.

Comment on attachment 9400712 [details]
Bug 1895086 - Suppress GC during JSObject::swap r?jandem

Security Approval Request

  • How easily could an exploit be constructed based on the patch?: Very difficult as it relies on shell-only test functions.
  • 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
  • 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?: Trivial
  • How likely is this patch to cause regressions; how much testing does it need?: Very unlikely, this just extends the scope of GC suppression that's already active in this method.
  • Is the patch ready to land after security approval is given?: Yes
  • Is Android affected?: Yes
Attachment #9400712 - Flags: sec-approval?

The severity field for this bug is set to S4. However, the bug is flagged with the sec-high keyword.
:jonco, could you consider increasing the severity of this security bug?

For more information, please visit BugBot documentation.

Flags: needinfo?(jcoppeard)

Comment on attachment 9400712 [details]
Bug 1895086 - Suppress GC during JSObject::swap r?jandem

Approved to land and uplift if needed - confused by the comment " relies on shell-only test functions." - if this relies on test functions, it wouldn't affect Firefox, right? In which case it should not be a security bug.

The test can land immediatlely if it's not a sec bug; or in July if it is.

Attachment #9400712 - Flags: sec-approval? → sec-approval+
Whiteboard: [reminder-test 2024-07-23]

(In reply to Tom Ritter [:tjr] from comment #8)

Comment on attachment 9400712 [details]
Bug 1895086 - Suppress GC during JSObject::swap r?jandem

Approved to land and uplift if needed - confused by the comment " relies on shell-only test functions." - if this relies on test functions, it wouldn't affect Firefox, right? In which case it should not be a security bug.

The test can land immediatlely if it's not a sec bug; or in July if it is.

The shell-only test functions simulate actual operations we do in the browser.

I should have said that the test in the patch relies on shell only functions. The issue is present in code used by the browser. The test has been moved to a separate patch now though.

A better answer to that question would be: Very difficult as it relies on triggering GC at a precise point and then exploiting the crash.

Flags: needinfo?(jcoppeard)
Pushed by jcoppeard@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/09652347e814 Suppress GC during JSObject::swap r=jandem
Group: javascript-core-security → core-security-release
Status: NEW → RESOLVED
Closed: 1 year ago
Resolution: --- → FIXED
Target Milestone: --- → 128 Branch

The patch landed in nightly and beta is affected.
:jonco, is this bug important enough to require an uplift?

  • If yes, please nominate the patch for beta approval.
  • If no, please set status-firefox127 to wontfix.

For more information, please visit BugBot documentation.

Flags: needinfo?(jcoppeard)

Comment on attachment 9400712 [details]
Bug 1895086 - Suppress GC during JSObject::swap r?jandem

Beta/Release Uplift Approval Request

  • User impact if declined: Possible crash / security vulnerability.
  • Is this code covered by automated tests?: Yes
  • Has the fix been verified in Nightly?: Yes
  • Needs manual test from QE?: No
  • If yes, steps to reproduce:
  • List of other uplifts needed: None
  • Risk to taking this patch: Low
  • Why is the change risky/not risky? (and alternatives if risky): This is a very simple change to extend an existing GC suppression region already present in this code.
  • String changes made/needed: None
  • Is Android affected?: Yes
Flags: needinfo?(jcoppeard)
Attachment #9400712 - Flags: approval-mozilla-beta?

Can we get an uplift request for esr as well?

Flags: needinfo?(jcoppeard)

Comment on attachment 9400712 [details]
Bug 1895086 - Suppress GC during JSObject::swap r?jandem

ESR Uplift Approval Request

  • If this is not a sec:{high,crit} bug, please state case for ESR consideration: This is a sec-high bug.
  • User impact if declined: Possible crash / security vulnerability.
  • Fix Landed on Version: 128
  • Risk to taking this patch: Low
  • Why is the change risky/not risky? (and alternatives if risky): This is a very simple change to extend an existing GC suppression region already present in this code.
Flags: needinfo?(jcoppeard)
Attachment #9400712 - Flags: approval-mozilla-esr115?

Comment on attachment 9400712 [details]
Bug 1895086 - Suppress GC during JSObject::swap r?jandem

Approved for 127 beta 4, thanks.

Attachment #9400712 - Flags: approval-mozilla-beta? → approval-mozilla-beta+
QA Whiteboard: [post-critsmash-triage]
Flags: qe-verify-
Flags: sec-bounty?

Comment on attachment 9400712 [details]
Bug 1895086 - Suppress GC during JSObject::swap r?jandem

Approved for 115.12esr.

Attachment #9400712 - Flags: approval-mozilla-esr115? → approval-mozilla-esr115+
Flags: sec-bounty? → sec-bounty+
Whiteboard: [reminder-test 2024-07-23] → [reminder-test 2024-07-23][adv-main127+]
Attached file advisory.txt
Whiteboard: [reminder-test 2024-07-23][adv-main127+] → [reminder-test 2024-07-23][adv-main127+][adv-esr115.12+]
Alias: CVE-2024-5688

2 months ago, tjr placed a reminder on the bug using the whiteboard tag [reminder-test 2024-07-23] .

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

Flags: needinfo?(jcoppeard)
Whiteboard: [reminder-test 2024-07-23][adv-main127+][adv-esr115.12+] → [adv-main127+][adv-esr115.12+]
Flags: needinfo?(jcoppeard)
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: