diff options
| author | Gregory Kurz <gkurz@meiosys.com> | 2004-10-18 08:55:24 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-10-18 08:55:24 -0700 |
| commit | 2dbc57298d84fe37d855c8bfa3b280dfd47ded4b (patch) | |
| tree | a3bb3e0fc30cd6e7e20e1f8c9b9ca6bea85b1a70 /kernel/fork.c | |
| parent | 0105467f1b4a7545f2a2c38a82e0b283bb3bc9b1 (diff) | |
[PATCH] fork() bug invalidates file descriptors
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)-
Diffstat (limited to 'kernel/fork.c')
| -rw-r--r-- | kernel/fork.c | 11 |
1 files changed, 10 insertions, 1 deletions
diff --git a/kernel/fork.c b/kernel/fork.c index 6fd57c2d22cc..bd33d8a507d7 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -762,8 +762,17 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk) 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); |
