Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS: -g -O2 -Werror=implicit-function-declaration
-fstack-protector-strong -fstack-clash-protection -Wformat
-Werror=format-security -fcf-protection -Wall
uname output: Linux samsung 6.17.13+deb14-amd64 #1 SMP PREEMPT_DYNAMIC Debian
6.17.13-1 (2025-12-20) x86_64 GNU/Linux
Machine Type: x86_64-pc-linux-gnu
Bash Version: 5.3
Patch Level: 3
Release Status: release
(same in current "devel" git head (commit
c4b56ed9ac00424bde0f9ce3adfb7edb3d19a557)
Description:
$ bash -c 'echo "${ exec 4> /dev/null && echo OK; }"; ls -dog "/proc/$$/fd/4"'
OK
l-wx------ 1 64 Jan 23 09:22 /proc/19083/fd/4 -> /dev/null
fine, but when 4 is the first free fd like after:
$ exec 3>&1
$ bash -c 'echo "${ exec 4> /dev/null && echo OK; }"; ls -dog "/proc/$$/fd/4"'
ls: cannot access '/proc/19106/fd/4': No such file or directory
Under strace, we see (trimmed):
memfd_create("anonopen", MFD_NOEXEC_SEAL) = 4
dup2(1, 255) = 255
close(1) = 0
dup2(4, 1) = 1
openat(AT_FDCWD, "/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 5
fcntl(4, F_GETFD) = 0
fcntl(4, F_DUPFD, 10) = 10
fcntl(4, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(5, 4) = 4
close(5) = 0
close(10) = 0
write(1, "OK\n", 3) = 3
lseek(4, 0, SEEK_SET) = 0
read(4, 0x7ffcf090bc80, 4096) = -1 EBADF (Bad file descriptor)
dup2(255, 1) = 1
close(255) = 0
close(4) = 0
We see the memfd open on the first free fd, here < 10, which
means it breaks if that particular fd is used within. fds 0 to 9
should be reserved for the user.
The doc has:
> Redirections using file descriptors greater than 9 should be used
> with care, as they may conflict with file descriptors the shell uses
> internally.
Implying 0 to 9 should be safe to use.
Here, not only fd 4 as open by the user was closed in the end
causing that error by ls, but command substitution failed as
bash tried to read the output from that fd 4 which it assumed
was still open on the memfd.
That came up at
https://unix.stackexchange.com/questions/803968/double-pass-processing-of-stdin-with-bash/803973#803973
when trying to do:
${ exec 3>&1; }
to get a fd open on an anonymous memfile.
Fix:
use a fd above 9 for the anonfile or the general approach that
bash uses in other contexts to avoid its internal fds clashing
with those manipulated by the user.
--
Stephane