Loading fs/fcntl.c +27 −26 Original line number Original line Diff line number Diff line Loading @@ -63,31 +63,35 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags) return -EINVAL; return -EINVAL; spin_lock(&files->file_lock); spin_lock(&files->file_lock); if (!(file = fcheck(oldfd))) goto out_unlock; get_file(file); /* We are now finished with oldfd */ err = expand_files(files, newfd); err = expand_files(files, newfd); file = fcheck(oldfd); if (unlikely(!file)) goto Ebadf; if (unlikely(err < 0)) { if (unlikely(err < 0)) { if (err == -EMFILE) if (err == -EMFILE) err = -EBADF; goto Ebadf; goto out_fput; goto out_unlock; } } /* /* To avoid races with open() and dup(), we will mark the fd as * We need to detect attempts to do dup2() over allocated but still * in-use in the open-file bitmap throughout the entire dup2() * not finished descriptor. NB: OpenBSD avoids that at the price of * process. This is quite safe: do_close() uses the fd array * extra work in their equivalent of fget() - they insert struct * entry, not the bitmap, to decide what work needs to be * file immediately after grabbing descriptor, mark it larval if * done. --sct */ * more work (e.g. actual opening) is needed and make sure that /* Doesn't work. open() might be there first. --AV */ * fget() treats larval files as absent. Potentially interesting, * but while extra work in fget() is trivial, locking implications /* Yes. It's a race. In user space. Nothing sane to do */ * and amount of surgery on open()-related paths in VFS are not. * FreeBSD fails with -EBADF in the same situation, NetBSD "solution" * deadlocks in rather amusing ways, AFAICS. All of that is out of * scope of POSIX or SUS, since neither considers shared descriptor * tables and this condition does not arise without those. */ err = -EBUSY; err = -EBUSY; fdt = files_fdtable(files); fdt = files_fdtable(files); tofree = fdt->fd[newfd]; tofree = fdt->fd[newfd]; if (!tofree && FD_ISSET(newfd, fdt->open_fds)) if (!tofree && FD_ISSET(newfd, fdt->open_fds)) goto out_fput; goto out_unlock; get_file(file); rcu_assign_pointer(fdt->fd[newfd], file); rcu_assign_pointer(fdt->fd[newfd], file); FD_SET(newfd, fdt->open_fds); FD_SET(newfd, fdt->open_fds); if (flags & O_CLOEXEC) if (flags & O_CLOEXEC) Loading @@ -98,17 +102,14 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags) if (tofree) if (tofree) filp_close(tofree, files); filp_close(tofree, files); err = newfd; out: return err; out_unlock: spin_unlock(&files->file_lock); goto out; out_fput: return newfd; Ebadf: err = -EBADF; out_unlock: spin_unlock(&files->file_lock); spin_unlock(&files->file_lock); fput(file); return err; goto out; } } asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) Loading Loading
fs/fcntl.c +27 −26 Original line number Original line Diff line number Diff line Loading @@ -63,31 +63,35 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags) return -EINVAL; return -EINVAL; spin_lock(&files->file_lock); spin_lock(&files->file_lock); if (!(file = fcheck(oldfd))) goto out_unlock; get_file(file); /* We are now finished with oldfd */ err = expand_files(files, newfd); err = expand_files(files, newfd); file = fcheck(oldfd); if (unlikely(!file)) goto Ebadf; if (unlikely(err < 0)) { if (unlikely(err < 0)) { if (err == -EMFILE) if (err == -EMFILE) err = -EBADF; goto Ebadf; goto out_fput; goto out_unlock; } } /* /* To avoid races with open() and dup(), we will mark the fd as * We need to detect attempts to do dup2() over allocated but still * in-use in the open-file bitmap throughout the entire dup2() * not finished descriptor. NB: OpenBSD avoids that at the price of * process. This is quite safe: do_close() uses the fd array * extra work in their equivalent of fget() - they insert struct * entry, not the bitmap, to decide what work needs to be * file immediately after grabbing descriptor, mark it larval if * done. --sct */ * more work (e.g. actual opening) is needed and make sure that /* Doesn't work. open() might be there first. --AV */ * fget() treats larval files as absent. Potentially interesting, * but while extra work in fget() is trivial, locking implications /* Yes. It's a race. In user space. Nothing sane to do */ * and amount of surgery on open()-related paths in VFS are not. * FreeBSD fails with -EBADF in the same situation, NetBSD "solution" * deadlocks in rather amusing ways, AFAICS. All of that is out of * scope of POSIX or SUS, since neither considers shared descriptor * tables and this condition does not arise without those. */ err = -EBUSY; err = -EBUSY; fdt = files_fdtable(files); fdt = files_fdtable(files); tofree = fdt->fd[newfd]; tofree = fdt->fd[newfd]; if (!tofree && FD_ISSET(newfd, fdt->open_fds)) if (!tofree && FD_ISSET(newfd, fdt->open_fds)) goto out_fput; goto out_unlock; get_file(file); rcu_assign_pointer(fdt->fd[newfd], file); rcu_assign_pointer(fdt->fd[newfd], file); FD_SET(newfd, fdt->open_fds); FD_SET(newfd, fdt->open_fds); if (flags & O_CLOEXEC) if (flags & O_CLOEXEC) Loading @@ -98,17 +102,14 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags) if (tofree) if (tofree) filp_close(tofree, files); filp_close(tofree, files); err = newfd; out: return err; out_unlock: spin_unlock(&files->file_lock); goto out; out_fput: return newfd; Ebadf: err = -EBADF; out_unlock: spin_unlock(&files->file_lock); spin_unlock(&files->file_lock); fput(file); return err; goto out; } } asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) Loading