On 11/8/25 11:37 PM, Al Viro wrote: > There are two filename-related problems in io_uring and its > interplay with audit. > > Filenames are imported when request is submitted and used when > it is processed. Unfortunately, the latter may very well > happen in a different thread. In that case the reference to > filename is put into the wrong audit_context - that of submitting > thread, not the processing one. Audit logics is called by > the latter, and it really wants to be able to find the names > in audit_context current (== processing) thread. > > Another related problem is the headache with refcounts - > normally all references to given struct filename are visible > only to one thread (the one that uses that struct filename). > io_uring violates that - an extra reference is stashed in > audit_context of submitter. It gets dropped when submitter > returns to userland, which can happen simultaneously with > processing thread deciding to drop the reference it got. > > We paper over that by making refcount atomic, but that means > pointless headache for everyone. > > Solution: the notion of partially imported filenames. Namely, > already copied from userland, but *not* exposed to audit yet. > > io_uring can create that in submitter thread, and complete the > import (obtaining the usual reference to struct filename) in > processing thread. > > Object: struct delayed_filename. > > Primitives for working with it: > > delayed_getname(&delayed_filename, user_string) - copies the name > from userland, returning 0 and stashing the address of (still incomplete) > struct filename in delayed_filename on success and returning -E... on > error. > > delayed_getname_uflags(&delayed_filename, user_string, atflags) - similar, > in the same relation to delayed_getname() as getname_uflags() is to getname() > > complete_getname(&delayed_getname) - completes the import of filename stashed > in delayed_getname and returns struct filename to caller, emptying > delayed_getname.
dismiss_delayed_filename() > diff --git a/io_uring/openclose.c b/io_uring/openclose.c > index bfeb91b31bba..6bc14f626923 100644 > --- a/io_uring/openclose.c > +++ b/io_uring/openclose.c > @@ -121,6 +118,7 @@ int io_openat2(struct io_kiocb *req, unsigned int > issue_flags) > struct file *file; > bool resolve_nonblock, nonblock_set; > bool fixed = !!open->file_slot; > + struct filename *name __free(putname) = > complete_getname(&open->filename); > int ret; > > ret = build_open_flags(&open->how, &op); I don't think this will work as-is - the prep has been done on the request, but we could be retrying io_openat2(). That will happen if this function returns -EAGAIN. That will then end up with a cleared out filename for the second (blocking) invocation. -- Jens Axboe
