Skip to content

fix(redir): validate the fd word of a {var} dup redirect - #659

Merged
brianjfox merged 2 commits into
mainfrom
fix/fdvar-dup-nonnumeric
Aug 13, 2026
Merged

fix(redir): validate the fd word of a {var} dup redirect#659
brianjfox merged 2 commits into
mainfrom
fix/fdvar-dup-nonnumeric

Conversation

@brianjfox

Copy link
Copy Markdown
Owner

Summary

{v}<&word / {v}>&word converted the target word with atoi, so a non-numeric word (exec {v}<&foo) silently duplicated fd 0 and succeeded. bash rejects any word that is not a plain fd number as an ambiguous redirect naming the variable (v: ambiguous redirect), leaves the variable untouched, and fails the redirection with status 1.

Closes #656

Changes

  • core/src/executor.cpp (dup path): validate the word before converting — foo, 0junk, -1, +1, and empty expansions are v: ambiguous redirect. Conversion uses strtoll with a range check: an fd beyond INT_MAX cannot exist and reports Bad file descriptor instead of being truncated into a valid fd. A failed dup prints bash's two-line report for the {var} form — the generic cannot duplicate fd line, then the converted number (010 reports as 10), or the raw digits when the value doesn't fit an intmax. Two bash-side quirks (it prints an uninitialized garbage number for <&"" and for set-variable non-move targets — nondeterministic across runs) are deliberately not replicated; gnash reports deterministically.
  • core/src/executor.cpp (separate commit): the issue's repro (set -e; exec {v}<&foo; echo success) exposed that a failed redirection on a simple command returned status 1 but skipped the ERR trap and set -e entirely — a pre-existing gap affecting every redirect failure (set -e; echo hi > /nonexistent-dir/f; ... also kept going). It now mirrors the compound-command path, including ! inverting the failure to success.
  • tests/harness/run_diff.sh: 14 differential cases (484 total).

Verification

  • The issue's exact repro now matches bash byte-for-byte: v: ambiguous redirect, exit 1, no success.
  • 26-case probe matrix vs bash 5.3 matches on stdout and exit status (ambiguous forms, valid dups and moves, converted-number failure reports, overflow, ERR trap, set -e, ! inversion, errexit-suppressed contexts).
  • run_diff 484/484 (three consecutive clean runs); ctest 23/23; full 83-suite scoreboard sweep with fresh oracles: no regressions (redir/vredir 0; only the standing comsub-eof=5 and the pre-existing errors=125 of errors.tests: remaining fatal-error divergences (readonly/posix aborts, ${$var} and ${-N} substitutions, set -u statuses) #658 remain).

`{v}<&word' / `{v}>&word' converted the target word with atoi, so a
non-numeric word (`{v}<&foo') silently duplicated fd 0 and succeeded.
bash rejects any word that is not a plain fd number -- `foo', `0junk',
`-1', `+1', an empty expansion -- as an ambiguous redirect naming the
VARIABLE, leaving it untouched, and the redirection fails with status 1.

Validate the word before converting (strtoll, range-checked: an fd
beyond INT_MAX cannot exist and reports Bad file descriptor rather than
being truncated).  A failed dup on the {var} form prints bash's
two-line report -- the generic `cannot duplicate fd' line, then the
CONVERTED number (`010' reports as `10'), or the raw digits when the
value does not fit an intmax.  bash's reports for a quoted-empty or
set-variable non-move target print an uninitialized number (a bash
bug); the ambiguous/converted reports here cover those cases
deterministically instead.

Closes #656
…lure

A failed redirection on a simple command returned status 1 but skipped
the ERR trap and `set -e' entirely: `set -e; exec {v}<&foo; echo x'
kept going where bash exits 1.  The compound-command path already
handled this; mirror it at the simple-command apply_redirects failure,
including `!' inverting the failure to success (`! echo hi >/bad/f'
is status 0, and does not trip errexit).
@brianjfox
brianjfox merged commit 257408c into main Aug 13, 2026
1 check passed
@brianjfox
brianjfox deleted the fix/fdvar-dup-nonnumeric branch August 13, 2026 22:23
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.

Non-numeric fd target for {var}<&word / {var}>&word diverges from bash

1 participant