Compare commits

...

1 Commits

Author SHA1 Message Date
Claude
3823b28106 fix(socket): handle missing argument in Listener.getsockname()
When `getsockname()` was called without an object argument (e.g. via
`listener.getsockname()`), the code passed `undefined` to `put()` which
called `JSC__JSValue__putBunString` in C++. That function called
`getObject()` on the undefined value, returning null, then dereferenced
it causing a null pointer crash.

Create a new empty object when no object argument is provided, and
return it directly. When an object argument is provided (internal
node:net usage), maintain the existing behavior of populating the
object and returning undefined.
2026-02-20 22:36:03 +00:00
2 changed files with 43 additions and 2 deletions

View File

@@ -850,7 +850,9 @@ pub fn getsockname(this: *Listener, globalThis: *jsc.JSGlobalObject, callFrame:
return .js_undefined;
}
const out = callFrame.argumentsAsArray(1)[0];
const arg = callFrame.argumentsAsArray(1)[0];
const have_out = arg.isObject();
const out = if (have_out) arg else JSValue.createEmptyObject(globalThis, 3);
const socket = this.listener.uws;
var buf: [64]u8 = [_]u8{0} ** 64;
@@ -872,7 +874,9 @@ pub fn getsockname(this: *Listener, globalThis: *jsc.JSGlobalObject, callFrame:
out.put(globalThis, bun.String.static("family"), family_js);
out.put(globalThis, bun.String.static("address"), address_js);
out.put(globalThis, bun.String.static("port"), port_js);
return .js_undefined;
// When called with an object argument (internal node:net usage), return
// undefined to indicate success. Otherwise return the newly created object.
return if (have_out) .js_undefined else out;
}
pub fn jsAddServerName(global: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {

View File

@@ -295,6 +295,43 @@ describe("tcp socket binaryType", () => {
}
});
it("getsockname without arguments should not crash", () => {
using server = Bun.listen({
socket: {
open() {},
close() {},
data() {},
},
port: 0,
hostname: "127.0.0.1",
});
const result = server.getsockname();
expect(result).toBeObject();
expect(result.family).toBe("IPv4");
expect(result.address).toBe("127.0.0.1");
expect(result.port).toBe(server.port);
});
it("getsockname with object argument populates the object", () => {
using server = Bun.listen({
socket: {
open() {},
close() {},
data() {},
},
port: 0,
hostname: "127.0.0.1",
});
const out: Record<string, any> = {};
const result = server.getsockname(out);
expect(result).toBeUndefined();
expect(out.family).toBe("IPv4");
expect(out.address).toBe("127.0.0.1");
expect(out.port).toBe(server.port);
});
it("should not leak memory", async () => {
// assert we don't leak the sockets
// we expect 1 or 2 because that's the prototype / structure