From 267705adb93736cc6577e342e002b4bae3106a52 Mon Sep 17 00:00:00 2001 From: Ameer Hamza Date: Fri, 4 Sep 2026 05:39:26 +0500 Subject: [PATCH] audit: look the audit_names record up by identity, not through filename->aname __audit_getname() caches a back-pointer to the audit_names record in filename->aname and __audit_inode() follows it. Nothing invalidates it. io_uring calls getname() in ->prep() but issues the operation later, on an io-wq worker or from task work, so the submitting syscall exits and audit_free_names() frees the record while io_uring still holds the filename. __audit_inode() then writes into freed memory: BUG: KASAN: slab-use-after-free in __audit_inode+0x45e/0xa10 Read of size 8 by task iou-wrk-422/433 Look the record up by identity among the records the current context owns instead. Every record on names_list holds a reference on its filename, so a match cannot name a freed one. Clearing the back-pointer in audit_free_names() would not be enough on its own: the freeing and reading contexts are different tasks, so the store races the read, and audit_free_names() hands the same preallocated_names[] slot back out at the same address, so validating the pointer by address accepts a recycled record. Mainline fixed this differently, in commit 9fa3ec84587c ("allow incomplete imports of filenames"), part of a series that reworks getname_flags() and does not apply to this tree. Signed-off-by: Ameer Hamza (cherry picked from commit c6e5cc0a2b8b0c3d2ec1d25e7e847ae4b6d054f4) --- kernel/auditsc.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index c547add13028..80ef7ad6f2e8 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2259,6 +2259,24 @@ static void audit_copy_inode(struct audit_names *name, audit_copy_fcaps(name, dentry); } +/* + * Find the audit_names record this context holds for @name. io_uring can make + * a filename outlive the context its record was created in, so the record is + * looked up by identity among the records this context actually owns rather + * than followed through the filename's ->aname back-pointer, which nothing + * invalidates. + */ +static struct audit_names *audit_name_lookup(struct audit_context *ctx, + const struct filename *name) +{ + struct audit_names *n; + + list_for_each_entry_reverse(n, &ctx->names_list, list) + if (n->name == name) + return n; + return NULL; +} + /** * __audit_inode - store the inode and device from a lookup * @name: name being audited @@ -2299,10 +2317,10 @@ void __audit_inode(struct filename *name, const struct dentry *dentry, goto out_alloc; /* - * If we have a pointer to an audit_names entry already, then we can - * just use it directly if the type is correct. + * If this context already holds a record for this filename, use it + * directly, provided the type is right. */ - n = name->aname; + n = audit_name_lookup(context, name); if (n) { if (parent) { if (n->type == AUDIT_TYPE_PARENT ||