mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 10:58:56 +00:00
## Summary - Fixed a debug build assertion failure in PostgreSQL error handling when all error message fields are empty - Added safety check before calling `StringBuilder.allocatedSlice()` to handle zero-length messages - Added regression test to prevent future occurrences ## The Problem When PostgreSQL sends an error response with completely empty message fields, the `ErrorResponse.toJS` function would: 1. Calculate `b.cap` but end up with `b.len = 0` (no actual content) 2. Call `b.allocatedSlice()[0..b.len]` unconditionally 3. Trigger an assertion in `StringBuilder.allocatedSlice()` that requires `cap > 0` This only affected debug builds since the assertion is compiled out in release builds. ## The Fix Check if `b.len > 0` before calling `allocatedSlice()`. If there's no content, use an empty string instead. ## Test Plan - [x] Added regression test that triggers the exact crash scenario - [x] Verified test crashes without the fix (debug build) - [x] Verified test passes with the fix - [x] Confirmed release builds were not affected 🤖 Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
47 lines
1.3 KiB
TypeScript
47 lines
1.3 KiB
TypeScript
import { SQL } from "bun";
|
|
import { expect, test } from "bun:test";
|
|
import net from "net";
|
|
|
|
test("PostgreSQL StringBuilder assertion - aggressive empty error test", async () => {
|
|
const server = net.createServer(socket => {
|
|
// Immediately send an error response with completely empty fields
|
|
// This is more likely to trigger the assertion
|
|
socket.write(createPostgresPacket("E", Buffer.from("\0"))); // Terminator only
|
|
socket.destroy();
|
|
});
|
|
|
|
await new Promise<void>(r => server.listen(0, "127.0.0.1", () => r()));
|
|
const port = (server.address() as net.AddressInfo).port;
|
|
|
|
const sql = new SQL({
|
|
url: `postgres://test@127.0.0.1:${port}/test`,
|
|
max: 10,
|
|
connection_timeout: 0.1,
|
|
});
|
|
|
|
const promises = [];
|
|
for (let i = 0; i < 20; i++) {
|
|
promises.push(
|
|
sql`SELECT ${i}`.catch(err => {
|
|
// We expect errors, just checking for crashes
|
|
return null;
|
|
}),
|
|
);
|
|
}
|
|
|
|
await Promise.all(promises);
|
|
await sql.close();
|
|
server.close();
|
|
|
|
// If we get here without crashing, the test passes
|
|
expect(true).toBe(true);
|
|
});
|
|
|
|
function createPostgresPacket(type: string, data: Buffer): Buffer {
|
|
const len = data.length + 4;
|
|
const header = Buffer.alloc(5);
|
|
header.write(type, 0);
|
|
header.writeInt32BE(len, 1);
|
|
return Buffer.concat([header, data]);
|
|
}
|