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

Reply via email to