Skip to content

fix(wasi): resolve path_open paths against preopen directory, not cwd#27724

Open
mkusaka wants to merge 3 commits intooven-sh:mainfrom
mkusaka:fix/wasi-path-open-preopens
Open

fix(wasi): resolve path_open paths against preopen directory, not cwd#27724
mkusaka wants to merge 3 commits intooven-sh:mainfrom
mkusaka:fix/wasi-path-open-preopens

Conversation

@mkusaka
Copy link

@mkusaka mkusaka commented Mar 3, 2026

What does this PR do?

Fixes path_open in the WASI implementation to resolve guest paths against the preopen directory's host path instead of the current working directory.

Bug

When a WASI guest opens a file using an absolute path (e.g., /tmp/foo.txt) with preopens: { "/": "/" }, Bun incorrectly resolves it as <cwd>/tmp/foo.txt instead of /tmp/foo.txt, causing ENOENT.

Root cause: path_open in src/js/node/wasi.ts discards the CHECK_FD() result and calls path.resolve(p), which uses cwd as the base. All other path syscalls (path_readlink, path_create_directory, path_unlink_file, etc.) correctly use path.resolve(stats.path, p).

Fix

Two-line change:

- CHECK_FD(dirfd, constants_1.WASI_RIGHT_PATH_OPEN);
+ const stats = CHECK_FD(dirfd, constants_1.WASI_RIGHT_PATH_OPEN);
  // ... (flag handling unchanged) ...
- const fullUnresolved = path.resolve(p);
+ const fullUnresolved = path.resolve(stats.path, p);

Test

Added a test that reads a temp file via a WASI binary to verify path_open resolves against preopens.

read-file.wasm is a minimal Rust WASI binary (wasm32-wasip1, 91KB) that reads a file path from args and prints contents to stdout. Built with:

// src/main.rs
use std::{env, fs, process};
fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 { eprintln!("usage: read-file <path>"); process::exit(1); }
    match fs::read_to_string(&args[1]) {
        Ok(c) => print!("{}", c),
        Err(e) => { eprintln!("error: {}: {}", args[1], e); process::exit(1); }
    }
}
cargo init --name read-file-wasi
cargo build --target wasm32-wasip1 --release

How did you verify your code works?

  • Confirmed the test fails on unmodified Bun v1.3.10 (ENOENT with doubled path)
  • Confirmed Node.js handles the same WASI binary correctly with identical preopens
  • The fix matches the exact pattern used by all other path syscalls in the same file

mkusaka added 2 commits March 4, 2026 01:29
path_open resolves file paths against cwd instead of the preopen
directory's host path. This causes absolute paths to be doubled
(e.g., /tmp/foo becomes <cwd>/tmp/foo) and fail with ENOENT.

read-file.wasm is a minimal Rust WASI binary (wasm32-wasip1) that
reads a file from args and prints its contents. Built with:
  cargo build --target wasm32-wasip1 --release
path_open was discarding the CHECK_FD result and resolving guest paths
with path.resolve(p), which uses cwd as the base. This caused absolute
paths like /tmp/foo to become <cwd>/tmp/foo, resulting in ENOENT.

Capture the CHECK_FD result and use stats.path (the preopen host path)
as the base directory, matching the pattern used by path_readlink,
path_create_directory, path_unlink_file, and other path syscalls.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 3, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b0783a0 and 8612656.

📒 Files selected for processing (1)
  • test/js/bun/wasm/wasi.test.js

Walkthrough

Path resolution for WASI path_open now resolves relative to the opened directory (preopen) by using the directory's stored path when constructing the unresolved path. A test was added to verify path_open resolves against preopens rather than the current working directory.

Changes

Cohort / File(s) Summary
WASI path resolution
src/js/node/wasi.ts
Updated path_open handling to store the dirfd rights check result in stats and compute fullUnresolved with path.resolve(stats.path, p) so resolution is based on the opened directory.
WASI path resolution test
test/js/bun/wasm/wasi.test.js
Added test "path_open should resolve paths against preopens, not cwd" which creates a temporary directory and file, runs read-file.wasm against the temp path, and asserts expected stdout/stderr/exitCode.
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: fixing path_open to resolve paths against the preopen directory rather than cwd, which is the primary objective of this PR.
Description check ✅ Passed The description comprehensively covers both required template sections: 'What does this PR do?' with detailed bug explanation and fix, and 'How did you verify your code works?' with verification steps.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/js/bun/wasm/wasi.test.js`:
- Around line 4-6: Replace direct use of fs.mkdtempSync with the test harness
helper: import and call tempDir (from harness) instead of mkdtempSync to create
the temporary directory used in this test (references: mkdtempSync -> tempDir).
Remove the explicit tmpdir/mkdtempSync import and any manual cleanup (rmSync)
for that directory and rely on harness-managed cleanup; keep using writeFileSync
and join to create files inside the tempDir path. Update any other occurrences
(lines ~29-31) that create or remove temp dirs to use tempDir consistently.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6c7e972 and b0783a0.

⛔ Files ignored due to path filters (1)
  • test/js/bun/wasm/read-file.wasm is excluded by !**/*.wasm
📒 Files selected for processing (2)
  • src/js/node/wasi.ts
  • test/js/bun/wasm/wasi.test.js

Replace manual mkdtempSync/rmSync with harness's tempDirWithFiles
helper per Bun coding guidelines.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant