From ef4165139332f27ec0f500c6a0fe04850249c47d Mon Sep 17 00:00:00 2001 From: Alistair Smith Date: Mon, 18 May 2026 21:11:26 +0100 Subject: [PATCH 1/7] semver: widen comparator counter to u32 to avoid debug overflow panic --- src/semver/SemverQuery.rs | 6 +++++- test/cli/install/semver.test.ts | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/semver/SemverQuery.rs b/src/semver/SemverQuery.rs index 543faf66e782..4f83e01ba0f9 100644 --- a/src/semver/SemverQuery.rs +++ b/src/semver/SemverQuery.rs @@ -722,7 +722,11 @@ pub fn parse(input: &[u8], sliced: SlicedString) -> Result { let mut token = Token::default(); let mut prev_token = Token::default(); - let mut count: u8 = 0; + // u32, not u8: a range with >=256 `||`/whitespace comparators overflows a + // u8 on the 256th increment. Zig's ReleaseFast wrapped (256 -> 0) silently; + // Rust's debug profile has overflow-checks and would panic+abort. `count` + // is only ever compared `== 0`, so the wider type is strictly more correct. + let mut count: u32 = 0; let mut skip_round; let mut is_or = false; diff --git a/test/cli/install/semver.test.ts b/test/cli/install/semver.test.ts index 80d3ae62c262..65ebe6dfe169 100644 --- a/test/cli/install/semver.test.ts +++ b/test/cli/install/semver.test.ts @@ -14,6 +14,7 @@ // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +import { bunEnv, bunExe } from "harness"; import { unsortedPrereleases } from "./semver-fixture.js"; const { satisfies, order } = Bun.semver; @@ -738,3 +739,18 @@ describe("Bun.semver.satisfies()", () => { expect(unsortedPrereleases.sort(Bun.semver.order)).toMatchSnapshot(); }); }); + +test("a version range with >=256 || comparators does not abort", async () => { + const range = Array(300).fill("1.0.0").join(" || "); + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", `process.stdout.write(String(Bun.semver.satisfies("1.0.0", ${JSON.stringify(range)})))`], + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + const [stdout, exitCode] = await Promise.all([proc.stdout.text(), proc.exited]); + // The 256th comparator overflowed a u8 counter -> panic+abort (exit 133) in + // debug builds. Fixed: a wider counter parses it fine, like released Bun. + expect(stdout).toBe("true"); + expect(exitCode).toBe(0); +}); From ef9b2b6176fe57b2a9c4997cf21cd82427a56e42 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 18 May 2026 20:30:24 +0000 Subject: [PATCH 2/7] Remove unnecessary inline comments --- src/semver/SemverQuery.rs | 4 ---- test/cli/install/semver.test.ts | 2 -- 2 files changed, 6 deletions(-) diff --git a/src/semver/SemverQuery.rs b/src/semver/SemverQuery.rs index 4f83e01ba0f9..13992719057e 100644 --- a/src/semver/SemverQuery.rs +++ b/src/semver/SemverQuery.rs @@ -722,10 +722,6 @@ pub fn parse(input: &[u8], sliced: SlicedString) -> Result { let mut token = Token::default(); let mut prev_token = Token::default(); - // u32, not u8: a range with >=256 `||`/whitespace comparators overflows a - // u8 on the 256th increment. Zig's ReleaseFast wrapped (256 -> 0) silently; - // Rust's debug profile has overflow-checks and would panic+abort. `count` - // is only ever compared `== 0`, so the wider type is strictly more correct. let mut count: u32 = 0; let mut skip_round; let mut is_or = false; diff --git a/test/cli/install/semver.test.ts b/test/cli/install/semver.test.ts index 65ebe6dfe169..03c0ec8ed0d6 100644 --- a/test/cli/install/semver.test.ts +++ b/test/cli/install/semver.test.ts @@ -749,8 +749,6 @@ test("a version range with >=256 || comparators does not abort", async () => { stderr: "pipe", }); const [stdout, exitCode] = await Promise.all([proc.stdout.text(), proc.exited]); - // The 256th comparator overflowed a u8 counter -> panic+abort (exit 133) in - // debug builds. Fixed: a wider counter parses it fine, like released Bun. expect(stdout).toBe("true"); expect(exitCode).toBe(0); }); From b5e51dbd392d0215fadc80fb238b5d848853d9d8 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 18 May 2026 20:34:06 +0000 Subject: [PATCH 3/7] test: surface stderr on subprocess failure --- src/semver/SemverQuery.rs | 2 +- test/cli/install/semver.test.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/semver/SemverQuery.rs b/src/semver/SemverQuery.rs index 13992719057e..543faf66e782 100644 --- a/src/semver/SemverQuery.rs +++ b/src/semver/SemverQuery.rs @@ -722,7 +722,7 @@ pub fn parse(input: &[u8], sliced: SlicedString) -> Result { let mut token = Token::default(); let mut prev_token = Token::default(); - let mut count: u32 = 0; + let mut count: u8 = 0; let mut skip_round; let mut is_or = false; diff --git a/test/cli/install/semver.test.ts b/test/cli/install/semver.test.ts index 03c0ec8ed0d6..ae1e24f49b32 100644 --- a/test/cli/install/semver.test.ts +++ b/test/cli/install/semver.test.ts @@ -748,7 +748,8 @@ test("a version range with >=256 || comparators does not abort", async () => { stdout: "pipe", stderr: "pipe", }); - const [stdout, exitCode] = await Promise.all([proc.stdout.text(), proc.exited]); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); expect(stdout).toBe("true"); + if (exitCode !== 0) expect(stderr).toBe(""); expect(exitCode).toBe(0); }); From 90793cd9561a2008489f36b6ac67f0afc30dbaec Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 18 May 2026 20:35:31 +0000 Subject: [PATCH 4/7] Restore u32 counter (accidentally reverted in b5e51dbd39) --- src/semver/SemverQuery.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semver/SemverQuery.rs b/src/semver/SemverQuery.rs index 543faf66e782..13992719057e 100644 --- a/src/semver/SemverQuery.rs +++ b/src/semver/SemverQuery.rs @@ -722,7 +722,7 @@ pub fn parse(input: &[u8], sliced: SlicedString) -> Result { let mut token = Token::default(); let mut prev_token = Token::default(); - let mut count: u8 = 0; + let mut count: u32 = 0; let mut skip_round; let mut is_or = false; From 0a802c5b5edf18c2525559afc26d9e4b38d6eae1 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 18 May 2026 22:16:16 +0000 Subject: [PATCH 5/7] Unblock debug build and give slow debug tests headroom - wtf-bindings.cpp: assert -> ASSERT (same as #30992; #30705 dropped the transitive include so debug builds fail to compile) - semver.test.ts: 30s timeout on the 1e5-iteration memory-churn test and the subprocess regression test; both exceed the 5s default under debug+ASAN on constrained runners --- src/jsc/bindings/wtf-bindings.cpp | 2 +- test/cli/install/semver.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jsc/bindings/wtf-bindings.cpp b/src/jsc/bindings/wtf-bindings.cpp index 0d4968f805fc..8b09b5a8665f 100644 --- a/src/jsc/bindings/wtf-bindings.cpp +++ b/src/jsc/bindings/wtf-bindings.cpp @@ -61,7 +61,7 @@ extern "C" int uv_tty_reset_mode(void) static void uv__tty_make_raw(struct termios* tio) { - assert(tio != NULL); + ASSERT(tio != NULL); #if defined __sun || defined __MVS__ /* diff --git a/test/cli/install/semver.test.ts b/test/cli/install/semver.test.ts index ae1e24f49b32..5cf9ead067cb 100644 --- a/test/cli/install/semver.test.ts +++ b/test/cli/install/semver.test.ts @@ -227,7 +227,7 @@ describe("Bun.semver.satisfies()", () => { } } Bun.gc(true); - }); + }, 30_000); test("exact versions", () => { testSatisfiesExact("1.2.3", "1.2.3", true); @@ -752,4 +752,4 @@ test("a version range with >=256 || comparators does not abort", async () => { expect(stdout).toBe("true"); if (exitCode !== 0) expect(stderr).toBe(""); expect(exitCode).toBe(0); -}); +}, 30_000); From 6c0c6789f431be5637e82c7b42cb82dfca18f505 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 18 May 2026 22:48:05 +0000 Subject: [PATCH 6/7] ci: retrigger From e21ce998a90551dc62d009ef118f8071ce0b96b2 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Tue, 19 May 2026 00:08:28 +0000 Subject: [PATCH 7/7] test: check stderr before stdout so panic backtrace surfaces in the diff --- test/cli/install/semver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cli/install/semver.test.ts b/test/cli/install/semver.test.ts index 5cf9ead067cb..58aaadf6db2a 100644 --- a/test/cli/install/semver.test.ts +++ b/test/cli/install/semver.test.ts @@ -749,7 +749,7 @@ test("a version range with >=256 || comparators does not abort", async () => { stderr: "pipe", }); const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect(stdout).toBe("true"); if (exitCode !== 0) expect(stderr).toBe(""); + expect(stdout).toBe("true"); expect(exitCode).toBe(0); }, 30_000);