Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix illegal memory reference in bun link #2147

Merged
merged 1 commit into from
Feb 24, 2023
Merged
Show file tree
Hide file tree
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
31 changes: 17 additions & 14 deletions src/install/resolvers/folder_resolver.zig
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub const FolderResolution = union(Tag) {
return std.hash.Wyhash.hash(0, normalized_path);
}

pub fn NewResolver(comptime tag: Resolution.Tag) type {
fn NewResolver(comptime tag: Resolution.Tag) type {
return struct {
folder_path: string,

Expand All @@ -50,11 +50,10 @@ pub const FolderResolution = union(Tag) {
};
}

pub const Resolver = NewResolver(Resolution.Tag.folder);
pub const SymlinkResolver = NewResolver(Resolution.Tag.symlink);
pub const WorkspaceResolver = NewResolver(Resolution.Tag.workspace);
pub const CacheFolderResolver = struct {
folder_path: []const u8 = "",
const Resolver = NewResolver(Resolution.Tag.folder);
const SymlinkResolver = NewResolver(Resolution.Tag.symlink);
const WorkspaceResolver = NewResolver(Resolution.Tag.workspace);
const CacheFolderResolver = struct {
version: Semver.Version,

pub fn resolve(this: @This(), comptime Builder: type, _: Builder, _: JSAst.Expr) !Resolution {
Expand Down Expand Up @@ -182,14 +181,18 @@ pub const FolderResolution = union(Tag) {
if (entry.found_existing) return entry.value_ptr.*;

const package: Lockfile.Package = switch (global_or_relative) {
.global => readPackageJSONFromDisk(
manager,
abs,
version,
Features.link,
SymlinkResolver,
SymlinkResolver{ .folder_path = non_normalized_path },
),
.global => brk: {
var path: [bun.MAX_PATH_BYTES]u8 = undefined;
std.mem.copy(u8, &path, non_normalized_path);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the future we should use bun.copy or @memcpy because std.mem.copy is slow

break :brk readPackageJSONFromDisk(
manager,
abs,
version,
Features.link,
SymlinkResolver,
SymlinkResolver{ .folder_path = path[0..non_normalized_path.len] },
);
},
.relative => |tag| switch (tag) {
.folder => readPackageJSONFromDisk(
manager,
Expand Down
142 changes: 133 additions & 9 deletions test/bun.js/install/bun-link.test.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
import { spawn } from "bun";
import { afterEach, beforeEach, expect, it } from "bun:test";
import { afterAll, afterEach, beforeAll, beforeEach, expect, it } from "bun:test";
import { bunExe } from "bunExe";
import { bunEnv as env } from "bunEnv";
import { mkdtemp, realpath, rm, writeFile } from "fs/promises";
import { access, mkdtemp, readlink, realpath, rm, writeFile } from "fs/promises";
import { basename, join } from "path";
import { tmpdir } from "os";
import {
dummyAfterAll,
dummyAfterEach,
dummyBeforeAll,
dummyBeforeEach,
package_dir,
readdirSorted,
} from "./dummy.registry";

let package_dir, link_dir;
beforeAll(dummyBeforeAll);
afterAll(dummyAfterAll);

let link_dir;

beforeEach(async () => {
link_dir = await mkdtemp(join(await realpath(tmpdir()), "bun-link.test"));
package_dir = await mkdtemp(join(await realpath(tmpdir()), "bun-link.pkg"));
await dummyBeforeEach();
});
afterEach(async () => {
await rm(link_dir, { force: true, recursive: true });
await rm(package_dir, { force: true, recursive: true });
await dummyAfterEach();
});

it("should link package", async () => {
var link_name = basename(link_dir).slice("bun-link.".length);
const link_name = basename(link_dir).slice("bun-link.".length);
await writeFile(
join(link_dir, "package.json"),
JSON.stringify({
Expand Down Expand Up @@ -114,13 +125,12 @@ it("should link package", async () => {
const err4 = await new Response(stderr4).text();
expect(err4).toContain(`error: package "${link_name}" is not linked`);
expect(stdout4).toBeDefined();
const out4 = await new Response(stdout4).text();
expect(await new Response(stdout4).text()).toBe("");
expect(await exited4).toBe(1);
});

it("should link scoped package", async () => {
var link_name = `@${basename(link_dir).slice("bun-link.".length)}/foo`;
const link_name = `@${basename(link_dir).slice("bun-link.".length)}/foo`;
await writeFile(
join(link_dir, "package.json"),
JSON.stringify({
Expand Down Expand Up @@ -216,7 +226,121 @@ it("should link scoped package", async () => {
const err4 = await new Response(stderr4).text();
expect(err4).toContain(`error: package "${link_name}" is not linked`);
expect(stdout4).toBeDefined();
const out4 = await new Response(stdout4).text();
expect(await new Response(stdout4).text()).toBe("");
expect(await exited4).toBe(1);
});

it("should link dependency without crashing", async () => {
const link_name = basename(link_dir).slice("bun-link.".length) + "-really-long-name";
await writeFile(
join(link_dir, "package.json"),
JSON.stringify({
name: link_name,
version: "0.0.1",
bin: {
[link_name]: `${link_name}.js`,
},
}),
);
await writeFile(join(link_dir, `${link_name}.js`), "console.log(42);");
await writeFile(
join(package_dir, "package.json"),
JSON.stringify({
name: "foo",
version: "0.0.2",
dependencies: {
[link_name]: `link:${link_name}`,
},
}),
);

const {
stdout: stdout1,
stderr: stderr1,
exited: exited1,
} = spawn({
cmd: [bunExe(), "link"],
cwd: link_dir,
stdout: null,
stdin: "pipe",
stderr: "pipe",
env,
});
expect(stderr1).toBeDefined();
const err1 = await new Response(stderr1).text();
expect(err1.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual(["bun link", ""]);
expect(stdout1).toBeDefined();
expect(await new Response(stdout1).text()).toContain(`Success! Registered \\"${link_name}\\"`);
expect(await exited1).toBe(0);

const {
stdout: stdout2,
stderr: stderr2,
exited: exited2,
} = spawn({
cmd: [bunExe(), "install", "--config", import.meta.dir + "/basic.toml"],
cwd: package_dir,
stdout: null,
stdin: "pipe",
stderr: "pipe",
env,
});
expect(stderr2).toBeDefined();
const err2 = await new Response(stderr2).text();
expect(err2.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual(["bun install", " Saved lockfile", ""]);
expect(stdout2).toBeDefined();
const out2 = await new Response(stdout2).text();
expect(out2.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
` + ${link_name}@link:${link_name}`,
"",
" 1 packages installed",
]);
expect(await exited2).toBe(0);
expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([".bin", ".cache", link_name].sort());
expect(await readdirSorted(join(package_dir, "node_modules", ".bin"))).toEqual([link_name]);
expect(await readlink(join(package_dir, "node_modules", ".bin", link_name))).toBe(
join("..", link_name, `${link_name}.js`),
);
expect(await readdirSorted(join(package_dir, "node_modules", link_name))).toEqual(
["package.json", `${link_name}.js`].sort(),
);
await access(join(package_dir, "bun.lockb"));

const {
stdout: stdout3,
stderr: stderr3,
exited: exited3,
} = spawn({
cmd: [bunExe(), "unlink"],
cwd: link_dir,
stdout: null,
stdin: "pipe",
stderr: "pipe",
env,
});
expect(stderr3).toBeDefined();
const err3 = await new Response(stderr3).text();
expect(err3.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual(["bun unlink", ""]);
expect(stdout3).toBeDefined();
expect(await new Response(stdout3).text()).toContain(`success: unlinked package "${link_name}"`);
expect(await exited3).toBe(0);

const {
stdout: stdout4,
stderr: stderr4,
exited: exited4,
} = spawn({
cmd: [bunExe(), "install"],
cwd: package_dir,
stdout: null,
stdin: "pipe",
stderr: "pipe",
env,
});
expect(stderr4).toBeDefined();
const err4 = await new Response(stderr4).text();
expect(err4).toContain(`error: FileNotFound installing ${link_name}`);
expect(stdout4).toBeDefined();
expect(await new Response(stdout4).text()).toBe("");
expect(await exited4).toBe(0);
});
Binary file modified test/fixtures/bun-link-to-pkg-fixture/bun.lockb
Binary file not shown.