diff --git a/src/jsc/bindings/bindings.cpp b/src/jsc/bindings/bindings.cpp index ae3d8986c710..b3021c959b3a 100644 --- a/src/jsc/bindings/bindings.cpp +++ b/src/jsc/bindings/bindings.cpp @@ -3459,7 +3459,12 @@ JSC::EncodedJSValue JSC__JSGlobalObject__createAggregateError(JSC::JSGlobalObjec auto& vm = JSC::getVM(globalObject); auto scope = DECLARE_THROW_SCOPE(vm); - WTF::String message = Zig::toString(*arg3); + // External-tagged messages transfer ownership of their heap buffer to the + // JS string (VirtualMachine.rs marks the joined build-failure message + // global and relies on C++ freeing it); everything else is copied so an + // untagged ZigString can't leave the AggregateError aliasing the caller's + // buffer. See Zig::getErrorInstance. + WTF::String message = Zig::isTaggedExternalPtr(arg3->ptr) ? Zig::toString(*arg3) : Zig::toStringCopy(*arg3); JSC::JSValue cause = JSC::jsUndefined(); JSC::JSArray* array = nullptr; { diff --git a/src/jsc/bindings/helpers.h b/src/jsc/bindings/helpers.h index cb9bc987755c..996920fc149b 100644 --- a/src/jsc/bindings/helpers.h +++ b/src/jsc/bindings/helpers.h @@ -393,7 +393,16 @@ static const WTF::String toStringStatic(ZigString str) static JSC::JSValue getErrorInstance(const ZigString* str, JSC::JSGlobalObject* globalObject) { - WTF::String message = toString(*str); + // External-tagged strings transfer ownership: the caller heap-allocated + // the bytes and marked them global so the JS string adopts them (and + // frees them via free_global_string) — keep that path, like toIdentifier + // does. Everything else is copied: an untagged ZigString would otherwise + // become a JSString that aliases the caller's bytes with no ownership, + // and several callers (AsyncModule's resolve/download error paths) pass + // stack-local buffers that are freed before JS can ever read + // error.message. The TypeError/SyntaxError/RangeError siblings below + // already copy. + WTF::String message = isTaggedExternalPtr(str->ptr) ? toString(*str) : toStringCopy(*str); if (message.isNull() && str->len > 0) [[unlikely]] { // pending exception while creating an error. return {};