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

Reply via email to