mirror of
https://github.com/oven-sh/bun
synced 2026-02-12 03:48:56 +00:00
## Summary - Fixes segmentation fault when applying patches with out-of-bounds line numbers - Adds comprehensive bounds checking in patch application logic - Includes regression tests to prevent future issues ## Problem Previously, malformed patches with line numbers beyond file bounds could cause segmentation faults by attempting to access memory beyond allocated array bounds in `addManyAt()` and `replaceRange()` calls. ## Solution Added bounds validation at four key points in `src/patch.zig`: 1. **Hunk start position validation** (line 283-286) - Ensures hunk starts within file bounds 2. **Context line validation** (line 294-297) - Validates context lines exist within bounds 3. **Insertion position validation** (line 302-305) - Checks insertion position is valid 4. **Deletion range validation** (line 317-320) - Ensures deletion range is within bounds All bounds violations now return `EINVAL` error gracefully instead of crashing. ## Test Coverage Added comprehensive regression tests in `test/regression/issue/patch-bounds-check.test.ts`: - ✅ Out-of-bounds insertion attempts - ✅ Out-of-bounds deletion attempts - ✅ Out-of-bounds context line validation - ✅ Valid patch application (positive test case) Tests verify that `bun install` completes gracefully when encountering malformed patches, with no crashes or memory corruption. ## Test Results ``` bun test v1.2.21 ✅ Bounds checking working: bun install completed gracefully despite malformed patch ✅ Bounds checking working: bun install completed gracefully despite deletion beyond bounds ✅ Bounds checking working: bun install completed gracefully despite context lines beyond bounds 4 pass 0 fail 22 expect() calls Ran 4 tests across 1 file. [4.70s] ``` 🤖 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> Co-authored-by: Jarred Sumner <jarred@jarredsumner.com> Co-authored-by: Zack Radisic <56137411+zackradisic@users.noreply.github.com>
144 lines
4.2 KiB
TypeScript
144 lines
4.2 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { bunEnv, bunExe, normalizeBunSnapshot as normalizeBunSnapshot_, tempDirWithFiles } from "harness";
|
|
|
|
const normalizeBunSnapshot = (str: string) => {
|
|
str = normalizeBunSnapshot_(str);
|
|
str = str.replace(/.*Resolved, downloaded and extracted.*\n?/g, "");
|
|
str = str.replaceAll("fstatat()", "stat()");
|
|
return str;
|
|
};
|
|
|
|
test("patch application should handle out-of-bounds line numbers gracefully", async () => {
|
|
const dir = tempDirWithFiles("patch-bounds-test", {
|
|
"package.json": JSON.stringify({
|
|
name: "test-pkg",
|
|
version: "1.0.0",
|
|
dependencies: {
|
|
"lodash": "4.17.21",
|
|
},
|
|
patchedDependencies: {
|
|
"lodash@4.17.21": "patches/lodash+4.17.21.patch",
|
|
},
|
|
}),
|
|
"patches/lodash+4.17.21.patch": `--- a/index.js
|
|
+++ b/index.js
|
|
@@ -1000,3 +1000,4 @@
|
|
module.exports = require('./lodash');
|
|
|
|
// This line doesn't exist but the patch says it does
|
|
+// Add this line way beyond the actual file bounds`,
|
|
});
|
|
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "install"],
|
|
env: bunEnv,
|
|
cwd: dir,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
// Should fail gracefully with proper error message, not crash
|
|
expect(exitCode).toBe(1);
|
|
expect(normalizeBunSnapshot(stderr)).toMatchInlineSnapshot(`
|
|
"Resolving dependencies
|
|
error: failed applying patch file: EINVAL: Invalid argument (stat())
|
|
error: failed to apply patchfile (patches/lodash+4.17.21.patch)"
|
|
`);
|
|
expect(normalizeBunSnapshot(stdout)).toMatchInlineSnapshot(`"bun install <version> (<revision>)"`);
|
|
});
|
|
|
|
test("patch application should handle deletion beyond file bounds", async () => {
|
|
const dir = tempDirWithFiles("patch-deletion-bounds-test", {
|
|
"package.json": JSON.stringify({
|
|
name: "test-pkg",
|
|
version: "1.0.0",
|
|
dependencies: {
|
|
"lodash": "4.17.21",
|
|
},
|
|
patchedDependencies: {
|
|
"lodash@4.17.21": "patches/lodash+4.17.21.patch",
|
|
},
|
|
}),
|
|
"patches/lodash+4.17.21.patch": `--- a/index.js
|
|
+++ b/index.js
|
|
@@ -1,5 +1,3 @@
|
|
module.exports = require('./lodash');
|
|
-line 2
|
|
-line 3
|
|
-line 4
|
|
-line 5`,
|
|
});
|
|
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "install"],
|
|
env: bunEnv,
|
|
cwd: dir,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
// Should fail gracefully, not crash
|
|
expect(exitCode).toBe(1);
|
|
expect(normalizeBunSnapshot(stderr)).toMatchInlineSnapshot(`
|
|
"Resolving dependencies
|
|
error: failed to parse patchfile: hunk_header_integrity_check_failed
|
|
error: failed to apply patchfile (patches/lodash+4.17.21.patch)"
|
|
`);
|
|
expect(normalizeBunSnapshot(stdout)).toMatchInlineSnapshot(`"bun install <version> (<revision>)"`);
|
|
});
|
|
|
|
test("patch application should work correctly with valid patches", async () => {
|
|
const dir = tempDirWithFiles("patch-valid-test", {
|
|
"package.json": JSON.stringify({
|
|
name: "test-pkg",
|
|
version: "1.0.0",
|
|
dependencies: {
|
|
"lodash": "4.17.21",
|
|
},
|
|
patchedDependencies: {
|
|
"lodash@4.17.21": "patches/lodash+4.17.21.patch",
|
|
},
|
|
}),
|
|
"patches/lodash+4.17.21.patch": `--- a/index.js
|
|
+++ b/index.js
|
|
@@ -1 +1,2 @@
|
|
+// Valid patch comment
|
|
module.exports = require('./lodash');`,
|
|
});
|
|
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "install"],
|
|
env: bunEnv,
|
|
cwd: dir,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
// Valid patch should succeed
|
|
expect(exitCode).toBe(0);
|
|
expect(normalizeBunSnapshot(stderr)).toMatchInlineSnapshot(`
|
|
"Resolving dependencies
|
|
Saved lockfile"
|
|
`);
|
|
expect(normalizeBunSnapshot(stdout)).toMatchInlineSnapshot(`
|
|
"bun install <version> (<revision>)
|
|
|
|
+ lodash@4.17.21
|
|
|
|
1 package installed"
|
|
`);
|
|
|
|
// Verify the patch was applied
|
|
const patchedFile = await Bun.file(`${dir}/node_modules/lodash/index.js`).text();
|
|
expect(patchedFile).toMatchInlineSnapshot(`
|
|
"// Valid patch comment
|
|
module.exports = require('./lodash');"
|
|
`);
|
|
});
|