!38 introduced a race condition in `<PassthroughFs as FileSystem>::create()`.
The new code first calls `libc::openat()` with `O_CREAT|O_EXCL`. If the file already exists, then it's opened using a subsequent call to `libc::openat()` (in `do_open()`) without those flags. But if the file was removed between those two calls, then the latter fails with `ENOENT (No such file or directory)`. **How to reproduce** It's easier to reproduce by adding a sleep call between the too `libc::openat()` calls. Furthermore, in my case (Fedora 35 guest), e.g. when running `touch` from the guest, it only sends `FUSE_CREATE` if the file doesn't already exist. Therefore, we also need to add a sleep before the first `libc::openat()` call, to make it easier to reproduce. To do this, apply the following patch to `virtiofsd` (applicable on top of https://gitlab.com/virtio-fs/virtiofsd/-/commit/36383911e7039996f19e130815ae36035e4636f3): ```diff diff --git a/src/passthrough/mod.rs b/src/passthrough/mod.rs index ce766ba..0fdef22 100644 --- a/src/passthrough/mod.rs +++ b/src/passthrough/mod.rs @@ -20,13 +20,13 @@ use std::borrow::Cow; use std::collections::{btree_map, BTreeMap}; use std::ffi::{CStr, CString}; use std::fs::File; -use std::io; use std::mem::MaybeUninit; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::str::FromStr; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::{Arc, RwLock}; use std::time::Duration; +use std::{io, thread, time}; use xattrmap::{AppliedRule, XattrMap}; const EMPTY_CSTR: &[u8] = b"\0"; @@ -1232,6 +1232,8 @@ impl FileSystem for PassthroughFs { let parent_file = data.get_file()?; + thread::sleep(time::Duration::from_millis(500)); + let fd = { let (_uid, _gid) = set_creds(ctx.uid, ctx.gid)?; @@ -1267,6 +1269,8 @@ impl FileSystem for PassthroughFs { _ => return Err(last_error), } + thread::sleep(time::Duration::from_millis(500)); + let entry = self.do_lookup(parent, name)?; let (handle, _) = self.do_open(entry.inode, kill_priv, flags)?; let handle = handle.ok_or_else(ebadf)?; diff --git a/src/seccomp.rs b/src/seccomp.rs index 89eca14..7d9b76e 100644 --- a/src/seccomp.rs +++ b/src/seccomp.rs @@ -65,6 +65,7 @@ pub fn enable_seccomp(action: SeccompAction) -> Result<(), Error> { allow_syscall!(ctx, libc::SYS_capget); // For CAP_FSETID allow_syscall!(ctx, libc::SYS_capset); allow_syscall!(ctx, libc::SYS_clock_gettime); + allow_syscall!(ctx, libc::SYS_clock_nanosleep); allow_syscall!(ctx, libc::SYS_clone); allow_syscall!(ctx, libc::SYS_clone3); allow_syscall!(ctx, libc::SYS_close); ``` On the host, run the following sh command where `test/shared` is the path to the shared directory: ```shell while true; do touch test/shared/test sleep 0.5 rm test/shared/test sleep 0.5 done ``` On the guest, mount the shared directory on `/mnt`. This is typically done via ``` sudo mount -t virtiofs host /mnt ``` or the like. Then run `touch /mnt/test` a few times until you see the error: ``` $ touch /mnt/test touch: cannot touch '/mnt/test': No such file or directory ``` **strace** Here's the relevant part of the output of `virtiofsd` run with `strace -f`: ``` [pid 984595] write(2, "\33[0m\33[38;5;8m[\33[0m2022-01-17T12:"..., 98[2022-01-17T12:56:57Z DEBUG virtiofsd] QUEUE_EVENT ) = 98 [pid 984595] write(2, "\33[0m\33[38;5;8m[\33[0m2022-01-17T12:"..., 115[2022-01-17T12:56:57Z DEBUG virtiofsd::server] Received request: 35 ) = 115 [pid 984595] clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=0, tv_nsec=500000000}, 0x7efd4cf00d00) = 0 [pid 984595] setresgid(-1, 1001, -1) = 0 [pid 984595] setresuid(-1, 1000, -1) = 0 [pid 984595] openat(11, "test", O_WRONLY|O_CREAT|O_EXCL|O_NONBLOCK|O_LARGEFILE|O_NOFOLLOW|O_CLOEXEC, 0100644) = -1 EEXIST (File exists) [pid 984595] setresgid(-1, 0, -1) = 0 [pid 984595] setresuid(-1, 0, -1) = 0 [pid 984595] clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=0, tv_nsec=500000000}, 0x7efd4cf00d00) = 0 [pid 984595] openat(11, "test", O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH) = -1 ENOENT (No such file or directory) [pid 984595] write(2, "\33[0m\33[38;5;8m[\33[0m2022-01-17T12:"..., 164[2022-01-17T12:56:58Z DEBUG virtiofsd::server] Replying ERROR, header: OutHeader { len: 16, error: -2, unique: 128 } ) = 164 ``` --- https://gitlab.com/virtio-fs/virtiofsd/-/issues/22 _______________________________________________ Virtio-fs mailing list [email protected] https://listman.redhat.com/mailman/listinfo/virtio-fs
