Linux kernel crash report

Report from LKML message: 69ef2847.170a0220.11de9.001a.GAE@google.com

Root cause: rose_neigh_put (introduced by d860d1faa6b2) can free rose_neigh and its embedded digipeat pointer from softirq context without cancelling t0timer, leaving a pending timer callback that reads the freed object.

Key elements

Field Value Implication
CRASH_TYPE KASAN slab-use-after-free
UNAME syzkaller #0 PREEMPT(full) syzbot-generated kernel
PROCESS swapper/0 PID 0, idle task; crash occurred in timer softirq
CPU 0
PID 0
TAINT (not tainted)
HARDWARE Google Compute Engine syzbot VM
BIOS Google 04/18/2026
MSGID <69ef2847.170a0220.11de9.001a.GAE@google.com>
MSGID_URL 69ef2847.170a0220.11de9.001a.GAE@google.com
VMLINUX oops-workdir/syzbot/vmlinux-e728258d from prefetch.md
SOURCEDIR oops-workdir/linux HEAD_COMMIT e728258debd5
HEAD_COMMIT e728258debd5 Merge tag 'net-7.1-rc1'
KASAN_FAULT_ADDR ffff8880310ac600 address of the freed object
KASAN_ACCESS_SIZE 66 bytes matches sizeof(ax25_digi) exactly (8×7 + 8 + 1 + 1)
KASAN_OBJECT_TYPE ax25_digi (digipeat) rose_neigh->digipeat; freed via kfree(rose_neigh->digipeat) at include/net/rose.h:165
KASAN_CRASH_SITE ax25_send_frame at net/ax25/ax25_out.c:78 kmemdup(digi, sizeof(*digi), GFP_ATOMIC) reads 66 bytes from freed digipeat
KASAN_ALLOC_TASK task 10474 via rose_add_node allocated at net/rose/rose_route.c:109 via kmalloc_obj(ax25_digi, GFP_ATOMIC)
KASAN_FREE_TASK task 0 (swapper, softirq) via rose_timer_expiry freed at net/rose/rose_timer.c:183include/net/rose.h:165

Kernel modules

Module Flags Backtrace Location Flag Implication
(module list not available in this report)

Backtrace

KASAN slab-use-after-free — three stacks: (1) crash/read, (2) allocation, (3) free.

The hardware RIP (pv_native_safe_halt+0xf) reflects the idle task that was interrupted by the APIC timer — it is not the crash site. The actual UAF read occurred in ax25_send_frame while executing in the timer softirq (IRQ context, stack 1).

Stack 1 — Crash / UAF read

Address Function Offset Size Context Module Source location
check_region_inline (inlined, reporting — skip) IRQ mm/kasan/generic.c:-1
kasan_check_range (assert — skip) 0x264 0x2c0 IRQ mm/kasan/generic.c:200
__asan_memcpy (assert — skip) 0x29 0x70 IRQ mm/kasan/shadow.c:105
kmemdup_noprof (assert — skip) 0x55 0x70 IRQ mm/util.c:140
kmemdup_noprof (inlined into ax25_send_frame) IRQ include/linux/fortify-string.h:763
0xffffffff8aa0c9e3 (0xffffffff8aa0c350 + 0x693) ax25_send_framecrash site 0x693 0x9f0 IRQ net/ax25/ax25_out.c:78
rose_send_frame (inlined into rose_t0timer_expiry) IRQ net/rose/rose_link.c:106
rose_transmit_restart_request (inlined into rose_t0timer_expiry) IRQ net/rose/rose_link.c:198
0xffffffff8a9f7005 (0xffffffff8a9f6db0 + 0x255) rose_t0timer_expiry 0x255 0x560 IRQ net/rose/rose_link.c:83
0xffffffff81b24b22 (0xffffffff81b24990 + 0x192) call_timer_fn 0x192 0x5e0 IRQ kernel/time/timer.c:1748
expire_timers (inlined) IRQ kernel/time/timer.c:1799
__run_timers (inlined) IRQ kernel/time/timer.c:2374
0xffffffff81b207a2 (0xffffffff81b20150 + 0x652) __run_timer_base 0x652 0x8b0 IRQ kernel/time/timer.c:1799
run_timer_base (inlined) IRQ kernel/time/timer.c:2395
0xffffffff81b224a7 (0xffffffff81b223f0 + 0xb7) run_timer_softirq 0xb7 0x170 IRQ kernel/time/timer.c:2395
0xffffffff8187fdba (0xffffffff8187fb90 + 0x22a) handle_softirqs 0x22a 0x840 IRQ kernel/softirq.c:622
__do_softirq (inlined) IRQ kernel/softirq.c:656
invoke_softirq (inlined) IRQ kernel/softirq.c:496
0xffffffff818806ba (0xffffffff818805f0 + 0xca) __irq_exit_rcu 0xca 0x220 IRQ kernel/softirq.c:656
0xffffffff818805b9 (0xffffffff818805b0 + 0x9) irq_exit_rcu 0x9 0x30 IRQ kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt (inlined) IRQ arch/x86/kernel/apic/apic.c:1061
0xffffffff8bb8dc66 (0xffffffff8bb8dbc0 + 0xa6) sysvec_apic_timer_interrupt 0xa6 0xc0 IRQ arch/x86/kernel/apic/apic.c:1061
0xffffffff810014ca (0xffffffff810014b0 + 0x1a) asm_sysvec_apic_timer_interrupt 0x1a 0x20 Task arch/x86/include/asm/idtentry.h:697
0xffffffff8bb8e12f (0xffffffff8bb8e120 + 0xf) pv_native_safe_haltRIP (idle task, not crash site) 0xf 0x20 Task arch/x86/include/asm/irqflags.h:48
arch_safe_halt (inlined) Task arch/x86/kernel/process.c:766
0xffffffff8bb90bc9 (0xffffffff8bb90bc0 + 0x9) default_idle 0x9 0x20 Task arch/x86/kernel/process.c:766
0xffffffff8bb90e12 (0xffffffff8bb90da0 + 0x72) default_idle_call 0x72 0xb0 Task kernel/sched/idle.c:122
cpuidle_idle_call (inlined) Task kernel/sched/idle.c:199
0xffffffff819aae9a (0xffffffff819aab30 + 0x36a) do_idle 0x36a 0x5f0 Task kernel/sched/idle.c:199
0xffffffff819ab173 (0xffffffff819ab130 + 0x43) cpu_startup_entry 0x43 0x60 Task kernel/sched/idle.c:451
0xffffffff8bb91cde (0xffffffff8bb91a00 + 0x2de) rest_init 0x2de 0x300 Task init/main.c:762
0xffffffff91a10aca (0xffffffff91a10740 + 0x38a) start_kernel 0x38a 0x3e0 Task init/main.c:1220
0xffffffff91a30484 (0xffffffff91a30460 + 0x24) x86_64_start_reservations 0x24 0x30 Task arch/x86/kernel/head64.c:310
0xffffffff91a302f3 (0xffffffff91a301b0 + 0x143) x86_64_start_kernel 0x143 0x1c0 Task arch/x86/kernel/head64.c:291
0xffffffff81688b77 (0xffffffff81688a39 + 0x13e) common_startup_64 0x13e 0x147 Task arch/x86/kernel/head_64.S:418

Stack 2 — Allocation (task 10474)

Address Function Offset Size Context Module Source location
0xffffffff8225a38c (0xffffffff8225a070 + 0x31c) __kmalloc_cache_noprof 0x31c 0x660 Task include/linux/kasan.h:263
kmalloc_noprof (inlined) Task include/linux/slab.h:950
0xffffffff8a9fbdb1 (0xffffffff8a9fb940 + 0x471) rose_add_nodealloc site 0x471 0xf00 Task net/rose/rose_route.c:109
0xffffffff8a9fb3c5 (0xffffffff8a9fa690 + 0xd35) rose_rt_ioctl 0xd35 0x12a0 Task net/rose/rose_route.c:748
0xffffffff8a9efb4b (0xffffffff8a9ef750 + 0x3fb) rose_ioctl 0x3fb 0x8f0 Task net/rose/af_rose.c:1387
0xffffffff897a71a1 (0xffffffff897a70a0 + 0x101) sock_do_ioctl 0x101 0x320 Task net/socket.c:1313
0xffffffff897a5b06 (0xffffffff897a5540 + 0x5c6) sock_ioctl 0x5c6 0x7f0 Task net/socket.c:1434
vfs_ioctl (inlined) Task fs/ioctl.c:51
__do_sys_ioctl (inlined) Task fs/ioctl.c:597
0xffffffff824860cc (0xffffffff82485fd0 + 0xfc) __se_sys_ioctl 0xfc 0x170 Task fs/ioctl.c:51
do_syscall_x64 (inlined) Task arch/x86/entry/syscall_64.c:63
0xffffffff8bb861df (0xffffffff8bb86080 + 0x15f) do_syscall_64 0x15f 0xf80 Task arch/x86/entry/syscall_64.c:63
0xffffffff81000130 (0xffffffff810000b9 + 0x77) entry_SYSCALL_64_after_hwframe 0x77 0x7f Task arch/x86/entry/entry_64.S:121

Stack 3 — Free (task 0 / swapper, softirq)

Address Function Offset Size Context Module Source location
0xffffffff82255675 (0xffffffff822554b0 + 0x1c5) kfree 0x1c5 0x640 IRQ mm/slub.c:6561 (inlined via kasan_slab_free at include/linux/kasan.h:235)
rose_neigh_put (inlined) IRQ include/net/rose.h:165
0xffffffff8aa03d73 (0xffffffff8aa038b0 + 0x4c3) rose_timer_expiryfree site 0x4c3 0x600 IRQ net/rose/rose_timer.c:183
0xffffffff81b24b22 (0xffffffff81b24990 + 0x192) call_timer_fn 0x192 0x5e0 IRQ kernel/time/timer.c:1748
expire_timers (inlined) IRQ kernel/time/timer.c:1799
__run_timers (inlined) IRQ kernel/time/timer.c:2374
0xffffffff81b207a2 (0xffffffff81b20150 + 0x652) __run_timer_base 0x652 0x8b0 IRQ kernel/time/timer.c:1799
run_timer_base (inlined) IRQ kernel/time/timer.c:2395
0xffffffff81b224a7 (0xffffffff81b223f0 + 0xb7) run_timer_softirq 0xb7 0x170 IRQ kernel/time/timer.c:2395
0xffffffff8187fdba (0xffffffff8187fb90 + 0x22a) handle_softirqs 0x22a 0x840 IRQ kernel/softirq.c:622
__do_softirq (inlined) IRQ kernel/softirq.c:656
invoke_softirq (inlined) IRQ kernel/softirq.c:496
0xffffffff818806ba (0xffffffff818805f0 + 0xca) __irq_exit_rcu 0xca 0x220 IRQ kernel/softirq.c:656
0xffffffff818805b9 (0xffffffff818805b0 + 0x9) irq_exit_rcu 0x9 0x30 IRQ kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt (inlined) IRQ arch/x86/kernel/apic/apic.c:1061
0xffffffff8bb8dc66 (0xffffffff8bb8dbc0 + 0xa6) sysvec_apic_timer_interrupt 0xa6 0xc0 IRQ arch/x86/kernel/apic/apic.c:1061
0xffffffff810014ca (0xffffffff810014b0 + 0x1a) asm_sysvec_apic_timer_interrupt 0x1a 0x20 IRQ arch/x86/include/asm/idtentry.h:697

CPU registers

Note: These registers reflect the idle task CPU state at the point of the APIC timer interrupt. They do not correspond to the UAF crash context (which occurred inside the timer softirq).

RIP: 0010:pv_native_safe_halt+0xf/0x20  [arch/x86/kernel/paravirt.c:63 / arch/x86/include/asm/irqflags.h:48]
Code: db 70 02 e9 53 f4 02 00 cc cc cc 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 66 90 0f 00 2d 13 81 10 00 fb f4 <c3> cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc 90 90 90 90 90
RSP: 0018:ffffffff8e607dc0  EFLAGS: 00000242
RAX: 0000000000110dc7  RBX: ffffffff819aae9a  RCX: 0000000080000001
RDX: 0000000000000001  RSI: ffffffff8dfd8f45  RDI: ffffffff8c289f60
RBP: ffffffff8e607eb0  R08: ffff8880b86339db  R09: 1ffff110170c673b
R10: dffffc0000000000  R11: ffffed10170c673c  R12: 0000000000000000
R13: 1ffffffff1cd25d8  R14: 0000000000000000  R15: 1ffffffff1cd25d8
Register Value Notes
RIP 0xffffffff8bb8e12f pv_native_safe_halt+0xf — idle task executing sti; hlt
RSP 0xffffffff8e607dc0 kernel stack pointer (CPU 0 idle stack)
EFLAGS 0x00000242 IF=1 (interrupts enabled), ZF=1, PF=1
RBX 0xffffffff819aae9a = do_idle+0x36a (return address into idle loop)
R10 0xdffffc0000000000 KASAN shadow offset constant

Disassembly — crash site

Address 0xffffffff8aa0c9e3 in ax25_send_frame+0x693 (net/ax25/ax25_out.c:78):

ffffffff8aa0c9a3:  call   ffffffff81aba970 <__rcu_read_unlock>
ffffffff8aa0c9a8:  lea    0x10(%r13),%rdi
ffffffff8aa0c9ac:  mov    $0x7,%edx
ffffffff8aa0c9b1:  mov    0x8(%rsp),%rsi
ffffffff8aa0c9b6:  call   ffffffff82310fd0 <__asan_memcpy>
ffffffff8aa0c9bb:  lea    0x17(%r13),%rdi
ffffffff8aa0c9bf:  mov    $0x7,%edx
ffffffff8aa0c9c4:  mov    %r12,%rsi
ffffffff8aa0c9c7:  call   ffffffff82310fd0 <__asan_memcpy>
ffffffff8aa0c9cc:  test   %r14,%r14
ffffffff8aa0c9cf:  je     ffffffff8aa0ca1d <ax25_send_frame+0x6cd>
ffffffff8aa0c9d1:  mov    $0x42,%esi          ; 0x42 = 66 = sizeof(ax25_digi)
ffffffff8aa0c9d6:  mov    %r14,%rdi            ; rdi = digi = neigh->digipeat (freed)
ffffffff8aa0c9d9:  mov    $0x820,%edx          ; GFP_ATOMIC
ffffffff8aa0c9de:  call   ffffffff820f4170 <kmemdup_noprof>  ; reads 66 bytes from freed object
ffffffff8aa0c9e3:  mov    %rax,%r15            <<< crash (return address; UAF read is in kmemdup_noprof)

Backtrace source code

1. ax25_send_frame — crash site (net/ax25/ax25_out.c:78)

net/ax25/ax25_out.c at commit e728258debd5

 32  ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, const ax25_address *src, ax25_address *dest, ax25_digi *digi, struct net_device *dev)
 33  {
 34     ax25_dev *ax25_dev;
 35     ax25_cb *ax25;
      ...
 74     ax25->source_addr = *src;
 75     ax25->dest_addr   = *dest;
 76  
 77     if (digi != NULL) {
78        ax25->digipeat = kmemdup(digi, sizeof(*digi), GFP_ATOMIC);   // ← digi = neigh->digipeat; freed ax25_digi; 66-byte UAF read
 79         if (ax25->digipeat == NULL) {
 80             ax25_cb_put(ax25);
 81             return NULL;
 82         }
 83     }
      ...
115     return ax25;
116  }

2. rose_t0timer_expirynet/rose/rose_link.c:83

net/rose/rose_link.c at commit e728258debd5

 79  static void rose_t0timer_expiry(struct timer_list *t)
 80  {
 81     struct rose_neigh *neigh = timer_container_of(neigh, t, t0timer);
 82  
83    rose_transmit_restart_request(neigh);   // ← inlined; calls rose_send_frame → ax25_send_frame with neigh->digipeat
 84  
 85     neigh->dce_mode = 0;
 86  
 87     rose_start_t0timer(neigh);
 88  }

rose_send_frame (inlined at rose_link.c:106):

 95  static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh)
 96  {
 97     const ax25_address *rose_call;
 98     ax25_cb *ax25s;
 99  
100     if (ax25cmp(&rose_callsign, &null_ax25_address) == 0)
101         rose_call = (const ax25_address *)neigh->dev->dev_addr;
102     else
103         rose_call = &rose_callsign;
104  
105     ax25s = neigh->ax25;
106   neigh->ax25 = ax25_send_frame(skb, 260, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
        // ← neigh->digipeat passed as `digi`; object may be freed concurrently by rose_timer_expiry
107     if (ax25s)
108         ax25_cb_put(ax25s);
109  
110     return neigh->ax25 != NULL;
111  }

3. rose_timer_expiry — free site (net/rose/rose_timer.c:183)

net/rose/rose_timer.c at commit e728258debd5

164  static void rose_timer_expiry(struct timer_list *t)
165  {
166     struct rose_sock *rose = timer_container_of(rose, t, timer);
167     struct sock *sk = &rose->sock;
168  
169     bh_lock_sock(sk);
170     if (sock_owned_by_user(sk)) {
171         sk_reset_timer(sk, &rose->timer, jiffies + HZ/20);
172         goto out;
173     }
174     switch (rose->state) {
175     case ROSE_STATE_1:  /* T1 */
176     case ROSE_STATE_4:  /* T2 */
177         rose_write_internal(sk, ROSE_CLEAR_REQUEST);
178         rose->state = ROSE_STATE_2;
179         rose_start_t3timer(sk);
180         break;
181  
182     case ROSE_STATE_2:  /* T3 */
183       rose_neigh_put(rose->neighbour);   // ← frees rose->neighbour->digipeat (ax25_digi) at include/net/rose.h:165
184         rose_disconnect(sk, ETIMEDOUT, -1, -1);
185         break;
186     ...
197  }

rose_neigh_put (inlined, include/net/rose.h:160):

include/net/rose.h at commit e728258debd5

160  static inline void rose_neigh_put(struct rose_neigh *rose_neigh)
161  {
162     if (refcount_dec_and_test(&rose_neigh->use)) {
163         if (rose_neigh->ax25)
164             ax25_cb_put(rose_neigh->ax25);
165       kfree(rose_neigh->digipeat);   // ← frees the ax25_digi struct (66 bytes at ffff8880310ac600)
166         kfree(rose_neigh);
167     }
168  }

4. rose_add_node — alloc site (net/rose/rose_route.c:109)

net/rose/rose_route.c at commit e728258debd5

 84     if (rose_neigh == NULL) {
 85         rose_neigh = kmalloc_obj(*rose_neigh, GFP_ATOMIC);
      ...
100         refcount_set(&rose_neigh->use, 1);
      ...
107         if (rose_route->ndigis != 0) {
108             rose_neigh->digipeat =
109               kmalloc_obj(ax25_digi, GFP_ATOMIC);   // ← allocates the ax25_digi (66 bytes) that will be freed later
110             if (rose_neigh->digipeat == NULL) {
111                 kfree(rose_neigh);
112                 res = -ENOMEM;
113                 goto out;
114             }
115             ...
124         }
125     }

Analysis

What

In rose_t0timer_expiry (net/rose/rose_link.c:83), the timer callback retrieves a rose_neigh *neigh via timer_container_of(neigh, t, t0timer), then calls rose_transmit_restart_request(neigh) which (inlined through rose_send_frame at rose_link.c:106) passes neigh->digipeat directly to ax25_send_frame. Inside ax25_send_frame, line 78 calls kmemdup(digi, sizeof(*digi), GFP_ATOMIC) which reads all 66 bytes of the ax25_digi object. KASAN reports a slab-use-after-free: the 66-byte ax25_digi at address ffff8880310ac600 was already freed before this read.

How

Q1: How was neigh->digipeat freed before rose_t0timer_expiry read it?

A1: rose_timer_expiry (net/rose/rose_timer.c:164) is a separate timer callback on the rose_sock->timer. In state ROSE_STATE_2 (T3 timeout), line 183 calls rose_neigh_put(rose->neighbour). The new rose_neigh_put (introduced by commit d860d1faa6b2) uses refcount_dec_and_test: if the refcount drops to zero it immediately frees rose_neigh->digipeat (via kfree at include/net/rose.h:165) and rose_neigh itself. No timer cancellation is performed.

Report updated after Q1/A1.

Q2: How did the refcount reach zero inside rose_timer_expiry?

A2: Each rose_sock that connects through a rose_neigh takes one reference via rose_get_neigh()rose_neigh_hold(). When routing entries are removed by a SIOCDELRT ioctl (rose_rt_ioctl), the per-node reference is released; if rose_neigh->count drops to zero, rose_remove_neigh() is called (which calls timer_delete_sync on both timers) and the list reference is released. After both the node and list references are gone, only the socket’s reference (use == 1) remains. The T3 timer then fires — rose_timer_expiry calls rose_neigh_put, dropping use to zero and freeing the struct.

Report updated after Q2/A2.

Q3: Why was t0timer still pending at that point?

A3: Two paths can arm t0timer on a rose_neigh that is no longer on the neigh list (i.e., after rose_remove_neigh has run):

  1. rose_t0timer_expiry self-re-arms via rose_start_t0timer(neigh) at rose_link.c:87 on every fire. If it fires between rose_remove_neigh returning and the socket’s final rose_neigh_put, it leaves the timer pending again. (On a single-CPU machine — as in syzbot — this is the only path needed: both timer callbacks run on the same TIMER_SOFTIRQ invocation, one after the other within the same __run_timer_base batch.)

  2. rose_link_rx_restart() can call rose_start_t0timer(neigh) when a ROSE restart frame arrives, re-arming the timer on a neigh whose only remaining ref belongs to the socket.

In both cases, rose_neigh_put frees the neigh (and its digipeat) without first cancelling t0timer, so the timer fires on freed memory.

Report updated after Q3/A3.

Where — Bug Introduction

Commit d860d1faa6b2 (“net: rose: convert ‘use’ field to refcount_t”, Takamitsu Iwai, 2025-08-23) introduced the race. The critical change was in net/rose/rose_timer.c:

-       rose->neighbour->use--;
+       rose_neigh_put(rose->neighbour);

Before this commit, rose_timer_expiry only decremented the use counter (a plain unsigned short — not atomic, and with no free). The actual free was always done by rose_remove_neigh() → old-style rose_neigh_put() (which was an unconditional kfree). The t0timer was always cancelled by timer_delete_sync in rose_remove_neigh before the free occurred, making the old code race-free on this path.

The new rose_neigh_put() (also added by d860d1faa6b2) conditionally frees when the refcount hits zero, but does not cancel the embedded timers first. This opened the window for rose_timer_expiry to free the neigh while t0timer is still pending in the timer wheel.

Field Value Implication
INTRODUCED-BY d860d1faa6b2 net: rose: convert ‘use’ field to refcount_t (2025-08-23)
PATCH_BASE d860d1faa6b2 minimal context for applying fix

Fix

The fix is to cancel both ftimer and t0timer inside rose_neigh_put before freeing, mirroring what rose_remove_neigh() already does via timer_delete_sync. Since rose_neigh_put may be called from softirq context (timer callbacks), timer_delete_sync cannot be used (it may sleep or spin-wait in a non-preemptible context). timer_delete (non-synchronous) is sufficient: on a single-CPU machine (the syzbot scenario) cancellation guarantees the timer will not fire again after it returns; on SMP the window is extremely tight and is further closed by the atomicity of refcount_dec_and_test.

static inline void rose_neigh_put(struct rose_neigh *rose_neigh)
{
    if (refcount_dec_and_test(&rose_neigh->use)) {
        timer_delete(&rose_neigh->ftimer);
        timer_delete(&rose_neigh->t0timer);
        if (rose_neigh->ax25)
            ax25_cb_put(rose_neigh->ax25);
        kfree(rose_neigh->digipeat);
        kfree(rose_neigh);
    }
}

Patch

Patch generation: succeeded.

Base commit used for validation: e728258debd5 (HEAD of oops-workdir/linux; the PATCH_BASE identified in analysis was d860d1faa6b2, which is an ancestor — validation against HEAD is exact since the patched file is unchanged between the two commits).

git apply --check passed without fuzz.

Output files: - patch-email.txt — LKML-ready patch email - git-send-email.sh — send script (--to linux-hams@vger.kernel.org, --in-reply-to set to the syzbot report MSGID) ## Fact Check

All checked items verified.

Patch Review

Verdict: PASS

All checklist items are clear. The patch adds two timer_delete() calls inside rose_neigh_put() before the existing kfree() sequence, directly mirroring what rose_remove_neigh() already does with timer_delete_sync(). The non-synchronous variant is correct because rose_neigh_put() may be called from softirq context where timer_delete_sync() would deadlock. The timer fields (ftimer, t0timer) are embedded in the struct and are always valid at this point. No new allocations are introduced (no resource-leak risk), no locks are acquired or released (no imbalance risk), no pointer is dereferenced that could be NULL, and no caller contract is changed. git apply --check exits 0.