Skip to content

fix(fs): extend Node arg-validation to closeSync/readSync/writeSync/fchmodSync/chmodSync/renameSync (#2013)#2328

Merged
proggeramlug merged 1 commit into
mainfrom
worktree-fix-2013-fs-arg-validation
May 28, 2026
Merged

fix(fs): extend Node arg-validation to closeSync/readSync/writeSync/fchmodSync/chmodSync/renameSync (#2013)#2328
proggeramlug merged 1 commit into
mainfrom
worktree-fix-2013-fs-arg-validation

Conversation

@proggeramlug
Copy link
Copy Markdown
Contributor

Summary

PR #2035 added the validation pattern for fs.readFileSync / fs.accessSync / fs.statSync / fs.openSync; the issue's follow-up call-out lists ~27 fs functions that still hit "Missing expected exception" because they don't yet validate. This gap-fills the fd-only sync surface and the path-mutator sync surface so each:

  • throws TypeError [ERR_INVALID_ARG_TYPE] on a non-fd / non-path first argument (matches Node);
  • throws Error [EBADF] on a numeric fd that isn't open in perry-runtime's FD_REGISTRY (matches Node's syscall surface).

Touched entry points:

  • closeSync, readSync, readvSync, writeSync (string + buffer), writevSync, fchmodSync, fsyncSync, fdatasyncSync, fchownSync — fd validation + EBADF
  • chmodSync, renameSync — path validation

New validate_fd_open(value, syscall) helper combines the existing validate_fd type+range probe with an FD_REGISTRY membership check + EBADF throw. Callers that need the validation order preserved (fchownSync: validate uid+gid BEFORE EBADF, per Node's validateInteger ordering) keep the bare validate_fd and issue the EBADF after the rest of the int32 validations. validate_fd itself stays single-purpose so the FileHandle wrappers — which can legitimately hold a -1 sentinel from a failed open — keep their silent no-op fallback.

Mode validation in fchmodSync and chmodSync is deliberately omitted: Node uses parseFileMode which throws ERR_INVALID_ARG_VALUE, not the validate_int32 shape — left as a follow-up to keep the diff focused.

Test plan

  • cargo fmt --all -- --check clean.
  • cargo test --release -p perry-runtime --lib — 746/746 green (no regressions).
  • test-parity/node-suite/fs/sync/arg-validation.ts extended with 13 new probes (closeSync, readSync, readvSync, writeSync ×2, fchmodSync ×2, chmodSync, renameSync ×2 covering both type-error and EBADF branches). Byte-identical against node --experimental-strip-types; the existing legacy block stays byte-identical.

Closes the fd-only + path-mutator slice of #2013; the remaining ~80 tests across buffer / net / zlib / http / process / crypto / url stay tracked under the same issue.

…chmodSync/chmodSync/renameSync (#2013)

PR #2035 added the validation pattern for `fs.readFileSync` /
`fs.accessSync` / `fs.statSync` / `fs.openSync`; the issue's
follow-up call-out lists ~27 fs functions that still hit
"Missing expected exception" because they don't yet validate. This
gap-fills the fd-only sync surface (`closeSync`, `readSync`,
`readvSync`, `writeSync`, `writevSync`, `fchmodSync`, `fsyncSync`,
`fdatasyncSync`, `fchownSync`) and the path-mutator sync surface
(`chmodSync`, `renameSync`) so each:

  - throws `TypeError [ERR_INVALID_ARG_TYPE]` on a non-fd / non-path
    first argument (matches Node);
  - throws `Error [EBADF]` on a numeric fd that isn't open in
    perry-runtime's `FD_REGISTRY` (matches Node's syscall surface).

The new `validate_fd_open(value, syscall)` helper combines the
existing `validate_fd` type+range probe with an `FD_REGISTRY`
membership check + EBADF throw. Callers that need the validation
order preserved (`fchownSync`: validate uid+gid BEFORE EBADF, per
Node's `validateInteger` ordering) keep the bare `validate_fd` and
issue the EBADF after the rest of the int32 validations. `validate_fd`
itself stays single-purpose so the FileHandle wrappers — which can
legitimately hold a `-1` sentinel from a failed open — keep their
silent no-op fallback.

The parity test (`test-parity/node-suite/fs/sync/arg-validation.ts`)
gets 13 new probes covering each new shape against
`node --experimental-strip-types`; the existing legacy block stays
byte-identical. `cargo test -p perry-runtime --lib` still 746/746.
@proggeramlug proggeramlug merged commit 39c22a0 into main May 28, 2026
11 checks passed
@proggeramlug proggeramlug deleted the worktree-fix-2013-fs-arg-validation branch May 28, 2026 23:10
proggeramlug pushed a commit that referenced this pull request May 28, 2026
…hrtime (#2013)

Follow-up to PR #2328 (fs slice). Each process-surface entry point
now throws the Node-matching `TypeError [ERR_INVALID_ARG_TYPE]` (or
`ERR_UNKNOWN_SIGNAL` for `process.kill`'s second arg) on a wrong-type
argument:

  - `process.chdir(123)` → ERR_INVALID_ARG_TYPE (was: garbage-deref to
    ENOENT). A new `js_process_chdir_jsv(value)` runtime entry takes
    the full NaN-boxed value, validates, then re-dispatches to the
    existing string-only `js_process_chdir`. The codegen lowering for
    `Expr::ProcessChdir` now emits the JSV entry instead of
    `unbox_to_i64` → bare `*const StringHeader`.
  - `process.kill('abc', 0)` → ERR_INVALID_ARG_TYPE on `pid`,
    `process.kill(0, {})` → ERR_UNKNOWN_SIGNAL on the signal arg
    (object/array/boolean don't reach the numeric coerce path).
  - `process.exit('foo')` → ERR_INVALID_ARG_TYPE on the code arg
    (null/undefined still pass through to the existing default).
  - `process.cpuUsage('abc')` → ERR_INVALID_ARG_TYPE on a non-object
    `prior`; null/undefined still pass through.
  - `process.hrtime('abc')` → ERR_INVALID_ARG_TYPE on a non-array
    `prior`. A new local `is_array_value` helper does the GC-type-tag
    probe so the heap-pointer-but-not-array shape (Object/Buffer/...)
    is also caught.

The fs validation helpers (`describe_received`,
`throw_type_error_with_code`, `is_numeric`) get crate-wide visibility
so process can reuse the Node-shaped error formatting without
duplicating the helpers.

Parity test `test-parity/node-suite/process/cwd/arg-validation.ts`
covers 14 probes byte-identical to `node --experimental-strip-types`.

The `(process.chdir as any)(123)` indirection path still goes through
typed-feedback (which doesn't validate); that's a separate gap in the
method-value-via-cast dispatch family and is tracked under #1343.
proggeramlug added a commit that referenced this pull request May 29, 2026
…hrtime (#2013) (#2332)

Follow-up to PR #2328 (fs slice). Each process-surface entry point
now throws the Node-matching `TypeError [ERR_INVALID_ARG_TYPE]` (or
`ERR_UNKNOWN_SIGNAL` for `process.kill`'s second arg) on a wrong-type
argument:

  - `process.chdir(123)` → ERR_INVALID_ARG_TYPE (was: garbage-deref to
    ENOENT). A new `js_process_chdir_jsv(value)` runtime entry takes
    the full NaN-boxed value, validates, then re-dispatches to the
    existing string-only `js_process_chdir`. The codegen lowering for
    `Expr::ProcessChdir` now emits the JSV entry instead of
    `unbox_to_i64` → bare `*const StringHeader`.
  - `process.kill('abc', 0)` → ERR_INVALID_ARG_TYPE on `pid`,
    `process.kill(0, {})` → ERR_UNKNOWN_SIGNAL on the signal arg
    (object/array/boolean don't reach the numeric coerce path).
  - `process.exit('foo')` → ERR_INVALID_ARG_TYPE on the code arg
    (null/undefined still pass through to the existing default).
  - `process.cpuUsage('abc')` → ERR_INVALID_ARG_TYPE on a non-object
    `prior`; null/undefined still pass through.
  - `process.hrtime('abc')` → ERR_INVALID_ARG_TYPE on a non-array
    `prior`. A new local `is_array_value` helper does the GC-type-tag
    probe so the heap-pointer-but-not-array shape (Object/Buffer/...)
    is also caught.

The fs validation helpers (`describe_received`,
`throw_type_error_with_code`, `is_numeric`) get crate-wide visibility
so process can reuse the Node-shaped error formatting without
duplicating the helpers.

Parity test `test-parity/node-suite/process/cwd/arg-validation.ts`
covers 14 probes byte-identical to `node --experimental-strip-types`.

The `(process.chdir as any)(123)` indirection path still goes through
typed-feedback (which doesn't validate); that's a separate gap in the
method-value-via-cast dispatch family and is tracked under #1343.

Co-authored-by: Ralph Küpper <ralph@skelpo.com>
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