NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/59523: posix_spawn(2) may fail eroneously trying to close an already closed file descriptor
>Number: 59523
>Category: kern
>Synopsis: posix_spawn(2) may fail eroneously trying to close an already closed file descriptor
>Confidential: no
>Severity: critical
>Priority: high
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Wed Jul 09 11:30:01 +0000 2025
>Originator: Martin Husemann
>Release: NetBSD 10.99.14
>Organization:
The NetBSD Foundation, Inc.
>Environment:
System: NetBSD martins.aprisoft.de 10.99.14 NetBSD 10.99.14 (GENERIC) #270: Wed Jul 9 12:36:43 CEST 2025 martin%martins.aprisoft.de@localhost:/home/martin/current/src/sys/arch/amd64/compile/GENERIC amd64
Architecture: x86_64
Machine: amd64
>Description:
This came up due to syzkaller failing on NetBSD. Their code uses posix_spawnp(3)
and adds a "close" file action for every possible file descriptor starting one
higher than the highest descriptor they use themself up to the configured maximum.
This causes the posix_spawn(2) call to fail with EBADF, which is in violation
of posix:
If the file_actions argument is not a null pointer, and specifies any
chdir, close, dup2, fchdir, or open actions to be performed, and if
posix_spawn() or posix_spawnp() fails for any of the reasons that would
cause chdir(), close(), dup2(), fchdir(), or open() to fail, other than
~~~~~~~~~~
attempting a close() on a file descriptor that is in range but already
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
closed, an error value shall be returned [...]
~~~~~~
Original report:
https://github.com/google/syzkaller/issues/6163
>How-To-Repeat:
Run code like this (from a new ATF test):
int status, fd;
pid_t pid;
char * const args[2] = { __UNCONST("ls"), NULL };
posix_spawn_file_actions_t fa;
posix_spawnattr_t attr;
/* get a free file descriptor number */
fd = open("/dev/null", O_RDONLY);
ATF_REQUIRE(fd >= 0);
close(fd);
RZ(posix_spawn_file_actions_init(&fa));
// redirect output to /dev/null to not garble atf test results
RZ(posix_spawn_file_actions_addopen(&fa, STDOUT_FILENO, "/dev/null",
O_WRONLY, 0));
// known closed fd
RZ(posix_spawn_file_actions_addclose(&fa, fd));
// a random fd we know nothing about (cross fingers!
RZ(posix_spawn_file_actions_addclose(&fa, fd+1));
// high fd probably not ever been allocated, likely to trigger
// a fd_getfile() failure in the kernel, which is another
// path that originaly caused the fallout in syzkaller
RZ(posix_spawn_file_actions_addclose(&fa, 560));
RZ(posix_spawnattr_init(&attr));
RZ(posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETPGROUP));
RZ(posix_spawn(&pid, "/bin/ls", &fa, &attr, args, NULL));
RZ(posix_spawn_file_actions_destroy(&fa));
/* ok, wait for the child to finish */
RL(waitpid(pid, &status, 0));
ATF_REQUIRE_MSG((WIFEXITED(status) &&
WEXITSTATUS(status) == EXIT_SUCCESS),
"status=0x%x", status);
and watch it fail with posix_spawn returning EBADF.
There is another path in the kernel (where the parent process does not wait
due to no POSIX_SPAWN_SETPGROUP or the spawn attr pointer being NULL)
that makes the posix_spawn call return 127 in this case.
But it should return 0 (and actually run the client process) in both cases.
>Fix:
upcoming...
Home |
Main Index |
Thread Index |
Old Index