使用dup2时的竞争状况 [英] Race condition when using dup2
问题描述
EBUSY(仅Linux)在执行下列操作时可能由dup2()或dup3()返回 open(2)和dup()的竞争条件.
它讨论什么种族条件?如果dup2
给出EBUSY
错误,我应该怎么办?我是否应该像EINTR
那样重试?
当要打开的描述符仍处于打开状态( 看起来像 编辑(更多信息,希望得到赏金) 为了了解 基本上发生了两个非常重要的事情:分配了文件描述符,然后才由VFS实际打开它.这两个操作修改了该过程的 为了记住已分配了哪个 此时,另一个线程(或在共享 我在 This manpage for the EBUSY (Linux only) This may be returned by dup2() or dup3() during a
race condition with open(2) and dup(). What race condition does it talk about and what should I do if There is an explanation in Looks like EDIT (more info and do want bounty) In order to understand how Basically two very important things happen: a file descriptor is allocated and only then it is actually opened by the VFS. These two operations modify the In order to memorize which At this precise moment, another thread (or another process in the case of shared I found additional info on this race condition in the comments of
这篇关于使用dup2时的竞争状况的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!fs/file.c
中有一个解释,fd_is_open
但不存在于fdtable
中)时,EBUSY
会返回.
!tofree && fd_is_open(fd, fdt)
的发生方式,让我们看看如何打开文件.这里是sys_open
的简化版本:long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
/* ... irrelevant stuff */
/* allocate the fd, uses a lock */
fd = get_unused_fd_flags(flags);
/* HERE the race condition can arise if another thread calls dup2 on fd */
/* do the real VFS stuff for this fd, also uses a lock */
fd_install(fd, f);
/* ... irrelevant stuff again */
return fd;
}
fdt
.它们都使用锁,因此在这两个调用中期望没有什么不好.fds
,fdt
使用了称为open_fds
的位向量.在get_unused_fd_flags()
之后,已分配fd
,并在open_fds
中设置了相应的位. fdt
的锁定已被释放,但尚未完成真正的VFS作业.fdt
的情况下,另一个进程)可以调用dup2,因为锁定已被释放,所以它不会阻塞.如果dup2
在此处采用其正常路径,则fd
将被替换,但是fd_install
对于旧文件仍将运行.因此,Ebusy
的检查和返回.fd_install()
的注释中找到了有关此比赛状况的其他信息,这证实了我的解释:/* The VFS is full of places where we drop the files lock between
* setting the open_fds bitmap and installing the file in the file
* array. At any such point, we are vulnerable to a dup2() race
* installing a file in the array before us. We need to detect this and
* fput() the struct file we are about to overwrite in this case.
*
* It should never happen - if we allow dup2() do it, _really_ bad things
* will follow. */
dup2
system call says:
dup2
gives EBUSY
error? Should I retry like in the case of EINTR
?fs/file.c
, do_dup2()
:/*
* We need to detect attempts to do dup2() over allocated but still
* not finished descriptor. NB: OpenBSD avoids that at the price of
* extra work in their equivalent of fget() - they insert struct
* file immediately after grabbing descriptor, mark it larval if
* more work (e.g. actual opening) is needed and make sure that
* fget() treats larval files as absent. Potentially interesting,
* but while extra work in fget() is trivial, locking implications
* 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.
*/
fdt = files_fdtable(files);
tofree = fdt->fd[fd];
if (!tofree && fd_is_open(fd, fdt))
goto Ebusy;
EBUSY
is returned when the descriptor to be freed is in some kind of incomplete state when it's still being opened (fd_is_open
but not present in fdtable
).!tofree && fd_is_open(fd, fdt)
can happen, let's see how files are opened. Here a simplified version of sys_open
:long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
/* ... irrelevant stuff */
/* allocate the fd, uses a lock */
fd = get_unused_fd_flags(flags);
/* HERE the race condition can arise if another thread calls dup2 on fd */
/* do the real VFS stuff for this fd, also uses a lock */
fd_install(fd, f);
/* ... irrelevant stuff again */
return fd;
}
fdt
of the process. They both use a lock, so nothing bad is to expect inside those two calls.fds
have been allocated a bit vector called open_fds
is used by the fdt
. After get_unused_fd_flags()
, the fd
has been allocated and the corresponding bit set in open_fds
. The lock on the fdt
has been released, but the real VFS job hasn't been done yet.fdt
) can call dup2 which will not block because the locks have been released. If the dup2
took its normal path here, the fd
would be replaced, but fd_install
would be still run for the old file. Hence the check and return of Ebusy
.fd_install()
which confirms my explanation:/* The VFS is full of places where we drop the files lock between
* setting the open_fds bitmap and installing the file in the file
* array. At any such point, we are vulnerable to a dup2() race
* installing a file in the array before us. We need to detect this and
* fput() the struct file we are about to overwrite in this case.
*
* It should never happen - if we allow dup2() do it, _really_ bad things
* will follow. */