Take a process P1 that spawns a thread T (aka. a clone with CLONE_FILES).
If P1 forks another process P2 (aka. not a clone) while T is blocked in a
open() that should return file descriptor FD, then FD will be unusable in
P2. This leads to strange behaviors in the context of P2: close(FD)
returns EBADF, while dup2(a_valid_fd, FD) returns EBUSY and of course FD is
never returned again by any syscall...
testcase:
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <signal.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <asm/page.h>
#define FIFO "/tmp/bug_fifo"
#define FD 0
/*
* This program is meant to show that calling fork() while a clone spawned
* with CLONE_FILES is blocked in open() makes a fd number unusable in the
* child.
*
*
* Parent Clone Child
* |
* clone(CLONE_FILES)-
for (i = open_files; i != 0; i--) {
struct file *f = *old_fds++;
- if (f)
+ if (f) {
get_file(f);
+ } else {
+ /*
+ * The fd may be claimed in the fd bitmap but not yet
+ * instantiated in the files array if a sibling thread
+ * is partway through open(). So make sure that this
+ * fd is available to the new process.
+ */
+ FD_CLR(open_files - i, newf->open_fds);
+ }
*new_fds++ = f;
}
spin_unlock(&oldf->file_lock);