diff --git a/src/shell/builtin/rm.zig b/src/shell/builtin/rm.zig index 9fd6ea209f..154022c877 100644 --- a/src/shell/builtin/rm.zig +++ b/src/shell/builtin/rm.zig @@ -185,6 +185,11 @@ pub noinline fn next(this: *Rm) Yield { for (filepath_args) |filepath| { const path = filepath[0..bun.len(filepath)]; + // Skip root check if combined path exceeds buffer size — + // a path that long can't be root, and the join would panic. + // The actual rm operation will return ENAMETOOLONG. + if (!ResolvePath.Platform.auto.isAbsolute(path) and cwd.len + path.len + 1 >= bun.MAX_PATH_BYTES) + continue; const resolved_path = if (ResolvePath.Platform.auto.isAbsolute(path)) path else bun.path.join(&[_][]const u8{ cwd, path }, .auto); const is_root = brk: { const normalized = bun.path.normalizeString(resolved_path, false, .auto); diff --git a/test/js/bun/shell/shell-path-max.test.ts b/test/js/bun/shell/shell-path-max.test.ts index 5f48874317..f6e8446dff 100644 --- a/test/js/bun/shell/shell-path-max.test.ts +++ b/test/js/bun/shell/shell-path-max.test.ts @@ -1,14 +1,14 @@ import { describe, expect, test } from "bun:test"; import { bunEnv, bunExe, isPosix } from "harness"; -// Regression test: touch and mkdir with paths exceeding PATH_MAX (4096) +// Regression test: touch, mkdir, and rm with paths exceeding PATH_MAX (4096) // used to panic with "index out of bounds" in resolve_path.zig. // After the fix, they return ENAMETOOLONG error instead. describe.if(isPosix)("builtins with paths exceeding PATH_MAX should not crash", () => { const longPath = Buffer.alloc(5000, "A").toString(); - test("touch with path > PATH_MAX returns error", async () => { + test.concurrent("touch with path > PATH_MAX returns error", async () => { await using proc = Bun.spawn({ cmd: [ bunExe(), @@ -33,7 +33,7 @@ describe.if(isPosix)("builtins with paths exceeding PATH_MAX should not crash", expect(exitCode).toBe(0); }); - test("mkdir with path > PATH_MAX returns error", async () => { + test.concurrent("mkdir with path > PATH_MAX returns error", async () => { await using proc = Bun.spawn({ cmd: [ bunExe(), @@ -56,4 +56,29 @@ describe.if(isPosix)("builtins with paths exceeding PATH_MAX should not crash", expect(stderr).toContain("File name too long"); expect(exitCode).toBe(0); }); + + test.concurrent("rm with path > PATH_MAX does not crash", async () => { + await using proc = Bun.spawn({ + cmd: [ + bunExe(), + "-e", + ` + import { $ } from "bun"; + $.throws(false); + const r = await $\`rm ${longPath}\`; + console.log("exitCode:" + r.exitCode); + `, + ], + env: { ...bunEnv, longPath }, + stderr: "pipe", + stdout: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + // rm should not crash (exit 132=illegal instruction). It should exit with an error. + expect(stdout).toContain("exitCode:"); + expect(stdout).not.toContain("exitCode:0"); + expect(exitCode).toBe(0); + }); });