On 11/29/23 14:12, Alice Ryhl wrote: > + /// Schedule a task work that closes the file descriptor when this task returns to userspace. > + pub fn close_fd(mut self, fd: u32) { > + use bindings::task_work_notify_mode_TWA_RESUME as TWA_RESUME; > + > + let file = unsafe { bindings::close_fd_get_file(fd) }; > + if file.is_null() { > + // Nothing further to do. The allocation is freed by the destructor of `self.inner`. > + return; > + } > + > + self.inner.file = file; > + > + // SAFETY: Since `DeferredFdCloserInner` is `#[repr(C)]`, casting the pointers gives a > + // pointer to the `twork` field. > + let inner = Box::into_raw(self.inner) as *mut bindings::callback_head; Here you can just use `.cast::<...>()`. > + // SAFETY: Getting a pointer to current is always safe. > + let current = unsafe { bindings::get_current() }; > + // SAFETY: The `file` pointer points at a valid file. > + unsafe { bindings::get_file(file) }; > + // SAFETY: Due to the above `get_file`, even if the current task holds an `fdget` to > + // this file right now, the refcount will not drop to zero until after it is released > + // with `fdput`. This is because when using `fdget`, you must always use `fdput` before > + // returning to userspace, and our task work runs after any `fdget` users have returned > + // to userspace. > + // > + // Note: fl_owner_t is currently a void pointer. > + unsafe { bindings::filp_close(file, (*current).files as bindings::fl_owner_t) }; > + // SAFETY: The `inner` pointer is compatible with the `do_close_fd` method. > + unsafe { bindings::init_task_work(inner, Some(Self::do_close_fd)) }; > + // SAFETY: The `inner` pointer points at a valid and fully initialized task work that is > + // ready to be scheduled. > + unsafe { bindings::task_work_add(current, inner, TWA_RESUME) }; I am a bit confused, when does `do_close_fd` actually run? Does `TWA_RESUME` mean that `inner` is scheduled to run after the current task has been completed? > + } > + > + // SAFETY: This function is an implementation detail of `close_fd`, so its safety comments > + // should be read in extension of that method. > + unsafe extern "C" fn do_close_fd(inner: *mut bindings::callback_head) { > + // SAFETY: In `close_fd` we use this method together with a pointer that originates from a > + // `Box<DeferredFdCloserInner>`, and we have just been given ownership of that allocation. > + let inner = unsafe { Box::from_raw(inner as *mut DeferredFdCloserInner) }; In order for this call to be sound, `inner` must be an exclusive pointer (including any possible references into the `callback_head`). Is this the case? -- Cheers, Benno > + // SAFETY: This drops a refcount we acquired in `close_fd`. Since this callback runs in a > + // task work after we return to userspace, it is guaranteed that the current thread doesn't > + // hold this file with `fdget`, as `fdget` must be released before returning to userspace. > + unsafe { bindings::fput(inner.file) }; > + // Free the allocation. > + drop(inner); > + } > +} > + > /// Represents the `EBADF` error code. > /// > /// Used for methods that can only fail with `EBADF`. > > -- > 2.43.0.rc1.413.gea7ed67945-goog >