Post #3971121
2026-07-20 22:12 UTC
@madcoder@infosec.exchange Linux implements dup() as "first look up the file object from the file descriptor, then install the file object into a new file descriptor" as two separate steps with no locks held in between:
SYSCALL_DEFINE1(dup, unsigned int, fildes)
{
int ret = -EBADF;
struct file *file = fget_raw(fildes);
if (file) {
ret = get_unused_fd_flags(0);
if (ret >= 0)
fd_install(ret, file);
else
fput(file);
}
return ret;
}
And unlike what IIRC XNU does, Linux generally does not prevent you from closing a file descriptor while another syscall is operating on a file object that was looked up from that descriptor. So you could, for example, start a blocking read() on a pipe on one thread, and then let another thread close() the pipe's FD while the read() is still pending.
I think Linux takes the position that if userspace decides to close() a file descriptor while another thread is operating on that file descriptor, userspace is being silly and shouldn't expect anything good to happen.
Replies (1)
-
@jann@infosec.exchange 2026-07-20 22:16
@madcoder@infosec.exchange On Linux, file descriptors directly refer to file objects, without the fileproc indirection that XNU has from what I remember; so ensuring that a reference is held on the file object is enough for almost everything to work fine. (There are just some small weird corners of the kernel where that causes complications, in particular one of the flavors of advisory file locks, where file locks are normally cleared when file descriptor table entries are removed but you can end up with file locks being created after no more file descriptor table entries for the file exist...)