Skip to content

procfs: make stat() follow /proc/<pid>/fd links to live files#908

Closed
brandonpayton wants to merge 1 commit into
mainfrom
fix/procfs-follow-stat
Closed

procfs: make stat() follow /proc/<pid>/fd links to live files#908
brandonpayton wants to merge 1 commit into
mainfrom
fix/procfs-follow-stat

Conversation

@brandonpayton

@brandonpayton brandonpayton commented Jul 13, 2026

Copy link
Copy Markdown
Member

Purpose

Make stat() on /proc/<pid>/fd/<number> report the metadata of the file that the descriptor actually has open.

This is the Linux procfs contract:

  • lstat() or AT_SYMLINK_NOFOLLOW describes the procfs link itself.
  • stat(), following fstatat(), and statx() describe the live open file behind that link.

Tools such as fuser compare device and inode values to find which process has a file open. Kandelo previously returned invented procfs regular-file metadata, so those tools could not match a descriptor to the real file.

Root Cause

Kandelo already parsed /proc/<pid>/fd/<number> as an fd link, but every procfs metadata request went through procfs_stat(). That function creates metadata for the synthetic procfs node; it never consulted the owning process's descriptor table or open-file-description table.

The wrong result came from kernel state handling, not from the fuser package or Homebrew.

What Changed

  • Validate that an fd link names a process and a complete, currently open descriptor/OFD pair.
  • Make following stat, fstatat, and statx requests use the same live metadata as fstat(fd).
  • Support both the calling process's descriptors and another Kandelo process's descriptors.
  • Keep lstat and AT_SYMLINK_NOFOLLOW reporting a symlink.
  • Return ENOENT after the process disappears or the descriptor closes.
  • Add one real Wasm fixture that forks a holder process, unlinks its open file, and verifies the remaining fd link from another process.
  • Run that fixture through both the Node and Chromium hosts.
  • Document the corrected procfs behavior.

There is no package-specific behavior in this change.

ABI

No ABI bump is required. This corrects the result of existing stat-family syscalls without changing syscall numbers, arguments, marshalling, pointer interpretation, exported functions, layouts, or binary expectations. Existing ABI 39 programs already request this behavior through the current interface.

ABI_VERSION remains 39. abi/snapshot.json, the generated C header, and the generated TypeScript bindings are unchanged. scripts/check-abi-version.sh passes on this head.

Merge Order

  1. Merge sdk: preserve static linker argument order #913 first.
  2. Rebase this PR onto the resulting main.
  3. Review and merge procfs: make stat() follow /proc/<pid>/fd links to live files #908 as its own platform fix.

Do not merge #908 before #913. PR #913's prepared package artifacts were generated from the current main branch. This procfs fix is intentionally separate and does not need to be absorbed into that package batch.

Validation

Current tested head: 538d884c1, based on exact main 9a7a67919.

Run through scripts/dev-shell.sh:

  • cargo test -p kandelo --target aarch64-apple-darwin --lib procfs_fd_stat_follows_the_live_ofd: 1/1 passed.
  • Fresh release kernel build with cargo build --release -p kandelo -Z build-std=core,alloc: passed.
  • Fresh SDK compile plus fork instrumentation of programs/procfs-foreign-fd-stat.c: passed.
  • npx vitest run --root . test/procfs-fd-stat.test.ts from host/, after removing host/dist: 1/1 passed on the final commit.
  • npx playwright test test/procfs-fd-stat.spec.ts --project=chromium from apps/browser-demos/, on a fresh non-reused Vite port: 1/1 passed on the final commit.
  • bash scripts/check-abi-version.sh: passed; ABI snapshot and generated bindings are in sync.
  • Prettier check for the two new TypeScript fixtures: passed.
  • git diff --check: passed.
  • Range comparison against the previously validated port commit 9260b569d: only Prettier wrapping changed; kernel, guest, browser, and documentation semantics are identical.

The full kernel, Vitest, browser, libc, POSIX, and Sortix suites were not rerun locally; CI owns those broad gates. The declared dev shell does not currently include cargo-fmt, so the local Rust formatting command could not run; CI will provide the authoritative formatting result.

@brandonpayton

Copy link
Copy Markdown
Member Author

Independent exact-head review completed for 4535456f2b0407051372f11b1cfb33937602ae4c: ACCEPT.

  • The single commit is directly based on current main and contains only kernel/procfs, docs, and regression fixtures.
  • Following fd magic links resolves through authoritative process fd/OFD state; lstat preserves the procfs link, missing/reaped targets return ENOENT, and recursion is bounded with ELOOP.
  • Foreign reads do not mutate fd offsets and introduce no new lock ordering; shared kernel behavior serves Node and browser.
  • ABI version, snapshot, C header, and TypeScript bindings are unchanged; the canonical ABI check passes.
  • Kernel 969/969 and the real Wasm fixture pass; the parent negative control reproduces the incorrect regular-file metadata.

No review blocker was found. CI still owns full Vitest, browser, libc, POSIX, and Sortix coverage.

@brandonpayton

Copy link
Copy Markdown
Member Author

Post-#907 integration evidence (no change to this PR's current head):

Landing plan: merge #907 first, then restack this existing PR to the equivalent post-#907 delta and rerun CI. Do not open or merge a second procfs PR. The port remains ABI-compatible.

@brandonpayton
brandonpayton force-pushed the fix/procfs-follow-stat branch from 4535456 to 538d884 Compare July 14, 2026 06:10
@brandonpayton brandonpayton changed the title Report procfs magic-link target status procfs: make stat() follow /proc/<pid>/fd links to live files Jul 14, 2026
@brandonpayton

Copy link
Copy Markdown
Member Author

The pre-#913 staging run was intentionally cancelled after focused local and COI evidence passed. #913 has already promoted its future package generation into the shared ABI 39 index, so a fetch-only run from this PRs current pre-#913 base cannot materialize matching package inputs. After #913 merges, rebase #908 and rerun CI against the matching main/index generation; do not add a source-build fallback or weaken the index check.

@brandonpayton

Copy link
Copy Markdown
Member Author

Absorbed by rebase-merged #967. Main is now 6efb411 with the sealed tree b133bfa018f7e2bc84c4845edf2f945b2f844954, and ABI 41 activation completed successfully in run 29550069085. The absorption audit confirmed this PR\x27s behavior and tests are present in that merged tree, so this source PR is closed and its branch is deleted.

@brandonpayton
brandonpayton deleted the fix/procfs-follow-stat branch July 17, 2026 02:38
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