fix again and add reproduction test

This commit is contained in:
Meghan Denny
2024-07-31 22:31:32 -07:00
parent a116bf9e7b
commit e331fea16a
7 changed files with 62 additions and 172 deletions

View File

@@ -703,18 +703,6 @@ pub const Subprocess = struct {
return .undefined;
}
pub fn onStdinDestroyed(this: *Subprocess) void {
this.island.clearStdin();
if (this.island.subprocess == null) {
// if the process has already been garbage collected, we can free the memory now
bun.default_allocator.destroy(this);
} else {
// otherwise update the pending activity flag
this.updateHasPendingActivity();
}
}
pub fn doSend(this: *Subprocess, global: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) JSValue {
const ipc_data = &(this.ipc_data orelse {
if (this.hasExited()) {
@@ -1180,13 +1168,6 @@ pub const Subprocess = struct {
// When the stream has closed we need to be notified to prevent a use-after-free
// We can test for this use-after-free by enabling hot module reloading on a file and then saving it twice
pub fn onClose(this: *Writable, _: ?bun.sys.Error) void {
const process: *Subprocess = @fieldParentPtr("stdin", this);
if (process.this_jsvalue != .zero) {
if (Subprocess.stdinGetCached(process.this_jsvalue)) |existing_value| {
JSC.WebCore.FileSink.JSSink.setDestroyCallback(existing_value, 0);
}
}
switch (this.*) {
.buffer => {
@@ -1194,11 +1175,11 @@ pub const Subprocess = struct {
},
.pipe => {
this.pipe.deref();
this.pipe.island.?.clearStdin();
},
else => {},
}
process.onStdinDestroyed();
this.* = .{
.ignore = {},
@@ -1230,6 +1211,7 @@ pub const Subprocess = struct {
},
}
pipe.writer.setParent(pipe);
pipe.island = island;
island.stdin = pipe;
return Writable{
@@ -1342,7 +1324,6 @@ pub const Subprocess = struct {
}
return pipe.toJSWithDestructor(
globalThis,
JSC.WebCore.SinkDestructor.Ptr.init(subprocess.island),
);
}
},
@@ -1350,13 +1331,6 @@ pub const Subprocess = struct {
}
pub fn finalize(this: *Writable) void {
const subprocess: *Subprocess = @fieldParentPtr("stdin", this);
if (subprocess.this_jsvalue != .zero) {
if (JSC.Codegen.JSSubprocess.stdinGetCached(subprocess.this_jsvalue)) |existing_value| {
JSC.WebCore.FileSink.JSSink.setDestroyCallback(existing_value, 0);
}
}
return switch (this.*) {
.pipe => |pipe| {
if (pipe.signal.ptr == @as(*anyopaque, @ptrCast(this))) {
@@ -1424,13 +1398,9 @@ pub const Subprocess = struct {
this.stdin.buffer.close();
}
if (existing_stdin_value != .zero) {
JSC.WebCore.FileSink.JSSink.setDestroyCallback(existing_stdin_value, 0);
}
if (stdin) |pipe| {
_ = pipe;
this.island.clearStdin();
pipe.onAttachedProcessExit();
}
var did_update_has_pending_activity = false;
@@ -1559,10 +1529,7 @@ pub const Subprocess = struct {
this.process.deref();
this.island.clearSubprocess();
if (this.island.stdin == null) {
// if no file sink exists we can free immediately
bun.default_allocator.destroy(this);
}
bun.default_allocator.destroy(this);
}
pub fn getExited(
@@ -2293,13 +2260,6 @@ pub const Subprocess = struct {
subprocess: ?*Subprocess = null,
stdin: ?*JSC.WebCore.FileSink = null,
pub fn onStdinDestroyed(this: *Island) void {
const had_subprocess = this.subprocess != null;
this.clearStdin();
if (!had_subprocess) return; // dont uaf the next line
this.subprocess.?.onStdinDestroyed();
}
pub fn clearSubprocess(this: *Island) void {
this.subprocess = null;
if (this.stdin == null) this.destroy();

View File

@@ -596,7 +596,7 @@ extern "C" JSC__JSValue SYSV_ABI Bun__Path__toNamespacedPath(JSC__JSGlobalObject
#endif
CPP_DECL JSC__JSValue ArrayBufferSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3);
CPP_DECL JSC__JSValue ArrayBufferSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor);
CPP_DECL JSC__JSValue ArrayBufferSink__createObject(JSC__JSGlobalObject* arg0, void* arg1);
CPP_DECL void ArrayBufferSink__detachPtr(JSC__JSValue JSValue0);
CPP_DECL void* ArrayBufferSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
CPP_DECL void ArrayBufferSink__onClose(JSC__JSValue JSValue0, JSC__JSValue JSValue1);
@@ -616,7 +616,7 @@ BUN_DECLARE_HOST_FUNCTION(ArrayBufferSink__write);
#endif
CPP_DECL JSC__JSValue HTTPSResponseSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3);
CPP_DECL JSC__JSValue HTTPSResponseSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor);
CPP_DECL JSC__JSValue HTTPSResponseSink__createObject(JSC__JSGlobalObject* arg0, void* arg1);
CPP_DECL void HTTPSResponseSink__detachPtr(JSC__JSValue JSValue0);
CPP_DECL void* HTTPSResponseSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
CPP_DECL void HTTPSResponseSink__onClose(JSC__JSValue JSValue0, JSC__JSValue JSValue1);
@@ -636,7 +636,7 @@ BUN_DECLARE_HOST_FUNCTION(HTTPSResponseSink__write);
#endif
CPP_DECL JSC__JSValue HTTPResponseSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3);
CPP_DECL JSC__JSValue HTTPResponseSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor);
CPP_DECL JSC__JSValue HTTPResponseSink__createObject(JSC__JSGlobalObject* arg0, void* arg1);
CPP_DECL void HTTPResponseSink__detachPtr(JSC__JSValue JSValue0);
CPP_DECL void* HTTPResponseSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
CPP_DECL void HTTPResponseSink__onClose(JSC__JSValue JSValue0, JSC__JSValue JSValue1);
@@ -656,7 +656,7 @@ BUN_DECLARE_HOST_FUNCTION(HTTPResponseSink__write);
#endif
CPP_DECL JSC__JSValue FileSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3);
CPP_DECL JSC__JSValue FileSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor);
CPP_DECL JSC__JSValue FileSink__createObject(JSC__JSGlobalObject* arg0, void* arg1);
CPP_DECL void FileSink__detachPtr(JSC__JSValue JSValue0);
CPP_DECL void* FileSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
CPP_DECL void FileSink__onClose(JSC__JSValue JSValue0, JSC__JSValue JSValue1);
@@ -677,7 +677,7 @@ BUN_DECLARE_HOST_FUNCTION(FileSink__write);
#endif
CPP_DECL JSC__JSValue FileSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3);
CPP_DECL JSC__JSValue FileSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor);
CPP_DECL JSC__JSValue FileSink__createObject(JSC__JSGlobalObject* arg0, void* arg1);
CPP_DECL void FileSink__detachPtr(JSC__JSValue JSValue0);
CPP_DECL void* FileSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
CPP_DECL void FileSink__onClose(JSC__JSValue JSValue0, JSC__JSValue JSValue1);

View File

@@ -357,30 +357,26 @@ pub extern fn Zig__GlobalObject__create(arg0: ?*anyopaque, arg1: i32, arg2: bool
pub extern fn Zig__GlobalObject__getModuleRegistryMap(arg0: *bindings.JSGlobalObject) ?*anyopaque;
pub extern fn Zig__GlobalObject__resetModuleRegistryMap(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) bool;
pub extern fn ArrayBufferSink__assignToStream(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue, arg2: ?*anyopaque, arg3: [*c]*anyopaque) JSC__JSValue;
pub extern fn ArrayBufferSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque, onDestroyPtrTag: usize) JSC__JSValue;
pub extern fn ArrayBufferSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) JSC__JSValue;
pub extern fn ArrayBufferSink__detachPtr(JSValue0: JSC__JSValue) void;
pub extern fn ArrayBufferSink__setDestroyCallback(JSValue0: JSC__JSValue, callback: usize) void;
pub extern fn ArrayBufferSink__fromJS(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*anyopaque;
pub extern fn ArrayBufferSink__onClose(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue) void;
pub extern fn ArrayBufferSink__onReady(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, JSValue2: JSC__JSValue) void;
pub extern fn HTTPSResponseSink__assignToStream(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue, arg2: ?*anyopaque, arg3: [*c]*anyopaque) JSC__JSValue;
pub extern fn HTTPSResponseSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque, onDestroyPtrTag: usize) JSC__JSValue;
pub extern fn HTTPSResponseSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) JSC__JSValue;
pub extern fn HTTPSResponseSink__detachPtr(JSValue0: JSC__JSValue) void;
pub extern fn HTTPSResponseSink__setDestroyCallback(JSValue0: JSC__JSValue, callback: usize) void;
pub extern fn HTTPSResponseSink__fromJS(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*anyopaque;
pub extern fn HTTPSResponseSink__onClose(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue) void;
pub extern fn HTTPSResponseSink__onReady(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, JSValue2: JSC__JSValue) void;
pub extern fn HTTPResponseSink__assignToStream(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue, arg2: ?*anyopaque, arg3: [*c]*anyopaque) JSC__JSValue;
pub extern fn HTTPResponseSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque, onDestroyPtrTag: usize) JSC__JSValue;
pub extern fn HTTPResponseSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) JSC__JSValue;
pub extern fn HTTPResponseSink__detachPtr(JSValue0: JSC__JSValue) void;
pub extern fn HTTPResponseSink__setDestroyCallback(JSValue0: JSC__JSValue, callback: usize) void;
pub extern fn HTTPResponseSink__fromJS(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*anyopaque;
pub extern fn HTTPResponseSink__onClose(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue) void;
pub extern fn HTTPResponseSink__onReady(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, JSValue2: JSC__JSValue) void;
pub extern fn FileSink__assignToStream(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue, arg2: ?*anyopaque, arg3: [*c]*anyopaque) JSC__JSValue;
pub extern fn FileSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque, onDestroyPtrTag: usize) JSC__JSValue;
pub extern fn FileSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) JSC__JSValue;
pub extern fn FileSink__detachPtr(JSValue0: JSC__JSValue) void;
pub extern fn FileSink__setDestroyCallback(JSValue0: JSC__JSValue, callback: usize) void;
pub extern fn FileSink__fromJS(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*anyopaque;
pub extern fn FileSink__onClose(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue) void;
pub extern fn FileSink__onReady(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, JSValue2: JSC__JSValue) void;

View File

@@ -1528,40 +1528,6 @@ const AutoFlusher = struct {
}
};
pub const SinkDestructor = struct {
const Detached = opaque {};
const Subprocess = JSC.API.Bun.Subprocess;
pub const Ptr = bun.TaggedPointerUnion(.{
Detached,
Subprocess.Island,
});
pub export fn Bun__onSinkDestroyed(
ptr_value: ?*anyopaque,
sink_ptr: ?*anyopaque,
) callconv(.C) void {
_ = sink_ptr; // autofix
const ptr = Ptr.from(ptr_value);
if (ptr.isNull()) {
return;
}
switch (ptr.tag()) {
.Detached => {
return;
},
.Island => {
const island = ptr.as(Subprocess.Island);
island.onStdinDestroyed();
},
else => {
Output.debugWarn("Unknown sink type", .{});
},
}
}
};
pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {
return struct {
sink: SinkType,
@@ -1610,10 +1576,10 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {
return shim.cppFn("onStart", .{ ptr, globalThis });
}
pub fn createObject(globalThis: *JSGlobalObject, object: *anyopaque, destructor: usize) callconv(.C) JSValue {
pub fn createObject(globalThis: *JSGlobalObject, object: *anyopaque) callconv(.C) JSValue {
JSC.markBinding(@src());
return shim.cppFn("createObject", .{ globalThis, object, destructor });
return shim.cppFn("createObject", .{ globalThis, object });
}
pub fn fromJS(globalThis: *JSGlobalObject, value: JSValue) ?*anyopaque {
@@ -1622,12 +1588,6 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {
return shim.cppFn("fromJS", .{ globalThis, value });
}
pub fn setDestroyCallback(value: JSValue, callback: usize) void {
JSC.markBinding(@src());
return shim.cppFn("setDestroyCallback", .{ value, callback });
}
pub fn construct(globalThis: *JSGlobalObject, _: *JSC.CallFrame) callconv(JSC.conv) JSValue {
JSC.markBinding(@src());
@@ -1651,7 +1611,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {
return .undefined;
};
this.sink.construct(allocator);
return createObject(globalThis, this, 0);
return createObject(globalThis, this);
}
pub fn finalize(ptr: *anyopaque) callconv(.C) void {
@@ -2954,6 +2914,7 @@ pub const FileSink = struct {
is_socket: bool = false,
fd: bun.FileDescriptor = bun.invalid_fd,
has_js_called_unref: bool = false,
island: ?*JSC.Subprocess.Island = null,
const log = Output.scoped(.FileSink, false);
@@ -3367,11 +3328,11 @@ pub const FileSink = struct {
}
pub fn toJS(this: *FileSink, globalThis: *JSGlobalObject) JSValue {
return JSSink.createObject(globalThis, this, 0);
return JSSink.createObject(globalThis, this);
}
pub fn toJSWithDestructor(this: *FileSink, globalThis: *JSGlobalObject, destructor: ?SinkDestructor.Ptr) JSValue {
return JSSink.createObject(globalThis, this, if (destructor) |dest| @intFromPtr(dest.ptr()) else 0);
pub fn toJSWithDestructor(this: *FileSink, globalThis: *JSGlobalObject) JSValue {
return JSSink.createObject(globalThis, this);
}
pub fn endFromJS(this: *FileSink, globalThis: *JSGlobalObject) JSC.Maybe(JSValue) {

View File

@@ -64,7 +64,7 @@ function header() {
class ${className} final : public JSC::JSDestructibleObject {
public:
using Base = JSC::JSDestructibleObject;
static ${className}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t destructor = 0);
static ${className}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr);
static constexpr SinkID Sink = SinkID::${name};
DECLARE_EXPORT_INFO;
@@ -106,13 +106,11 @@ function header() {
void* m_sinkPtr;
int m_refCount { 1 };
uintptr_t m_onDestroy { 0 };
${className}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy)
${className}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
m_sinkPtr = sinkPtr;
m_onDestroy = onDestroy;
}
void finishCreation(JSC::VM&);
@@ -123,7 +121,7 @@ function header() {
class ${controller} final : public JSC::JSDestructibleObject {
public:
using Base = JSC::JSDestructibleObject;
static ${controller}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy);
static ${controller}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr);
static constexpr SinkID Sink = SinkID::${name};
DECLARE_EXPORT_INFO;
@@ -162,13 +160,10 @@ function header() {
mutable WriteBarrier<JSC::Unknown> m_onClose;
mutable JSC::Weak<JSObject> m_weakReadableStream;
uintptr_t m_onDestroy { 0 };
${controller}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy)
${controller}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
: Base(vm, structure)
{
m_sinkPtr = sinkPtr;
m_onDestroy = onDestroy;
}
void finishCreation(JSC::VM&);
@@ -566,10 +561,6 @@ const ClassInfo ${controller}::s_info = { "${controllerName}"_s, &Base::s_info,
${className}::~${className}()
{
if (m_onDestroy) {
Bun__onSinkDestroyed(m_onDestroy, m_sinkPtr);
}
if (m_sinkPtr) {
${name}__finalize(m_sinkPtr);
}
@@ -578,10 +569,6 @@ ${className}::~${className}()
${controller}::~${controller}()
{
if (m_onDestroy) {
Bun__onSinkDestroyed(m_onDestroy, m_sinkPtr);
}
if (m_sinkPtr) {
${name}__finalize(m_sinkPtr);
}
@@ -598,12 +585,6 @@ JSObject* JS${controllerName}::createPrototype(VM& vm, JSDOMGlobalObject& global
}
void JS${controllerName}::detach() {
if (m_onDestroy) {
auto destroy = m_onDestroy;
m_onDestroy = 0;
Bun__onSinkDestroyed(destroy, m_sinkPtr);
}
m_sinkPtr = nullptr;
m_onPull.clear();
@@ -635,16 +616,16 @@ ${constructor}* ${constructor}::create(JSC::VM& vm, JSC::JSGlobalObject* globalO
return ptr;
}
${className}* ${className}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy)
${className}* ${className}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr)
{
${className}* ptr = new (NotNull, JSC::allocateCell<${className}>(vm)) ${className}(vm, structure, sinkPtr, onDestroy);
${className}* ptr = new (NotNull, JSC::allocateCell<${className}>(vm)) ${className}(vm, structure, sinkPtr);
ptr->finishCreation(vm);
return ptr;
}
${controller}* ${controller}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy)
${controller}* ${controller}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr)
{
${controller}* ptr = new (NotNull, JSC::allocateCell<${controller}>(vm)) ${controller}(vm, structure, sinkPtr, onDestroy);
${controller}* ptr = new (NotNull, JSC::allocateCell<${controller}>(vm)) ${controller}(vm, structure, sinkPtr);
ptr->finishCreation(vm);
return ptr;
}
@@ -697,16 +678,6 @@ void ${controller}::finishCreation(VM& vm)
ASSERT(inherits(info()));
}
extern "C" void ${name}__setDestroyCallback(EncodedJSValue encodedValue, uintptr_t callback)
{
JSValue value = JSValue::decode(encodedValue);
if (auto* sink = JSC::jsDynamicCast<WebCore::${className}*>(value)) {
sink->m_onDestroy = callback;
} else if (auto* controller = JSC::jsDynamicCast<WebCore::${controller}*>(value)) {
controller->m_onDestroy = callback;
}
}
void ${className}::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer)
{
auto* thisObject = jsCast<${className}*>(cell);
@@ -844,12 +815,12 @@ default:
const { className, controller, prototypeName, controllerPrototypeName, constructor } = names(name);
templ += `
extern "C" JSC__JSValue ${name}__createObject(JSC__JSGlobalObject* arg0, void* sinkPtr, uintptr_t destructor)
extern "C" JSC__JSValue ${name}__createObject(JSC__JSGlobalObject* arg0, void* sinkPtr)
{
auto& vm = arg0->vm();
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg0);
JSC::Structure* structure = globalObject->${name}Structure();
return JSC::JSValue::encode(WebCore::JS${name}::create(vm, globalObject, structure, sinkPtr, destructor));
return JSC::JSValue::encode(WebCore::JS${name}::create(vm, globalObject, structure, sinkPtr));
}
extern "C" void* ${name}__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1)
@@ -883,7 +854,7 @@ extern "C" JSC__JSValue ${name}__assignToStream(JSC__JSGlobalObject* arg0, JSC__
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg0);
JSC::Structure* structure = WebCore::getDOMStructure<WebCore::${controller}>(vm, *globalObject);
WebCore::${controller} *controller = WebCore::${controller}::create(vm, globalObject, structure, sinkPtr, 0);
WebCore::${controller} *controller = WebCore::${controller}::create(vm, globalObject, structure, sinkPtr);
*controllerValue = reinterpret_cast<void*>(JSC::JSValue::encode(controller));
return globalObject->assignToStream(JSC::JSValue::decode(stream), controller);
}

View File

@@ -246,36 +246,7 @@ pub const ShellSubprocess = struct {
}
}
pub fn toJS(this: *Writable, globalThis: *JSC.JSGlobalObject, subprocess: *Subprocess) JSValue {
return switch (this.*) {
.fd => |fd| JSValue.jsNumber(fd),
.memfd, .ignore => JSValue.jsUndefined(),
.buffer, .inherit => JSValue.jsUndefined(),
.pipe => |pipe| {
this.* = .{ .ignore = {} };
if (subprocess.process.hasExited() and !subprocess.flags.has_stdin_destructor_called) {
pipe.onAttachedProcessExit();
return pipe.toJS(globalThis);
} else {
subprocess.flags.has_stdin_destructor_called = false;
subprocess.weak_file_sink_stdin_ptr = pipe;
return pipe.toJSWithDestructor(
globalThis,
JSC.WebCore.SinkDestructor.Ptr.init(subprocess),
);
}
},
};
}
pub fn finalize(this: *Writable) void {
const subprocess: *Subprocess = @fieldParentPtr("stdin", this);
if (subprocess.this_jsvalue != .zero) {
if (JSC.Codegen.JSSubprocess.stdinGetCached(subprocess.this_jsvalue)) |existing_value| {
JSC.WebCore.FileSink.JSSink.setDestroyCallback(existing_value, 0);
}
}
return switch (this.*) {
.pipe => |pipe| {
pipe.deref();

View File

@@ -0,0 +1,31 @@
const jsc = require("bun:jsc");
for (let i = 0; i < 30; i++) {
test(`this used to crash :: ${i}`, async () => {
const buffer = Buffer.alloc(1024 * 1024, "a");
async function getStdin() {
{
let subprocess = Bun.spawn({
cmd: [process.argv0, "-e", "Bun.sleep(100)"],
stdio: ["pipe", "ignore", "ignore"],
});
subprocess.unref();
subprocess.stdin.write(buffer);
process.kill(subprocess.pid, "SIGKILL");
await subprocess.stdin.end().catch(() => {});
}
}
async function iter() {
await getStdin();
}
await Promise.all(Array.from({ length: 200 }, () => iter()));
Bun.gc(true);
await Bun.sleep(10);
const { objectTypeCounts } = jsc.heapStats();
console.log("objectTypeCounts:", objectTypeCounts.FileSink, objectTypeCounts.Subprocess);
console.log("RSS", (process.memoryUsage.rss() / 1024 / 1024) | 0, "MB");
});
}