Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion test/js/node/net/socketaddress.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* @see https://nodejs.org/api/net.html#class-netsocketaddress
*/
import { rss } from "harness";
import { expectRssDeltaBelow, rss } from "harness";
import { SocketAddress, SocketAddressInitOptions } from "node:net";

let v4: SocketAddress;
Expand Down Expand Up @@ -132,6 +132,42 @@ describe("SocketAddress constructor", () => {

expect(after).toBeLessThanOrEqual(before * growthFactor);
});

it("does not leak the address string on validation-error paths", async () => {
Comment thread
robobun marked this conversation as resolved.
// Each case reads options.address into a native string and then throws
// from a later validator. The string is 512 KiB, so a leaked ref pins
// about 50 MiB per case over 100 iterations.
const code = /* js */ `
const net = require("node:net");
const big = Buffer.alloc(512 * 1024, "a").toString();
const cases = {
// Options::from_js: a later option validator throws after the address was read
bad_family: i => new net.SocketAddress({ address: big + i, family: "bad!" }),
bad_port: i => new net.SocketAddress({ address: big + i, port: NaN }),
bad_flow_type: i => new net.SocketAddress({ address: big + i, family: "ipv6", flowlabel: "x" }),
bad_flow_range: i => new net.SocketAddress({ address: big + i, family: "ipv6", flowlabel: -1 }),
// init_js: pton rejects an invalid IP after the options were accepted
pton_reject: i => new net.SocketAddress({ address: big + i, family: "ipv4" }),
// init_from_addr_family: AF::from_js throws after the address was read
blocklist_bad_family: i => new net.BlockList().addAddress(big + i, "bad!"),
};
for (const fn of Object.values(cases))
for (let i = 0; i < 20; i++) try { fn(i); } catch {}
Bun.gc(true);
const out = {};
for (const [name, fn] of Object.entries(cases)) {
const before = process.memoryUsage.rss();
for (let i = 0; i < 100; i++) try { fn(i); } catch {}
Bun.gc(true);
out[name] = (process.memoryUsage.rss() - before) / 1024 / 1024;
}
console.log(JSON.stringify(out));
console.log(JSON.stringify({ deltaMiB: Math.max(...Object.values(out)) }));
`;

// Unfixed: about 50 MiB on every path. Fixed: allocator slack only.
await expectRssDeltaBelow(["--smol", "-e", code], { release: 20, debug: 30 });
});
}); // </SocketAddress constructor>

describe("SocketAddress.isSocketAddress", () => {
Expand Down