On Tue, Aug 11, 2026 at 7:02 PM Jérémy Jean
<[email protected]> wrote:
>
> audit_del_rule() is used for both netlink deletion templates and internal
> fsnotify autoremove.  The former passes a parsed template which owns a
> temporary tree reference; the latter passes the installed entry itself.
>
> The unconditional audit_put_tree() at the end of audit_del_rule() assumes
> the template case.  For mixed AUDIT_DIR plus AUDIT_EXE rules, an fsnotify
> autoremove event therefore drops the installed rule's live tree reference.
> Repeating this across rules sharing the same tree can free the tree while
> another rule still references it, and a later autoremove dereferences the
> freed pathname while comparing rules.
>
> Move the temporary-tree put to audit_rule_change(), the caller that owns
> deletion templates.  Keep it in the AUDIT_DEL_RULE cleanup so both
> successful deletion and -ENOENT still release the parser-owned tree.
>
> Fixes: 34d99af52ad4 ("audit: implement audit by executable")
> Assisted-by: Codex:gpt-5
> Signed-off-by: Jérémy Jean <[email protected]>
> ---
>  kernel/auditfilter.c | 6 ++----
>  1 file changed, 2 insertions(+), 4 deletions(-)
>
> diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
> index 7f791afe5791..666c2091b9e4 100644
> --- a/kernel/auditfilter.c
> +++ b/kernel/auditfilter.c
> @@ -1023,7 +1023,6 @@ static inline int audit_add_rule(struct audit_entry 
> *entry)
>  int audit_del_rule(struct audit_entry *entry)
>  {
>         struct audit_entry  *e;
> -       struct audit_tree *tree = entry->rule.tree;
>         struct list_head *list;
>         int ret = 0;
>  #ifdef CONFIG_AUDITSYSCALL
> @@ -1071,9 +1070,6 @@ int audit_del_rule(struct audit_entry *entry)
>  out:
>         mutex_unlock(&audit_filter_mutex);
>
> -       if (tree)
> -               audit_put_tree(tree);   /* that's the temporary one */
> -
>         return ret;
>  }
>
> @@ -1158,6 +1154,8 @@ int audit_rule_change(int type, int seq, void *data, 
> size_t datasz)
>         }
>
>         if (err || type == AUDIT_DEL_RULE) {
> +               if (type == AUDIT_DEL_RULE && entry->rule.tree)
> +                       audit_put_tree(entry->rule.tree); /* that's the 
> template one */
>                 if (entry->rule.exe)
>                         audit_remove_mark(entry->rule.exe);
>                 audit_free_rule(entry);
> --
> 2.47.3
>
>

Looks good to me. It passes the audit testsuite as well.

# make test
amcast_joinpart/test ................. ok
backlog_wait_time_actual_reset/test .. ok
bpf/test ............................. ok
coredump/test ........................ ok
exec_execve/test ..................... ok
exec_name/test ....................... ok
fanotify/test ........................ ok
field_compare/test ................... ok
file_create/test ..................... ok
file_delete/test ..................... ok
file_permission/test ................. ok
file_rename/test ..................... ok
filter_device/test ................... ok
filter_exclude/test .................. ok
filter_exit/test ..................... ok
filter_inode/test .................... ok
filter_saddr_fam/test ................ ok
filter_sessionid/test ................ ok
io_uring/test ........................ ok
login_tty/test ....................... ok
lost_reset/test ...................... ok
netfilter_pkt/test ................... ok
signal/test .......................... ok
syscalls_file/test ................... ok
syscall_module/test .................. ok
syscall_socketcall/test .............. ok
time_change/test ..................... ok
user_msg/test ........................ ok
All tests successful.
Files=28, Tests=303, 107 wallclock secs ( 0.10 usr  0.02 sys + 31.68
cusr  1.89 csys = 33.69 CPU)
Result: PASS

Reviewed-by: Ricardo Robaina <[email protected]>
Tested-by: Ricardo Robaina <[email protected]>

-Ricardo


Reply via email to