| 0/0 |
2026/08/02 01:17 |
flow |
assessment-security |
4m
Results: map[DenialOfService:true Explanation:Based on the analysis of the bug report and the kernel source code, here is the
assessment of the vulnerability:
**Vulnerability Analysis:**
The bug is a Use-After-Free (UAF) vulnerability in the AF_RXRPC network protocol
subsystem, specifically caused by a race condition between the destruction of an
`rxrpc_connection` object and its timer callback `rxrpc_connection_timer`.
1. When the last reference to an `rxrpc_connection` is dropped,
`rxrpc_put_connection()` is called. This function calls
`timer_delete(&conn->timer)` (which is non-blocking) and then schedules the
connection's destructor (`rxrpc_clean_up_connection()`).
2. If the timer callback is already running concurrently on another CPU, it
executes `rxrpc_poke_conn()`, which attempts to take a new reference on the
connection using `rxrpc_get_connection()`.
3. `rxrpc_get_connection()` uses `refcount_inc()` instead of
`refcount_inc_not_zero()`. This increments the reference count from 0 to 1
(triggering the `refcount_t: addition on 0; use-after-free` warning) and adds
the connection to the `local->conn_attend_q` list to be processed by the I/O
thread.
4. Meanwhile, the destructor `rxrpc_clean_up_connection()` waits for the timer
callback to finish using `timer_delete_sync()`, and then proceeds to free the
connection memory via RCU.
5. Later, the I/O thread (`rxrpc_io_thread()`) processes the `conn_attend_q`
list, accesses the freed connection memory (Use-After-Free), and calls
`rxrpc_put_connection()` again, which can lead to further memory corruption.
**Exploitability:**
This vulnerability is **highly exploitable**. An attacker can spray the heap to
reallocate and control the freed `rxrpc_connection` object. When the I/O thread
accesses the attacker-controlled object, it can be manipulated to hijack control
flow or corrupt arbitrary memory (e.g., by forging the `conn->local` pointer to
cause a spinlock on an arbitrary memory address).
**Access and Trigger:**
The bug can be triggered by any **unprivileged local user**. Creating an
AF_RXRPC socket (`socket(AF_RXRPC, SOCK_DGRAM, PF_INET)`) does not require any
special capabilities (no `capable()` or `ns_capable()` checks are present in
`rxrpc_create()`). The user can initiate a client connection and trigger the
race condition by carefully timing the connection's destruction with the timer's
expiration. Since it is accessible to unprivileged users in the initial
namespace, it is also accessible within user namespaces.
It is not considered a remote network trigger because the timer
(`rxrpc_reduce_conn_timer`) is only armed for client connections initiated
locally, meaning a remote attacker cannot trigger this independently without a
local process initiating the connection.
**Conclusion:**
This is a critical local privilege escalation (LPE) and Denial-of-Service (DoS)
vulnerability accessible to unprivileged users. Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/08/02 01:17 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/08/02 01:17 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/d8c60cecff4c452a1f8b661b13d5b4d032f7121a]
|
| 3/1 |
2026/08/02 01:17 |
action |
kernel-builder |
0m
Results: map[KernelObj:/app/workdir/cache/build/c7f5bb50f67b73674fe06e2a21046207c0714f4c]
|
| 4/1 |
2026/08/02 01:17 |
action |
codesearch-prepare |
0m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/08/02 01:18 |
agent |
expert |
3m
Model:
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption, elevated privileges, or an information leak.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Information leaks are exploitable on their own and should be classified as such. A bug that copies kernel
memory contents to userspace (e.g. an out-of-bounds read whose result is returned to the caller, or
uninitialized stack/heap bytes written to a user buffer) is exploitable: it can reveal kernel pointer
values and defeat KASLR, expose sensitive data such as cryptographic keys or other processes' memory, and
serves as a necessary building block in most modern kernel privilege-escalation exploit chains. Do not classify
an information leak as non-exploitable solely because it does not directly cause a memory write or control-flow
hijack; the leak itself is the exploit primitive.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
------------[ cut here ]------------
refcount_t: addition on 0; use-after-free.
WARNING: lib/refcount.c:25 at refcount_warn_saturate+0x111/0x130 lib/refcount.c:25, CPU#0: syz.7.10144/5848
Modules linked in:
CPU: 0 UID: 0 PID: 5848 Comm: syz.7.10144 Tainted: G L syzkaller #0 PREEMPT(full)
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/16/2026
RIP: 0010:refcount_warn_saturate+0x111/0x130 lib/refcount.c:25
Code: cc e8 63 3a ec fc 48 8d 3d 6c 5e 0f 0c 67 48 0f b9 3a e8 52 3a ec fc 5b 5d e9 4b bb a0 06 e8 46 3a ec fc 48 8d 3d 5f 5e 0f 0c <67> 48 0f b9 3a e8 35 3a ec fc 5b 5d c3 cc cc cc cc 48 89 df e8 06
RSP: 0018:ffffc90000007bc8 EFLAGS: 00010046
RAX: 0000000000000000 RBX: ffff888040b2f038 RCX: ffffffff851e26eb
RDX: ffff888021b04a80 RSI: ffffffff851e279a RDI: ffffffff912d8600
RBP: 0000000000000002 R08: 0000000000000005 R09: 0000000000000004
R10: 0000000000000002 R11: 0000000000000001 R12: 0000000000000009
R13: 0000000000000001 R14: ffff888040b2f038 R15: 0000000000000001
FS: 000055557e548500(0000) GS:ffff888123dda000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000000110c38ed7a CR3: 0000000074082000 CR4: 00000000003526f0
Call Trace:
<IRQ>
__refcount_add include/linux/refcount.h:289 [inline]
__refcount_inc include/linux/refcount.h:366 [inline]
rxrpc_get_connection+0xef/0x100 net/rxrpc/conn_object.c:251
rxrpc_poke_conn+0xf7/0x240 net/rxrpc/conn_object.c:37
call_timer_fn+0x19a/0x610 kernel/time/timer.c:1748
expire_timers kernel/time/timer.c:1799 [inline]
__run_timers+0x757/0xb00 kernel/time/timer.c:2374
__run_timer_base kernel/time/timer.c:2386 [inline]
__run_timer_base kernel/time/timer.c:2378 [inline]
run_timer_base+0x114/0x190 kernel/time/timer.c:2395
run_timer_softirq+0x1a/0x50 kernel/time/timer.c:2405
handle_softirqs+0x1ea/0x9b0 kernel/softirq.c:622
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0x162/0x210 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
sysvec_apic_timer_interrupt+0xa3/0xc0 arch/x86/kernel/apic/apic.c:1062
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:preempt_schedule_irq+0x4b/0x90 kernel/sched/core.c:7556
Code: 00 00 00 fc ff df 48 89 eb 48 c1 eb 03 48 01 d3 f6 c4 02 75 47 bf 01 00 00 00 e8 30 d1 18 f6 e8 7b ca 55 f6 fb bf 01 00 00 00 <e8> a0 91 ff ff 9c 58 fa f6 c4 02 75 1e bf 01 00 00 00 e8 1e 71 18
RSP: 0018:ffffc90003e87948 EFLAGS: 00000202
RAX: 000000000000826b RBX: ffffed1004360950 RCX: 0000000000000040
RDX: 0000000000000000 RSI: ffffffff8e1a76e9 RDI: 0000000000000001
RBP: ffff888021b04a80 R08: 0000000000000001 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000000000
R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
irqentry_exit_to_kernel_mode_preempt include/linux/irq-entry-common.h:468 [inline]
irqentry_exit_to_kernel_mode include/linux/irq-entry-common.h:539 [inline]
irqentry_exit+0x205/0xa00 kernel/entry/common.c:167
asm_sysvec_reschedule_ipi+0x1a/0x20 arch/x86/include/asm/idtentry.h:679
RIP: 0010:lockdep_unregister_key+0x12b/0x1d0 kernel/locking/lockdep.c:6616
Code: e8 da dd ff ff 48 83 2d 5a 88 9e 14 01 89 c3 e8 2b e0 ff ff 9c 58 f6 c4 02 0f 85 8a 00 00 00 f7 c5 00 02 00 00 74 01 fb 84 db <75> 40 5b 5d 41 5c e9 fa a8 09 00 9c 58 f6 c4 02 0f 84 16 ff ff ff
RSP: 0018:ffffc90003e87a50 EFLAGS: 00000246
RAX: 0000000000000046 RBX: 0000000000000000 RCX: 0000000000000001
RDX: 0000000000000000 RSI: ffffffff8e29db9c RDI: ffffffff8c402680
RBP: 0000000000000246 R08: 000000000008ab94 R09: ffffffff96958588
R10: 0000000000000200 R11: 0000000000000000 R12: ffffffff97eb2ec8
R13: ffff88804fa48000 R14: ffff88804fa48568 R15: 0000000000000100
qdisc_lock_uninit include/net/pkt_sched.h:332 [inline]
__qdisc_destroy+0x172/0x530 net/sched/sch_generic.c:1124
qdisc_put+0xb0/0xf0 net/sched/sch_generic.c:1150
shutdown_scheduler_queue+0xa5/0x160 net/sched/sch_generic.c:1203
netdev_for_each_tx_queue include/linux/netdevice.h:2735 [inline]
dev_shutdown+0xb3/0x440 net/sched/sch_generic.c:1512
unregister_netdevice_many_notify+0xd55/0x2140 net/core/dev.c:12434
unregister_netdevice_many net/core/dev.c:12507 [inline]
unregister_netdevice_queue net/core/dev.c:12319 [inline]
unregister_netdevice_queue+0x30b/0x3c0 net/core/dev.c:12309
unregister_netdevice include/linux/netdevice.h:3472 [inline]
__tun_detach+0x1173/0x1480 drivers/net/tun.c:621
tun_detach drivers/net/tun.c:637 [inline]
tun_chr_close+0xc2/0x220 drivers/net/tun.c:3442
__fput+0x3ff/0xb50 fs/file_table.c:512
task_work_run+0x150/0x240 kernel/task_work.c:233
resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
__exit_to_user_mode_loop kernel/entry/common.c:70 [inline]
exit_to_user_mode_loop+0x1d8/0x6f0 kernel/entry/common.c:101
__exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline]
do_syscall_64+0x666/0x870 arch/x86/entry/syscall_64.c:100
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fb9a859e019
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fff6e3ae3e8 EFLAGS: 00000246 ORIG_RAX: 00000000000001b4
RAX: 0000000000000000 RBX: 00007fff6e3ae4d0 RCX: 00007fb9a859e019
RDX: 0000000000000000 RSI: 000000000000001e RDI: 0000000000000003
RBP: 00000000001873db R08: 0000000000000001 R09: 0000000000000000
R10: 0000001b2ce20000 R11: 0000000000000246 R12: 00007fff6e3ae510
R13: 00007fb9a8825fac R14: 0000000000187458 R15: 00007fb9a8825fa0
</TASK>
----------------
Code disassembly (best guess):
0: cc int3
1: e8 63 3a ec fc call 0xfcec3a69
6: 48 8d 3d 6c 5e 0f 0c lea 0xc0f5e6c(%rip),%rdi # 0xc0f5e79
d: 67 48 0f b9 3a ud1 (%edx),%rdi
12: e8 52 3a ec fc call 0xfcec3a69
17: 5b pop %rbx
18: 5d pop %rbp
19: e9 4b bb a0 06 jmp 0x6a0bb69
1e: e8 46 3a ec fc call 0xfcec3a69
23: 48 8d 3d 5f 5e 0f 0c lea 0xc0f5e5f(%rip),%rdi # 0xc0f5e89
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: e8 35 3a ec fc call 0xfcec3a69
34: 5b pop %rbx
35: 5d pop %rbp
36: c3 ret
37: cc int3
38: cc int3
39: cc int3
3a: cc int3
3b: 48 89 df mov %rbx,%rdi
3e: e8 .byte 0xe8
3f: 06 (bad)
Reply: Based on the analysis of the bug report and the kernel source code, here is the assessment of the vulnerability:
**Vulnerability Analysis:**
The bug is a Use-After-Free (UAF) vulnerability in the AF_RXRPC network protocol subsystem, specifically caused by a race condition between the destruction of an `rxrpc_connection` object and its timer callback `rxrpc_connection_timer`.
1. When the last reference to an `rxrpc_connection` is dropped, `rxrpc_put_connection()` is called. This function calls `timer_delete(&conn->timer)` (which is non-blocking) and then schedules the connection's destructor (`rxrpc_clean_up_connection()`).
2. If the timer callback is already running concurrently on another CPU, it executes `rxrpc_poke_conn()`, which attempts to take a new reference on the connection using `rxrpc_get_connection()`.
3. `rxrpc_get_connection()` uses `refcount_inc()` instead of `refcount_inc_not_zero()`. This increments the reference count from 0 to 1 (triggering the `refcount_t: addition on 0; use-after-free` warning) and adds the connection to the `local->conn_attend_q` list to be processed by the I/O thread.
4. Meanwhile, the destructor `rxrpc_clean_up_connection()` waits for the timer callback to finish using `timer_delete_sync()`, and then proceeds to free the connection memory via RCU.
5. Later, the I/O thread (`rxrpc_io_thread()`) processes the `conn_attend_q` list, accesses the freed connection memory (Use-After-Free), and calls `rxrpc_put_connection()` again, which can lead to further memory corruption.
**Exploitability:**
This vulnerability is **highly exploitable**. An attacker can spray the heap to reallocate and control the freed `rxrpc_connection` object. When the I/O thread accesses the attacker-controlled object, it can be manipulated to hijack control flow or corrupt arbitrary memory (e.g., by forging the `conn->local` pointer to cause a spinlock on an arbitrary memory address).
**Access and Trigger:**
The bug can be triggered by any **unprivileged local user**. Creating an AF_RXRPC socket (`socket(AF_RXRPC, SOCK_DGRAM, PF_INET)`) does not require any special capabilities (no `capable()` or `ns_capable()` checks are present in `rxrpc_create()`). The user can initiate a client connection and trigger the race condition by carefully timing the connection's destruction with the timer's expiration. Since it is accessible to unprivileged users in the initial namespace, it is also accessible within user namespaces.
It is not considered a remote network trigger because the timer (`rxrpc_reduce_conn_timer`) is only armed for client connections initiated locally, meaning a remote attacker cannot trigger this independently without a local process initiating the connection.
**Conclusion:**
This is a critical local privilege escalation (LPE) and Denial-of-Service (DoS) vulnerability accessible to unprivileged users.
|
| 6/2 |
2026/08/02 01:18 |
llm |
expert |
0m
Model:
Tokens:
input: 10004
output: 40
thoughts: 201
Thoughts: **Debugging a Use-After-Free in rxrpc_get_connection**
Okay, so I've got a bug report: a nasty `refcount_t` issue β specifically, an addition on zero, leading to a use-after-free β deep within `rxrpc_get_connection`. The trace is pretty clear, pointing directly to the problem: It seems to be happening when a reference is added via `__refcount_add` (called through `__refcount_inc`) within the `rxrpc_get_connection` function itself. That function is then called by `rxrpc_poke_conn`, which itself is being called from a timer function, `call_timer_fn`. The interesting call here is `rxrpc_poke_conn` and the fact that it is called from a timer. Let's dig into `rxrpc_poke_conn` in `net/rxrpc/conn_object.c` and see what's happening there with the reference counting. This should give me a better understanding of the code flow and the potential race condition that is causing the refcount to hit zero when it shouldn't.
|
| 7/2 |
2026/08/02 01:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rxrpc/conn_object.c Name:rxrpc_poke_conn]
Results: map[SourceCode: 26: void rxrpc_poke_conn(struct rxrpc_connection *conn, enum rxrpc_conn_trace why)
27: {
28: struct rxrpc_local *local = conn->local;
29: bool busy;
30:
31: if (WARN_ON_ONCE(!local))
32: return;
33:
34: spin_lock_irq(&local->lock);
35: busy = !list_empty(&conn->attend_link);
36: if (!busy) {
37: rxrpc_get_connection(conn, why);
38: list_add_tail(&conn->attend_link, &local->conn_attend_q);
39: }
40: spin_unlock_irq(&local->lock);
41: rxrpc_wake_up_io_thread(local);
42: }
SourceFile:net/rxrpc/conn_object.c]
|
| 8/2 |
2026/08/02 01:18 |
llm |
expert |
0m
Model:
Tokens:
input: 10766
output: 57
thoughts: 56
Thoughts: Okay, let's break this down.
**Diving into the Use-After-Free Bug**
Right, so the core of the problem is a classic use-after-free, plain and simple. It looks like `rxrpc_get_connection` is being called on a connection that's *already* been freed β its reference count is zero. That's never good. Now, the key here is to figure out the path the code is taking to get to this state. The stack trace is pointing towards `rxrpc_poke_conn`, which itself is invoked by some timer. So, where's this timer coming from? I need to track down the specific timer that's triggering this behavior.
|
| 9/2 |
2026/08/02 01:18 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/rxrpc/conn_object.c IncludeSnippetLines:2 Name:rxrpc_poke_conn SourceTreePrefix:net/rxrpc]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_abort_conn SourceFile:net/rxrpc/conn_event.c SourceLine:66 SourceSnippet: 64: RXRPC_CALL_LOCALLY_ABORTED)) {
65: trace_rxrpc_abort(0, why, cid, call, seq, abort_code, err);
66: rxrpc_poke_conn(conn, rxrpc_conn_get_poke_abort);
67: }
68: return -EPROTO;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_connection_timer SourceFile:net/rxrpc/conn_object.c SourceLine:49 SourceSnippet: 47: container_of(timer, struct rxrpc_connection, timer);
48:
49: rxrpc_poke_conn(conn, rxrpc_conn_get_poke_timer);
50: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_post_response SourceFile:net/rxrpc/conn_event.c SourceLine:598 SourceSnippet: 596: }
597: spin_unlock_irq(&local->lock);
598: rxrpc_poke_conn(conn, rxrpc_conn_get_poke_response);
599: rxrpc_free_skb(old, rxrpc_skb_put_old_response);
600: }
]] TruncatedOutput:false]
|
| 10/2 |
2026/08/02 01:18 |
llm |
expert |
0m
Model:
Tokens:
input: 11468
output: 40
thoughts: 37
Thoughts: Okay, here's my interpretation of those thoughts, framed as if they are my own:
**Investigating the Connection Timer**
Alright, I need to get to the bottom of this timer. I'm seeing references to `rxrpc_connection_timer` and that immediately flags it as something relevant to the Remote Procedure Call (rxrpc) connection management within the network stack. My first instinct is to dig into the code and see exactly what this timer is doing, so let's head over to `net/rxrpc/conn_object.c`. That file should contain the definition and likely the main logic surrounding the connections themselves. I'm expecting to find the timer initialization, its handler function, and how it interacts with the state machine of an rxrpc connection. Time to dive in.
|
| 11/2 |
2026/08/02 01:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rxrpc/conn_object.c Name:rxrpc_connection_timer]
Results: map[SourceCode: 44: static void rxrpc_connection_timer(struct timer_list *timer)
45: {
46: struct rxrpc_connection *conn =
47: container_of(timer, struct rxrpc_connection, timer);
48:
49: rxrpc_poke_conn(conn, rxrpc_conn_get_poke_timer);
50: }
SourceFile:net/rxrpc/conn_object.c]
|
| 12/2 |
2026/08/02 01:18 |
llm |
expert |
0m
Model:
Tokens:
input: 11846
output: 57
thoughts: 22
Thoughts: Here's my thought process, summarized as you requested:
**Investigating the `rxrpc_connection_timer`**
Alright, let's dive in. I need to understand the lifecycle of this `rxrpc_connection_timer`. My first step is clear: I need to trace back from where it's *used* to understand its initialization. I need to see exactly where it's defined and how the system controls it. What are the key points in this timer's management: When is it started? When is it stopped or canceled? What data structures are involved and how are they affected by this timer? Once I have a good handle on its basic functionality, I can look for potential problems in its implementation. Any concurrency issues? Race conditions? Memory leaks related to its lifecycle? Let's get to work!
|
| 13/2 |
2026/08/02 01:18 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/rxrpc/conn_object.c IncludeSnippetLines:2 Name:rxrpc_connection_timer SourceTreePrefix:net/rxrpc]
Results: map[References:[map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:rxrpc_alloc_connection SourceFile:net/rxrpc/conn_object.c SourceLine:65 SourceSnippet: 63: if (conn) {
64: INIT_LIST_HEAD(&conn->cache_link);
65: timer_setup(&conn->timer, &rxrpc_connection_timer, 0);
66: INIT_WORK(&conn->processor, rxrpc_process_connection);
67: INIT_WORK(&conn->destructor, rxrpc_clean_up_connection);
]] TruncatedOutput:false]
|
| 14/2 |
2026/08/02 01:18 |
llm |
expert |
0m
Model:
Tokens:
input: 12279
output: 42
thoughts: 23
Thoughts: Okay, here's my interpretation of what's going on, thinking through those functions as if they're relevant to a situation I'm currently pondering:
**Analyzing `rxrpc_clean_up_connection` and `rxrpc_put_connection`**
Alright, I need to really understand how `rxrpc_clean_up_connection` and `rxrpc_put_connection` are architected. I see them as critical components in the connection lifecycle, likely dealing with resource management and potential error handling within the RXRPC protocol stack. My initial hunch is that `rxrpc_clean_up_connection` probably handles the *final* stages of decommissioning a connection. I'd expect it to be a catch-all for freeing allocated memory, releasing network resources, and potentially signaling to other system components that the connection is now closed. This is likely the place where you'd handle things like freeing the connection object itself, decrementing reference counts, and possibly even closing the underlying network socket. The most important thing here is to ensure that there are no leaks and that the resources are properly managed so that they don't impact overall system performance, I also need to make sure that the system can handle any error conditions that may arise from this phase of the process.
Now, `rxrpc_put_connection`. Based on the name, this probably signifies a "return" or "release" operation. It is probably used to put a connection back into some kind of pool or release a reference that allows the connection to be reclaimed. It could be used in conjunction with the connection cache, potentially decrementing a reference count. I'd assume there's a reference counter or some sort of lifecycle state associated with each connection. I need to figure out what happens if the reference count hits zero. Is this when cleanup is triggered? Is it a soft-close situation? And if so, how does that relate to `rxrpc_clean_up_connection`? There's a possible race condition I need to be aware of: can the cleanup happen concurrently with operations using the connection? I'm betting that proper synchronization mechanisms, like mutexes or read-write locks, are critical here to protect shared data structures. This is the bit I really need to get right.
I need to dig into the surrounding code to determine how these two functions interact, where they are called, and under what conditions. Understanding the calling contexts, error paths, and any resource dependencies is essential to ensuring proper connection management and prevent those nasty race conditions that plague so many distributed systems. Gotta make sure those connections are really gone when we're done with them!
|
| 15/2 |
2026/08/02 01:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rxrpc/conn_object.c Name:rxrpc_clean_up_connection]
Results: map[SourceCode: 303: /*
304: * Clean up a dead connection.
305: */
306: static void rxrpc_clean_up_connection(struct work_struct *work)
307: {
308: struct rxrpc_connection *conn =
309: container_of(work, struct rxrpc_connection, destructor);
310: struct rxrpc_net *rxnet = conn->rxnet;
311:
312: ASSERT(!conn->channels[0].call &&
313: !conn->channels[1].call &&
314: !conn->channels[2].call &&
315: !conn->channels[3].call);
316: ASSERT(list_empty(&conn->cache_link));
317:
318: timer_delete_sync(&conn->timer);
319: cancel_work_sync(&conn->processor); /* Processing may restart the timer */
320: timer_delete_sync(&conn->timer);
321:
322: write_lock(&rxnet->conn_lock);
323: list_del_init(&conn->proc_link);
324: write_unlock(&rxnet->conn_lock);
325:
326: if (conn->pmtud_probe) {
327: trace_rxrpc_pmtud_lost(conn, 0);
328: conn->peer->pmtud_probing = false;
329: conn->peer->pmtud_pending = true;
330: }
331:
332: rxrpc_purge_queue(&conn->rx_queue);
333: rxrpc_free_skb(conn->tx_response, rxrpc_skb_put_response);
334:
335: rxrpc_kill_client_conn(conn);
336:
337: conn->security->clear(conn);
338: key_put(conn->key);
339: rxrpc_put_bundle(conn->bundle, rxrpc_bundle_put_conn);
340: rxrpc_put_peer(conn->peer, rxrpc_peer_put_conn);
341: rxrpc_put_local(conn->local, rxrpc_local_put_kill_conn);
342:
343: /* Drain the Rx queue. Note that even though we've unpublished, an
344: * incoming packet could still be being added to our Rx queue, so we
345: * will need to drain it again in the RCU cleanup handler.
346: */
347: rxrpc_purge_queue(&conn->rx_queue);
348:
349: page_frag_cache_drain(&conn->tx_data_alloc);
350: call_rcu(&conn->rcu, rxrpc_rcu_free_connection);
351: }
SourceFile:net/rxrpc/conn_object.c]
|
| 16/2 |
2026/08/02 01:18 |
llm |
expert |
0m
Model:
Tokens:
input: 13728
output: 40
thoughts: 57
Thoughts: Here's my summary, as the thought process unfolds:
**Digging into Connection Cleanup in RxRPC**
Okay, I'm tracing through this RxRPC code and something just jumped out at me. I was looking at how connections are cleaned up and I see `rxrpc_clean_up_connection` calls `timer_delete_sync(&conn->timer)`. That's a little unusual, isn't it? I'm pretty sure `timer_delete_sync` is supposed to be safe from being called in any context, and `rxrpc_clean_up_connection` is, if memory serves, *also* called from a workqueue. Hmm. This could be a race condition waiting to happen, if the workqueue is running in parallel. I need to refresh my memory on the call stacks, to see if I am missing something.
Let's quickly check where this `rxrpc_clean_up_connection` function is used. I'll need to figure out the path the code execution takes to get there. Okay, where is that function called? I will check `rxrpc_put_connection` to see if that gives me any more clues.
|
| 17/2 |
2026/08/02 01:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rxrpc/conn_object.c Name:rxrpc_put_connection]
Results: map[SourceCode: 353: /*
354: * Drop a ref on a connection.
355: */
356: void rxrpc_put_connection(struct rxrpc_connection *conn,
357: enum rxrpc_conn_trace why)
358: {
359: unsigned int debug_id;
360: bool dead;
361: int r;
362:
363: if (!conn)
364: return;
365:
366: debug_id = conn->debug_id;
367: dead = __refcount_dec_and_test(&conn->ref, &r);
368: trace_rxrpc_conn(debug_id, r - 1, why);
369: if (dead) {
370: timer_delete(&conn->timer);
371: cancel_work(&conn->processor);
372:
373: if (in_softirq() || work_busy(&conn->processor) ||
374: timer_pending(&conn->timer))
375: /* Can't use the rxrpc workqueue as we need to cancel/flush
376: * something that may be running/waiting there.
377: */
378: schedule_work(&conn->destructor);
379: else
380: rxrpc_clean_up_connection(&conn->destructor);
381: }
382: }
SourceFile:net/rxrpc/conn_object.c]
|
| 18/2 |
2026/08/02 01:18 |
llm |
expert |
0m
Model:
Tokens:
input: 14521
output: 40
thoughts: 1238
Thoughts: **Analyzing a Potential Race Condition in the RxRPC Connection Management**
Okay, let's break this down. My initial thought is that `rxrpc_put_connection` calls `timer_delete(&conn->timer)`. The problem is that `timer_delete` isn't synchronous. This means even after the call, the timer callback might still be executing on a different CPU.
If `rxrpc_put_connection` detects the connection is `dead`, it does call `timer_delete(&conn->timer)`. Then, the code checks `timer_pending(&conn->timer)`. But here's the catch: if the timer is *actually* running on another CPU, `timer_pending` can return `false` because the timer might already have been dequeued. Wait, `timer_delete` returns 1 if it *did* deactivate the timer and 0 if it didn't. If the timer is actually running when `timer_delete` is called, it returns 0! `timer_pending` could therefore return false in this case, meaning that the `dead` connection would erroneously attempt to continue to cleanup.
Now, if we are not in a soft interrupt and the `work_busy` flag isn't set, then the code might even call `rxrpc_clean_up_connection` directly. The key detail is that `rxrpc_clean_up_connection` calls `timer_delete_sync(&conn->timer)`. This is good; if called directly, it waits for the timer to finish, preventing a race. But, what if `schedule_work(&conn->destructor)` is called instead? The `rxrpc_clean_up_connection` will be executed later, *after* we've returned from `rxrpc_put_connection`. Meanwhile, the timer callback, `rxrpc_connection_timer`, could *still* be running on another CPU.
The timer callback calls `rxrpc_poke_conn(conn, rxrpc_conn_get_poke_timer)`. Let's look at `rxrpc_poke_conn`:
```c
spin_lock_irq(&local->lock);
busy = !list_empty(&conn->attend_link);
if (!busy) {
rxrpc_get_connection(conn, why);
list_add_tail(&conn->attend_link, &local->conn_attend_q);
}
spin_unlock_irq(&local->lock);
```
If the timer is currently running, `conn->ref` is already 0 (because `rxrpc_put_connection` has been called). `rxrpc_get_connection` will then increment the refcount from 0, and that's the warning! The `refcount_t: addition on 0; use-after-free` message we are seeing!
The bug report says the trace includes the timer callback:
```
__refcount_add include/linux/refcount.h:289 [inline]
__refcount_inc include/linux/refcount.h:366 [inline]
rxrpc_get_connection+0xef/0x100 net/rxrpc/conn_object.c:251
rxrpc_poke_conn+0xf7/0x240 net/rxrpc/conn_object.c:37
call_timer_fn+0x19a/0x610 kernel/time/timer.c:1748
```
So yes, the timer callback is running. Is this a real use-after-free? If `rxrpc_put_connection` schedules the destructor, the destructor *will* call `timer_delete_sync(&conn->timer)`, which *will* wait for the timer to finish. However, the timer callback *already* tries to take a reference using `rxrpc_get_connection`. `rxrpc_get_connection` uses `refcount_inc`, which warns on 0.
If the timer callback successfully adds `conn` to `local->conn_attend_q`, then the processor work might be scheduled. Let's look at `rxrpc_clean_up_connection` again:
```c
timer_delete_sync(&conn->timer);
cancel_work_sync(&conn->processor); /* Processing may restart the timer */
timer_delete_sync(&conn->timer);
```
Right, it waits for the timer *and* the processor work to finish. But, the timer callback just incremented the refcount from 0 to 1! Then, the destructor frees the connection via RCU:
```c
call_rcu(&conn->rcu, rxrpc_rcu_
|
| 19/2 |
2026/08/02 01:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rxrpc/local_object.c Name:rxrpc_io_thread]
Results: map[SourceCode: 427: /*
428: * I/O and event handling thread.
429: */
430: int rxrpc_io_thread(void *data)
431: {
432: struct rxrpc_connection *conn;
433: struct sk_buff_head rx_queue;
434: struct rxrpc_local *local = data;
435: struct rxrpc_call *call;
436: struct sk_buff *skb;
437: #ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
438: ktime_t now;
439: #endif
440: bool should_stop;
441: LIST_HEAD(conn_attend_q);
442: LIST_HEAD(call_attend_q);
443:
444: complete(&local->io_thread_ready);
445:
446: skb_queue_head_init(&rx_queue);
447:
448: set_user_nice(current, MIN_NICE);
449:
450: for (;;) {
451: rxrpc_inc_stat(local->rxnet, stat_io_loop);
452:
453: /* Inject a delay into packets if requested. */
454: #ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
455: now = ktime_get_real();
456: while ((skb = skb_peek(&local->rx_delay_queue))) {
457: if (ktime_before(now, skb->tstamp))
458: break;
459: skb = skb_dequeue(&local->rx_delay_queue);
460: skb_queue_tail(&local->rx_queue, skb);
461: }
462: #endif
463:
464: if (!skb_queue_empty(&local->rx_queue)) {
465: spin_lock_irq(&local->rx_queue.lock);
466: skb_queue_splice_tail_init(&local->rx_queue, &rx_queue);
467: spin_unlock_irq(&local->rx_queue.lock);
468: trace_rxrpc_iothread_rx(local, skb_queue_len(&rx_queue));
469: }
470:
471: /* Distribute packets and errors. */
472: while ((skb = __skb_dequeue(&rx_queue))) {
473: struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
474: switch (skb->mark) {
475: case RXRPC_SKB_MARK_PACKET:
476: skb->priority = 0;
477: if (!rxrpc_input_packet(local, skb))
478: rxrpc_reject_packet(local, skb);
479: trace_rxrpc_rx_done(skb->mark, skb->priority);
480: rxrpc_free_skb(skb, rxrpc_skb_put_input);
481: break;
482: case RXRPC_SKB_MARK_ERROR:
483: rxrpc_input_error(local, skb);
484: rxrpc_free_skb(skb, rxrpc_skb_put_error_report);
485: break;
486: case RXRPC_SKB_MARK_SERVICE_CONN_SECURED:
487: rxrpc_input_conn_event(sp->poke_conn, skb);
488: rxrpc_put_connection(sp->poke_conn, rxrpc_conn_put_poke);
489: rxrpc_free_skb(skb, rxrpc_skb_put_conn_secured);
490: break;
491: default:
492: WARN_ON_ONCE(1);
493: rxrpc_free_skb(skb, rxrpc_skb_put_unknown);
494: break;
495: }
496: }
497:
498: /* Deal with connections that want immediate attention. */
499: if (!list_empty_careful(&local->conn_attend_q)) {
500: spin_lock_irq(&local->lock);
501: list_splice_tail_init(&local->conn_attend_q, &conn_attend_q);
502: spin_unlock_irq(&local->lock);
503: }
504:
505: while ((conn = list_first_entry_or_null(&conn_attend_q,
506: struct rxrpc_connection,
507: attend_link))) {
508: spin_lock_irq(&local->lock);
509: list_del_init(&conn->attend_link);
510: spin_unlock_irq(&local->lock);
511: rxrpc_input_conn_event(conn, NULL);
512: rxrpc_put_connection(conn, rxrpc_conn_put_poke);
513: }
514:
515: if (test_and_clear_bit(RXRPC_CLIENT_CONN_REAP_TIMER,
516: &local->client_conn_flags))
517: rxrpc_discard_expired_client_conns(local);
518:
519: /* Deal with calls that want immediate attention. */
520: spin_lock_irq(&local->lock);
521: list_splice_tail_init(&local->call_attend_q, &call_attend_q);
522: spin_unlock_irq(&local->lock);
523:
524: while ((call = list_first_entry_or_null(&call_attend_q,
525: struct rxrpc_call,
526: attend_link))) {
527: spin_lock_irq(&local->lock);
528: list_del_init(&call->attend_link);
529: spin_unlock_irq(&local->lock);
530: trace_rxrpc_call_poked(call);
531: rxrpc_input_call_event(call);
532: rxrpc_put_call(call, rxrpc_call_put_poke);
533: }
534:
535: if (!list_empty(&local->new_client_calls))
536: rxrpc_connect_client_calls(local);
537:
538: set_current_state(TASK_INTERRUPTIBLE);
539: should_stop = kthread_should_stop();
540: if (!skb_queue_empty(&local->rx_queue) ||
541: !list_empty(&local->call_attend_q) ||
542: !list_empty(&local->conn_attend_q) ||
543: !list_empty(&local->new_client_calls) ||
544: test_bit(RXRPC_CLIENT_CONN_REAP_TIMER,
545: &local->client_conn_flags)) {
546: __set_current_state(TASK_RUNNING);
547: continue;
548: }
549:
550: if (should_stop)
551: break;
552:
553: #ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
554: skb = skb_peek(&local->rx_delay_queue);
555: if (skb) {
556: unsigned long timeout;
557: ktime_t tstamp = skb->tstamp;
558: ktime_t now = ktime_get_real();
559: s64 delay_ns = ktime_to_ns(ktime_sub(tstamp, now));
560:
561: if (delay_ns <= 0) {
562: __set_current_state(TASK_RUNNING);
563: continue;
564: }
565:
566: timeout = nsecs_to_jiffies(delay_ns);
567: timeout = umax(timeout, 1);
568: schedule_timeout(timeout);
569: __set_current_state(TASK_RUNNING);
570: continue;
571: }
572: #endif
573:
574: schedule();
575: }
576:
577: __set_current_state(TASK_RUNNING);
578: rxrpc_see_local(local, rxrpc_local_stop);
579: rxrpc_destroy_local(local);
580: WRITE_ONCE(local->io_thread, NULL);
581: rxrpc_see_local(local, rxrpc_local_stopped);
582: return 0;
583: }
SourceFile:net/rxrpc/io_thread.c]
|
| 20/2 |
2026/08/02 01:18 |
llm |
expert |
0m
Model:
Tokens:
input: 17782
output: 40
thoughts: 0
|
| 21/2 |
2026/08/02 01:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rxrpc/conn_object.c Name:rxrpc_get_connection]
Results: map[SourceCode: 243: /*
244: * Get a ref on a connection.
245: */
246: struct rxrpc_connection *rxrpc_get_connection(struct rxrpc_connection *conn,
247: enum rxrpc_conn_trace why)
248: {
249: int r;
250:
251: __refcount_inc(&conn->ref, &r);
252: trace_rxrpc_conn(conn->debug_id, r + 1, why);
253: return conn;
254: }
SourceFile:net/rxrpc/conn_object.c]
|
| 22/2 |
2026/08/02 01:18 |
llm |
expert |
0m
Model:
Tokens:
input: 19655
output: 58
thoughts: 574
Thoughts: **Analyzing a Potential Use-After-Free/Double-Free Vulnerability in rxrpc**
Okay, so I'm looking at this `rxrpc_io_thread` and how it handles the `conn_attend_q`. I see it's processing entries like this:
```c
while ((conn = list_first_entry_or_null(&conn_attend_q,
struct rxrpc_connection,
attend_link))) {
spin_lock_irq(&local->lock);
list_del_init(&conn->attend_link);
spin_unlock_irq(&local->lock);
rxrpc_input_conn_event(conn, NULL);
rxrpc_put_connection(conn, rxrpc_conn_put_poke);
}
```
My initial thought is, the timer callback increments a connection's refcount, adds it to `conn_attend_q`. The I/O thread grabs it, does its thing, and then calls `rxrpc_put_connection`. `rxrpc_put_connection` then decrements the refcount, potentially taking it to zero and calling `rxrpc_clean_up_connection`. But, wait a minute, isn't that same `rxrpc_clean_up_connection` *already* potentially scheduled or running from a previous `rxrpc_put_connection` that dropped the refcount?
Ah, I think I see a race condition. It's possible that `rxrpc_clean_up_connection` hasn't completely finished. I see that it calls `timer_delete_sync(&conn->timer)`. If the timer callback *is* still running, `timer_delete_sync` will block until that callback completes. That callback might be adding the connection back to the `conn_attend_q` and incrementing the refcount back to 1. Then `timer_delete_sync` returns and the `rxrpc_clean_up_connection` proceeds and frees the connection with RCU. Meanwhile, the I/O thread runs, pulls the same connection from `conn_attend_q`, and accesses freed memory β that's a classic use-after-free! Then, `rxrpc_put_connection` gets called again, dropping the refcount to zero and triggering `rxrpc_clean_up_connection` *again* β a double-free.
So yeah, that's definitely a use-after-free and double-free scenario. The question is, is it exploitable? Kernel use-after-free and double-free bugs are *generally* exploitable. The next step is to figure out how to trigger it. Can unprivileged users trigger this? Looking at the code, it seems AF_RXRPC sockets can be created by unprivileged users if the `af_rxrpc` module is loaded. I need to confirm that `af_rxrpc` is accessible to unprivileged users. If that's the case, we have a likely exploitable vulnerability.
|
| 23/2 |
2026/08/02 01:18 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/rxrpc/af_rxrpc.c IncludeSnippetLines:2 Name:rxrpc_family_ops SourceTreePrefix:net/rxrpc]
Results: map[References:<nil> TruncatedOutput:false]
|
| 24/2 |
2026/08/02 01:18 |
llm |
expert |
0m
Model:
Tokens:
input: 20951
output: 26
thoughts: 21
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Investigating AF_RXRPC Net Protocol Family Registration**
Alright, let's dive in. My immediate thought is to track down the registration for `AF_RXRPC` within the networking protocol family definitions. I need to understand how the system recognizes and handles this specific address family. Knowing the registration details is crucial for understanding its capabilities, its place in the system's architecture, and for debugging if there's any odd behavior. I'll need to locate where `net_proto_family` structures are defined or manipulated related to `AF_RXRPC`. Specifically, I'm thinking about the standard kernel data structures or registration functions. Perhaps there's an issue there that needs to be addressed.
|
| 25/2 |
2026/08/02 01:19 |
tool |
grepper |
0m
Error:
Args: map[Expression:register_netproto_family.*PF_RXRPC]
Results:
|
| 26/2 |
2026/08/02 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 21180
output: 20
thoughts: 13
Thoughts: Okay, here's my attempt at summarizing that thought process, tailored for an expert audience:
**Investigating `af_rxrpc` Registration**
Alright, let's dive in. My immediate thought is to initiate a search specifically targeting `af_rxrpc` registration. I need to understand how this particular address family is being brought into the system. Knowing the registration process is fundamental to understanding its overall behavior, from setup to interaction. I'll need to know where and how the kernel hooks in to handle this. I'm assuming registration implies some sort of initialization sequence or a binding process, but I need to pinpoint the exact mechanisms at play. Let's start digging and see what we can find.
|
| 27/2 |
2026/08/02 01:19 |
tool |
grepper |
0m
Args: map[Expression:sock_register.*rxrpc]
Results: map[Output:net/rxrpc/af_rxrpc.c=1036=static int __init af_rxrpc_init(void)
--
net/rxrpc/af_rxrpc.c-1073-
net/rxrpc/af_rxrpc.c:1074: ret = sock_register(&rxrpc_family_ops);
net/rxrpc/af_rxrpc.c-1075- if (ret < 0) {
]
|
| 28/2 |
2026/08/02 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 21483
output: 41
thoughts: 0
|
| 29/2 |
2026/08/02 01:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rxrpc/af_rxrpc.c Name:rxrpc_family_ops]
Results: map[SourceCode:1027: static const struct net_proto_family rxrpc_family_ops = {
1028: .family = PF_RXRPC,
1029: .create = rxrpc_create,
1030: .owner = THIS_MODULE,
1031: };
SourceFile:net/rxrpc/af_rxrpc.c]
|
| 30/2 |
2026/08/02 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 21636
output: 39
thoughts: 10
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Initial Assessment of RxRPC Creation**
Alright, time to dive in. My first instinct here is to zero in on `rxrpc_create`. This function is, of course, the bedrock of the entire RxRPC instantiation process. I need to quickly establish a baseline understanding of its core functionality, any potential gotchas, and how it interacts with the broader system. I'm thinking: is it thread-safe? What resource allocation is happening inside? How are connection objects being initialized? Are there any obvious areas for optimization or potential race conditions that jump out? Given the usual design trade-offs, how did the developers handle memory management, error handling, and concurrency? Let's check `rxrpc_create`.
|
| 31/2 |
2026/08/02 01:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rxrpc/af_rxrpc.c Name:rxrpc_create]
Results: map[SourceCode: 799: /*
800: * create an RxRPC socket
801: */
802: static int rxrpc_create(struct net *net, struct socket *sock, int protocol,
803: int kern)
804: {
805: struct rxrpc_net *rxnet;
806: struct rxrpc_sock *rx;
807: struct sock *sk;
808:
809: _enter("%p,%d", sock, protocol);
810:
811: /* we support transport protocol UDP/UDP6 only */
812: if (protocol != PF_INET &&
813: IS_ENABLED(CONFIG_AF_RXRPC_IPV6) && protocol != PF_INET6)
814: return -EPROTONOSUPPORT;
815:
816: if (sock->type != SOCK_DGRAM)
817: return -ESOCKTNOSUPPORT;
818:
819: sock->ops = &rxrpc_rpc_ops;
820: sock->state = SS_UNCONNECTED;
821:
822: sk = sk_alloc(net, PF_RXRPC, GFP_KERNEL, &rxrpc_proto, kern);
823: if (!sk)
824: return -ENOMEM;
825:
826: sock_init_data(sock, sk);
827: sock_set_flag(sk, SOCK_RCU_FREE);
828: sk->sk_state = RXRPC_UNBOUND;
829: sk->sk_write_space = rxrpc_write_space;
830: sk->sk_max_ack_backlog = 0;
831: sk->sk_destruct = rxrpc_sock_destructor;
832:
833: rx = rxrpc_sk(sk);
834: rx->family = protocol;
835: rx->calls = RB_ROOT;
836:
837: spin_lock_init(&rx->incoming_lock);
838: skb_queue_head_init(&rx->recvmsg_oobq);
839: rx->pending_oobq = RB_ROOT;
840: INIT_LIST_HEAD(&rx->sock_calls);
841: INIT_LIST_HEAD(&rx->to_be_accepted);
842: INIT_LIST_HEAD(&rx->recvmsg_q);
843: spin_lock_init(&rx->recvmsg_lock);
844: rwlock_init(&rx->call_lock);
845: memset(&rx->srx, 0, sizeof(rx->srx));
846:
847: rxnet = rxrpc_net(sock_net(&rx->sk));
848: timer_reduce(&rxnet->peer_keepalive_timer, jiffies + 1);
849:
850: _leave(" = 0 [%p]", rx);
851: return 0;
852: }
SourceFile:net/rxrpc/af_rxrpc.c]
|
| 32/2 |
2026/08/02 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 20495
output: 41
thoughts: 0
|
| 33/2 |
2026/08/02 01:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rxrpc/af_rxrpc.c Name:rxrpc_rpc_ops]
Results: map[SourceCode: 997: /*
998: * RxRPC network protocol
999: */
1000: static const struct proto_ops rxrpc_rpc_ops = {
1001: .family = PF_RXRPC,
1002: .owner = THIS_MODULE,
1003: .release = rxrpc_release,
1004: .bind = rxrpc_bind,
1005: .connect = rxrpc_connect,
1006: .socketpair = sock_no_socketpair,
1007: .accept = sock_no_accept,
1008: .getname = sock_no_getname,
1009: .poll = rxrpc_poll,
1010: .ioctl = sock_no_ioctl,
1011: .listen = rxrpc_listen,
1012: .shutdown = rxrpc_shutdown,
1013: .setsockopt = rxrpc_setsockopt,
1014: .getsockopt_iter = rxrpc_getsockopt,
1015: .sendmsg = rxrpc_sendmsg,
1016: .recvmsg = rxrpc_recvmsg,
1017: .mmap = sock_no_mmap,
1018: };
SourceFile:net/rxrpc/af_rxrpc.c]
|
| 34/2 |
2026/08/02 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 23175
output: 52
thoughts: 149
Thoughts: **Vulnerability Discovered in RXRPC Socket Handling**
Okay, so here's what I've found digging into this RXRPC code. The `rxrpc_create` function, surprisingly, doesn't seem to have any privilege checks whatsoever. That means *anyone*, even an unprivileged user, can just go ahead and create an `AF_RXRPC` socket. That's a red flag right off the bat.
The root of the issue, however, appears to be lurking within `rxrpc_put_connection` and the related `rxrpc_connection_timer`. The question is, can an unprivileged user actually *trigger* this vulnerability? Let's break it down.
Thinking it through, yes, absolutely. An unprivileged user creates the socket, sends a bit of data to establish a connection. Then, they either close the socket (which is likely) or just let the connection time out. That's where the problem arises: closing the socket or the timeout causes a call to `rxrpc_put_connection`, and the `rxrpc_connection_timer` might be running concurrently! That timing is going to be the kicker.
The timer itself is set up in `rxrpc_alloc_connection`, right? That's the function that starts the whole process. Now I need to nail down *when* that timer is armed exactly. Where's the call to `mod_timer`? I need to check the exact circumstances. That's where the next part of my investigation needs to be focused. This looks like a race condition, but I need to confirm the exact circumstances of the timing.
|
| 35/2 |
2026/08/02 01:19 |
tool |
codesearch-find-references |
0m
Error:
Args: map[ContextFile:net/rxrpc/conn_object.c IncludeSnippetLines:2 Name:timer SourceTreePrefix:net/rxrpc]
Results: map[References:<nil> TruncatedOutput:false]
|
| 36/2 |
2026/08/02 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 23739
output: 30
thoughts: 22
Thoughts: Okay, here's my interpretation of that thought process, as if I'm the one thinking it, tailored for someone who knows their way around these concepts:
**Digging into that `conn->timer`**
Alright, so I need to understand what's happening with that `conn->timer` structure. Time to get down to brass tacks. My initial instinct is to track down the relevant code that interacts with it. Where are we setting or modifying this timer? I'm going to jump right into a search. Let's start with a broad approach and try keywords that are frequently associated with timer manipulation: I'll start with `timer_reduce`, knowing that might be a custom function or macro, and then `mod_timer` because that's a standard kernel function for modifying existing timers. Hopefully, this search will lead me directly to the heart of the matter and quickly show me where this `conn->timer` variable is being used. I expect to be able to quickly understand the core function with these key terms.
|
| 37/2 |
2026/08/02 01:19 |
tool |
grepper |
0m
Args: map[Expression:(timer_reduce|mod_timer|timer_setup).*conn->timer]
Results: map[Output:drivers/net/wireless/ath/ath6kl/txrx.c=1105=static bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid,
--
drivers/net/wireless/ath/ath6kl/txrx.c-1220- agg_conn->timer_scheduled = true;
drivers/net/wireless/ath/ath6kl/txrx.c:1221: mod_timer(&agg_conn->timer,
drivers/net/wireless/ath/ath6kl/txrx.c-1222- (jiffies + (HZ * AGGR_RX_TIMEOUT) / 1000));
--
drivers/net/wireless/ath/ath6kl/txrx.c=1623=static void aggr_timeout(struct timer_list *t)
--
drivers/net/wireless/ath/ath6kl/txrx.c-1668- if (aggr_conn->timer_scheduled)
drivers/net/wireless/ath/ath6kl/txrx.c:1669: mod_timer(&aggr_conn->timer,
drivers/net/wireless/ath/ath6kl/txrx.c-1670- jiffies + msecs_to_jiffies(AGGR_RX_TIMEOUT));
--
drivers/net/wireless/ath/ath6kl/txrx.c=1749=void aggr_conn_init(struct ath6kl_vif *vif, struct aggr_info *aggr_info,
--
drivers/net/wireless/ath/ath6kl/txrx.c-1756- aggr_conn->dev = vif->ndev;
drivers/net/wireless/ath/ath6kl/txrx.c:1757: timer_setup(&aggr_conn->timer, aggr_timeout, 0);
drivers/net/wireless/ath/ath6kl/txrx.c-1758- aggr_conn->aggr_info = aggr_info;
--
net/rxrpc/ar-internal.h=1226=static inline void rxrpc_reduce_conn_timer(struct rxrpc_connection *conn,
--
net/rxrpc/ar-internal.h-1228-{
net/rxrpc/ar-internal.h:1229: timer_reduce(&conn->timer, expire_at);
net/rxrpc/ar-internal.h-1230-}
--
net/rxrpc/conn_object.c=55=struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *rxnet,
--
net/rxrpc/conn_object.c-64- INIT_LIST_HEAD(&conn->cache_link);
net/rxrpc/conn_object.c:65: timer_setup(&conn->timer, &rxrpc_connection_timer, 0);
net/rxrpc/conn_object.c-66- INIT_WORK(&conn->processor, rxrpc_process_connection);
]
|
| 38/2 |
2026/08/02 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 24757
output: 59
thoughts: 22
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing `rxrpc_reduce_conn_timer`**
Alright, let's dive into this `rxrpc_reduce_conn_timer` function. I need to understand its role in the bigger picture, so my initial instinct is to track down its call sites. Knowing where it's used is crucial; itβs the only way to figure out the context in which this timer is operating and the effects itβs meant to produce. I'll need to use my usual tools to pinpoint all of its callers β hopefully, they'll give me clues about the concurrency issues or timing dependencies I should anticipate. I need to quickly identify the points where this timer is started, stopped, or potentially modified. This will immediately begin to illuminate the intent of the author, and any potential points of interest.
|
| 39/2 |
2026/08/02 01:19 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/rxrpc/ar-internal.h IncludeSnippetLines:2 Name:rxrpc_reduce_conn_timer SourceTreePrefix:net/rxrpc]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_disconnect_client_call SourceFile:net/rxrpc/conn_client.c SourceLine:648 SourceSnippet: 646: smp_wmb(); /* vs rxrpc_process_delayed_final_acks() */
647: set_bit(RXRPC_CONN_FINAL_ACK_0 + channel, &conn->flags);
648: rxrpc_reduce_conn_timer(conn, final_ack_at);
649: }
650:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_process_delayed_final_acks SourceFile:net/rxrpc/conn_event.c SourceLine:378 SourceSnippet: 376: goto again;
377: if (set)
378: rxrpc_reduce_conn_timer(conn, next_j);
379: }
]] TruncatedOutput:false]
|
| 40/2 |
2026/08/02 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 25314
output: 42
thoughts: 808
Thoughts: **Vulnerability Analysis: RXRPC Connection Cleanup Race Condition**
Okay, here's what's running through my head. `rxrpc_reduce_conn_timer` kicks off when a client call disconnects, essentially setting up a timer to send a final ACK. That timer is armed, and then the connection itself is put away. Critically, if the connection's refcount drops to zero during this process, `rxrpc_put_connection` is called immediately.
The problem? A potential race condition between the `rxrpc_connection_timer` callback and `rxrpc_put_connection`. Specifically, `rxrpc_put_connection` calls `timer_delete(&conn->timer)` without waiting for the timer callback. So, if the timer *also* fires around the same time, here's what could happen: the timer callback might run concurrently with `rxrpc_put_connection`. The callback bumps the refcount from zero to one, and adds the connection to `conn_attend_q`. But, `rxrpc_put_connection` proceeds regardless, potentially scheduling a cleanup via `rxrpc_clean_up_connection`.
Now, `rxrpc_clean_up_connection` *does* call `timer_delete_sync`. It's meant to ensure the timer callback is finished before proceeding. However, the timer callback already *did* its thing - it incremented the refcount, added the connection to the queue and may already be done. Then, `rxrpc_clean_up_connection` continues, ultimately freeing the connection using RCU.
Here's where it goes south. The I/O thread, which processes `conn_attend_q`, *later* accesses this freed connection, potentially triggering a use-after-free, and a subsequent double free because the I/O thread itself calls `rxrpc_put_connection`. The fact that the timer callback has already increased the refcount and moved the connection on the queue is the core problem.
This is a pretty classic use-after-free and double-free vulnerability, and exploitable because an attacker has some control over the timing. They could trigger this by creating a client call, sending some data, then intentionally closing the socket, which would disconnect the client call, arm the timer and lead to this race condition. It's concerning that this could be triggered by an unprivileged user.
Okay, let me double check the context `rxrpc_put_connection` runs in. If it's *not* in a softirq, and `work_busy` is false, it's problematic. If it *is* in a softirq, it schedules a destructor. The destructor then calls `timer_delete_sync`. As discussed, this waits for the timer and then cleans up. The problem is that the timer callback could have added the entry to `conn_attend_q` before the clean up. When the I/O thread later processes the queue, it's hitting a use-after-free.
I need to confirm the I/O thread is the one that processes `conn_attend_q`... Yep, `rxrpc_io_thread` is the culprit. Is it running concurrently with the destructor? Yes, that makes sense - it's a separate kernel thread, and the destructor is in a workqueue, so they *can* collide. The I/O thread, after accessing the freed memory, calls `rxrpc_put_connection` which would reduce the refcount, call the same cleanup again and lead to the double free.
Okay, back to `rxrpc_clean_up_connection`β¦ Yes, the problem is clear, `timer_delete_sync` does not protect the queue entry.
|
| 41/2 |
2026/08/02 01:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rxrpc/conn_object.c Name:rxrpc_clean_up_connection]
Results: map[SourceCode: 303: /*
304: * Clean up a dead connection.
305: */
306: static void rxrpc_clean_up_connection(struct work_struct *work)
307: {
308: struct rxrpc_connection *conn =
309: container_of(work, struct rxrpc_connection, destructor);
310: struct rxrpc_net *rxnet = conn->rxnet;
311:
312: ASSERT(!conn->channels[0].call &&
313: !conn->channels[1].call &&
314: !conn->channels[2].call &&
315: !conn->channels[3].call);
316: ASSERT(list_empty(&conn->cache_link));
317:
318: timer_delete_sync(&conn->timer);
319: cancel_work_sync(&conn->processor); /* Processing may restart the timer */
320: timer_delete_sync(&conn->timer);
321:
322: write_lock(&rxnet->conn_lock);
323: list_del_init(&conn->proc_link);
324: write_unlock(&rxnet->conn_lock);
325:
326: if (conn->pmtud_probe) {
327: trace_rxrpc_pmtud_lost(conn, 0);
328: conn->peer->pmtud_probing = false;
329: conn->peer->pmtud_pending = true;
330: }
331:
332: rxrpc_purge_queue(&conn->rx_queue);
333: rxrpc_free_skb(conn->tx_response, rxrpc_skb_put_response);
334:
335: rxrpc_kill_client_conn(conn);
336:
337: conn->security->clear(conn);
338: key_put(conn->key);
339: rxrpc_put_bundle(conn->bundle, rxrpc_bundle_put_conn);
340: rxrpc_put_peer(conn->peer, rxrpc_peer_put_conn);
341: rxrpc_put_local(conn->local, rxrpc_local_put_kill_conn);
342:
343: /* Drain the Rx queue. Note that even though we've unpublished, an
344: * incoming packet could still be being added to our Rx queue, so we
345: * will need to drain it again in the RCU cleanup handler.
346: */
347: rxrpc_purge_queue(&conn->rx_queue);
348:
349: page_frag_cache_drain(&conn->tx_data_alloc);
350: call_rcu(&conn->rcu, rxrpc_rcu_free_connection);
351: }
SourceFile:net/rxrpc/conn_object.c]
|
| 42/2 |
2026/08/02 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 27790
output: 59
thoughts: 639
Thoughts: **Analyzing a Potential Use-After-Free Vulnerability in rxrpc**
Okay, so I've been digging into this `rxrpc_clean_up_connection` function, and it's starting to look like there's a serious problem here. My initial concern stems from the order of operations: `rxrpc_clean_up_connection` removes the connection (`conn`) from `rxnet->conn_proc_list` and then immediately calls `call_rcu(&conn->rcu, rxrpc_rcu_free_connection)`. The RCU mechanism means the memory isn't freed immediately; that much I know.
Here's where the alarm bells are really going off: The I/O thread, in its `conn_attend_q` processing loop, *doesn't* appear to be running within an RCU read-side critical section. It simply pulls the connection from a list (`conn_attend_q`) and processes it. Crucially, the code doesn't use `rcu_read_lock` or `rcu_read_unlock` here. It seems that the `conn_attend_q` is a per-cpu queue that can be accessed by the I/O thread.
Now, if the `rxrpc_rcu_free_connection` function has already run and freed the memory, and the I/O thread accesses `conn` after the RCU grace period, then we're talking about a classic use-after-free scenario. Even if the I/O thread accesses the memory *before* the grace period ends, it still calls `rxrpc_put_connection`. The first call to `rxrpc_clean_up_connection` causes the connection memory to be placed on the RCU list and scheduled for freeing and dropping the refcount to 0. The second call to `rxrpc_clean_up_connection`, from inside `rxrpc_put_connection`, could cause a double-free on crucial data structures like `conn->tx_response`, `conn->bundle`, `conn->peer`, and `conn->local`. Plus, it will result in calling `call_rcu` twice, corrupting the RCU callback list. That's a textbook exploitation vector. This could lead to a very exploitable situation.
The big question now is, can this be triggered remotely? I'm thinking, can an attacker abuse this? I can imagine an attacker creating and destroying a connection to trigger this. The rxrpc implementation supports connections being created and destroyed as part of the normal operation. If I can send packets to the AF_RXRPC socket to create a connection, that's step one. A server socket will, by its nature, establish a connection when it receives a packet. If the connection can be aborted, that might trigger the connection destruction, and potentially the bug. I am curious about the `rxrpc_connection_timer`, when the timer gets armed for server connections. I think it is triggered when an abort is received. That seems like the right path to exploit this.
|
| 43/2 |
2026/08/02 01:19 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/rxrpc/conn_object.c IncludeSnippetLines:2 Name:rxrpc_reduce_conn_timer SourceTreePrefix:net/rxrpc]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_disconnect_client_call SourceFile:net/rxrpc/conn_client.c SourceLine:648 SourceSnippet: 646: smp_wmb(); /* vs rxrpc_process_delayed_final_acks() */
647: set_bit(RXRPC_CONN_FINAL_ACK_0 + channel, &conn->flags);
648: rxrpc_reduce_conn_timer(conn, final_ack_at);
649: }
650:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_process_delayed_final_acks SourceFile:net/rxrpc/conn_event.c SourceLine:378 SourceSnippet: 376: goto again;
377: if (set)
378: rxrpc_reduce_conn_timer(conn, next_j);
379: }
]] TruncatedOutput:false]
|
| 44/2 |
2026/08/02 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 29423
output: 44
thoughts: 81
Thoughts: Here's my summary, as though I'm thinking through this process:
**Diving into RxRPC Connection Management**
Okay, so I'm digging into how RxRPC handles connection timeouts. I see that `rxrpc_reduce_conn_timer` is exclusively called from two functions: `rxrpc_disconnect_client_call` and `rxrpc_process_delayed_final_acks`. That's interesting; they both seem to be deeply connected with the client-side of the connection, particularly when they are sending final acknowledgements.
Now, wait a second... `rxrpc_process_delayed_final_acks`... isn't that function defined in `conn_event.c`? That would make sense, since event management often involves handling timers and the like. Right, let's head over to that file and see what's happening there to understand how delayed ACKs and connection timers interact.
|
| 45/2 |
2026/08/02 01:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rxrpc/conn_event.c Name:rxrpc_process_delayed_final_acks]
Results: map[SourceCode: 341: /*
342: * Process delayed final ACKs that we haven't subsumed into a subsequent call.
343: */
344: void rxrpc_process_delayed_final_acks(struct rxrpc_connection *conn, bool force)
345: {
346: unsigned long j = jiffies, next_j;
347: unsigned int channel;
348: bool set;
349:
350: again:
351: next_j = j + LONG_MAX;
352: set = false;
353: for (channel = 0; channel < RXRPC_MAXCALLS; channel++) {
354: struct rxrpc_channel *chan = &conn->channels[channel];
355: unsigned long ack_at;
356:
357: if (!test_bit(RXRPC_CONN_FINAL_ACK_0 + channel, &conn->flags))
358: continue;
359:
360: ack_at = chan->final_ack_at;
361: if (time_before(j, ack_at) && !force) {
362: if (time_before(ack_at, next_j)) {
363: next_j = ack_at;
364: set = true;
365: }
366: continue;
367: }
368:
369: if (test_and_clear_bit(RXRPC_CONN_FINAL_ACK_0 + channel,
370: &conn->flags))
371: rxrpc_conn_retransmit_call(conn, NULL, channel);
372: }
373:
374: j = jiffies;
375: if (time_before_eq(next_j, j))
376: goto again;
377: if (set)
378: rxrpc_reduce_conn_timer(conn, next_j);
379: }
SourceFile:net/rxrpc/conn_event.c]
|
| 46/2 |
2026/08/02 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 30347
output: 26
thoughts: 38
Thoughts: Okay, here's my take on those thoughts, framed from my perspective:
**Connecting the Dots in Client Connection Management**
Alright, this code section is clearly tied to how we handle client connections. The immediate question popping into my head is whether that `rxrpc_connection_timer` function is *exclusively* used for client-side connections. It feels like it *should* be, but I need to be absolutely certain.
The best way to solidify that understanding, and to be absolutely sure of the logic's boundaries, is to track down all the places where `conn->timer` gets modified. That'll show me the complete picture of how the connection timer is used across the codebase and confirm whether its use is solely client focused. We need to be able to fully understand the life cycle of the timer. This is essential for a robust and reliable system.
|
| 47/2 |
2026/08/02 01:19 |
tool |
grepper |
0m
Args: map[Expression:(mod_timer|timer_reduce).*conn->timer]
Results: map[Output:drivers/net/wireless/ath/ath6kl/txrx.c=1105=static bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid,
--
drivers/net/wireless/ath/ath6kl/txrx.c-1220- agg_conn->timer_scheduled = true;
drivers/net/wireless/ath/ath6kl/txrx.c:1221: mod_timer(&agg_conn->timer,
drivers/net/wireless/ath/ath6kl/txrx.c-1222- (jiffies + (HZ * AGGR_RX_TIMEOUT) / 1000));
--
drivers/net/wireless/ath/ath6kl/txrx.c=1623=static void aggr_timeout(struct timer_list *t)
--
drivers/net/wireless/ath/ath6kl/txrx.c-1668- if (aggr_conn->timer_scheduled)
drivers/net/wireless/ath/ath6kl/txrx.c:1669: mod_timer(&aggr_conn->timer,
drivers/net/wireless/ath/ath6kl/txrx.c-1670- jiffies + msecs_to_jiffies(AGGR_RX_TIMEOUT));
--
net/rxrpc/ar-internal.h=1226=static inline void rxrpc_reduce_conn_timer(struct rxrpc_connection *conn,
--
net/rxrpc/ar-internal.h-1228-{
net/rxrpc/ar-internal.h:1229: timer_reduce(&conn->timer, expire_at);
net/rxrpc/ar-internal.h-1230-}
]
|
| 48/2 |
2026/08/02 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 27021
output: 57
thoughts: 0
|
| 49/2 |
2026/08/02 01:20 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/rxrpc/conn_object.c IncludeSnippetLines:2 Name:rxrpc_put_connection SourceTreePrefix:net/rxrpc]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_add_conn_to_bundle SourceFile:net/rxrpc/conn_client.c SourceLine:350 SourceSnippet: 348: bundle->conn_ids[slot] = 0;
349: trace_rxrpc_client(old, -1, rxrpc_client_replace);
350: rxrpc_put_connection(old, rxrpc_conn_put_noreuse);
351: }
352:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_clean_up_local_conns SourceFile:net/rxrpc/conn_client.c SourceLine:829 SourceSnippet: 827: trace_rxrpc_client(conn, -1, rxrpc_client_discard);
828: rxrpc_unbundle_conn(conn);
829: rxrpc_put_connection(conn, rxrpc_conn_put_local_dead);
830: }
831:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_destroy_call SourceFile:net/rxrpc/call_object.c SourceLine:694 SourceSnippet: 692: rxrpc_cleanup_rx_buffers(call);
693: rxrpc_put_txbuf(call->tx_pending, rxrpc_txbuf_put_cleaned);
694: rxrpc_put_connection(call->conn, rxrpc_conn_put_call);
695: rxrpc_deactivate_bundle(call->bundle);
696: rxrpc_put_bundle(call->bundle, rxrpc_bundle_put_call);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_discard_expired_client_conns SourceFile:net/rxrpc/conn_client.c SourceLine:790 SourceSnippet: 788: rxrpc_unbundle_conn(conn);
789: /* Drop the ->cache_link ref */
790: rxrpc_put_connection(conn, rxrpc_conn_put_discard_idle);
791:
792: nr_conns--;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_input_packet SourceFile:net/rxrpc/io_thread.c SourceLine:291 SourceSnippet: 289:
290: ret = rxrpc_input_packet_on_conn(conn, &peer_srx, skb);
291: rxrpc_put_connection(conn, rxrpc_conn_put_call_input);
292: return ret;
293: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_input_packet SourceFile:net/rxrpc/io_thread.c SourceLine:312 SourceSnippet: 310: rcu_read_unlock();
311: ret = rxrpc_input_packet_on_conn(conn, &peer_srx, skb);
312: rxrpc_put_connection(conn, rxrpc_conn_put_call_input);
313: return ret;
314: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_io_thread SourceFile:net/rxrpc/io_thread.c SourceLine:488 SourceSnippet: 486: case RXRPC_SKB_MARK_SERVICE_CONN_SECURED:
487: rxrpc_input_conn_event(sp->poke_conn, skb);
488: rxrpc_put_connection(sp->poke_conn, rxrpc_conn_put_poke);
489: rxrpc_free_skb(skb, rxrpc_skb_put_conn_secured);
490: break;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_io_thread SourceFile:net/rxrpc/io_thread.c SourceLine:512 SourceSnippet: 510: spin_unlock_irq(&local->lock);
511: rxrpc_input_conn_event(conn, NULL);
512: rxrpc_put_connection(conn, rxrpc_conn_put_poke);
513: }
514:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_kernel_free_oob SourceFile:net/rxrpc/oob.c SourceLine:332 SourceSnippet: 330: switch (oob->mark) {
331: case RXRPC_OOB_CHALLENGE:
332: rxrpc_put_connection(sp->chall.conn, rxrpc_conn_put_oob);
333: break;
334: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_post_challenge SourceFile:net/rxrpc/conn_event.c SourceLine:469 SourceSnippet: 467: if (!respond) {
468: rcu_read_unlock();
469: rxrpc_put_connection(conn, rxrpc_conn_put_challenge_input);
470: sp->chall.conn = NULL;
471: return false;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_post_challenge SourceFile:net/rxrpc/conn_event.c SourceLine:478 SourceSnippet: 476: rcu_read_unlock();
477: if (call && !queued) {
478: rxrpc_put_connection(conn, rxrpc_conn_put_challenge_input);
479: sp->chall.conn = NULL;
480: return false;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_process_event SourceFile:net/rxrpc/conn_event.c SourceLine:284 SourceSnippet: 282: ret = conn->security->respond_to_challenge(conn, skb);
283: sp->chall.conn = NULL;
284: rxrpc_put_connection(conn, rxrpc_conn_put_challenge_input);
285: return ret;
286:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_respond_to_oob SourceFile:net/rxrpc/oob.c SourceLine:218 SourceSnippet: 216: switch (skb->mark) {
217: case RXRPC_OOB_CHALLENGE:
218: rxrpc_put_connection(sp->chall.conn, rxrpc_conn_put_oob);
219: break;
220: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_service_connection_reaper SourceFile:net/rxrpc/conn_object.c SourceLine:456 SourceSnippet: 454:
455: ASSERTCMP(atomic_read(&conn->active), ==, -1);
456: rxrpc_put_connection(conn, rxrpc_conn_put_service_reaped);
457: }
458:
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_unbundle_conn SourceFile:net/rxrpc/conn_client.c SourceLine:694 SourceSnippet: 692: rxrpc_put_client_connection_id(bundle->local, conn);
693: rxrpc_deactivate_bundle(bundle);
694: rxrpc_put_connection(conn, rxrpc_conn_put_unbundle);
695: }
696: }
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rxrpc_unidle_conn SourceFile:net/rxrpc/conn_client.c SourceLine:463 SourceSnippet: 461: if (!list_empty(&conn->cache_link)) {
462: list_del_init(&conn->cache_link);
463: rxrpc_put_connection(conn, rxrpc_conn_put_unidle);
464: }
465: }
]] TruncatedOutput:false]
|
| 50/2 |
2026/08/02 01:20 |
llm |
expert |
0m
Model:
Tokens:
input: 33456
output: 26
thoughts: 122
Thoughts: **Analyzing the `rxrpc_connection_timer` Usage**
Okay, so the initial question is: is this `rxrpc_connection_timer` function actually used for *both* client and server connections? My gut feeling is that it's going to be universal given the naming scheme, but I need to confirm. The next thing that jumps out is that `rxrpc_alloc_connection` seems to initialize the timer for *every* connection, regardless of whether it's client or server. That feels like a solid foundation for the timer's role.
Now, the crucial follow-up question is: when and *how* is this timer actually activated for server connections? I need to track down the calls to `rxrpc_reduce_conn_timer` or `mod_timer` that specifically involve `conn->timer`. That's where the rubber meets the road.
I know, from previous debugging, that `rxrpc_reduce_conn_timer` gets called within `rxrpc_disconnect_client_call` and `rxrpc_process_delayed_final_acks`. That's a good starting point, showing a few possible use cases. But are these the only places the timer is manipulated? I need to conduct a thorough search to get the whole picture. I need to be sure. I'll need to go through the code with a fine-toothed comb to be absolutely certain.
|
| 51/2 |
2026/08/02 01:20 |
tool |
grepper |
0m
Args: map[Expression:(mod_timer|timer_reduce).*conn->timer]
Results: map[Output:drivers/net/wireless/ath/ath6kl/txrx.c=1105=static bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid,
--
drivers/net/wireless/ath/ath6kl/txrx.c-1220- agg_conn->timer_scheduled = true;
drivers/net/wireless/ath/ath6kl/txrx.c:1221: mod_timer(&agg_conn->timer,
drivers/net/wireless/ath/ath6kl/txrx.c-1222- (jiffies + (HZ * AGGR_RX_TIMEOUT) / 1000));
--
drivers/net/wireless/ath/ath6kl/txrx.c=1623=static void aggr_timeout(struct timer_list *t)
--
drivers/net/wireless/ath/ath6kl/txrx.c-1668- if (aggr_conn->timer_scheduled)
drivers/net/wireless/ath/ath6kl/txrx.c:1669: mod_timer(&aggr_conn->timer,
drivers/net/wireless/ath/ath6kl/txrx.c-1670- jiffies + msecs_to_jiffies(AGGR_RX_TIMEOUT));
--
net/rxrpc/ar-internal.h=1226=static inline void rxrpc_reduce_conn_timer(struct rxrpc_connection *conn,
--
net/rxrpc/ar-internal.h-1228-{
net/rxrpc/ar-internal.h:1229: timer_reduce(&conn->timer, expire_at);
net/rxrpc/ar-internal.h-1230-}
]
|
| 52/2 |
2026/08/02 01:20 |
llm |
expert |
0m
Model:
Tokens:
input: 34341
output: 37
thoughts: 1186
Thoughts: **Analysis of the RXRPC Connection Timer Bug**
Okay, so here's the breakdown of this RXRPC bug, as I see it. It looks like `rxrpc_reduce_conn_timer` is the culprit. I know that function is only modifying the `conn->timer` and is called exclusively within `rxrpc_disconnect_client_call` and `rxrpc_process_delayed_final_acks`. Those functions are only for client connections, which are initiated locally. So, a remote attacker can't directly trigger this. Thank goodness for small mercies.
My next thought is, can an unprivileged local user trigger this? Yes, they absolutely can, since they create client connections by calling `connect()` or `sendmsg()` on an `AF_RXRPC` socket. This is a crucial detail. I think this thing is exploitable since it's a use-after-free and double-free scenario. This could lead to either a kernel crash or memory corruption, which is always a bad day.
So, let me summarize my findings for my own sanity:
* **Bug:** Use-after-free and double-free in `rxrpc_put_connection` and `rxrpc_connection_timer`.
* **Trigger:** An unprivileged local user can create an `AF_RXRPC` socket, initiate a client connection, and then trigger a race condition between the connection timer and the connection destruction.
* **Exploitability:** High. This use-after-free and double-free means this could be bad news.
* **Remote Trigger:** No, it's not possible since the timer is only armed for locally initiated client connections.
* **Unprivileged:** Yes, `AF_RXRPC` sockets don't require any special permissions to create.
* **User Namespace:** Possible, but not required since it is accessible from the initial namespace.
* **VM Guest/Host:** Not virtualization-specific.
* **Network Trigger:** No, it's triggered via socket API locally.
Now, let me think about how an unprivileged user can have this socket. Will the `af_rxrpc` module auto-load when an unprivileged user creates an `AF_RXRPC` socket? Yes, calling `socket(AF_RXRPC, ...)` will trigger module auto-loading, assuming `CONFIG_AF_RXRPC` is built as a module. Is that common? The kernel configuration is often enabled and it could be loaded. But even if it isn't auto-loaded, if it is loaded, any unprivileged user can trigger it.
Alright, let me make sure the root cause is `timer_delete` in `rxrpc_put_connection`. Yes, it's a non-blocking `timer_delete`, so that is the issue. If the timer callback is running, it will increment the refcount from 0 to 1, and add the connection to `conn_attend_q`. Then `rxrpc_put_connection` calls `rxrpc_clean_up_connection`. `rxrpc_clean_up_connection` calls `timer_delete_sync`, which waits for the timer callback to finish. This seems fine, but the timer callback has already resurrected the connection by incrementing the refcount and adding it to `conn_attend_q`, but `rxrpc_clean_up_connection` still frees the connection. This is our problem. Later, the I/O thread processes `conn_attend_q`, accesses the freed connection, and calls `rxrpc_put_connection`. The refcount goes to 0, which calls `rxrpc_clean_up_connection` again. Boom, that's the classic UAF and double-free.
Wait a second. What if `rxrpc_clean_up_connection` is called directly from `rxrpc_put_connection`?
```c
if (dead) {
timer_delete(&conn->timer);
cancel_work(&conn->processor);
if (in_softirq() || work_busy(&conn->processor) ||
timer_pending(&conn->timer))
/* Can't use the rxrpc workqueue as we need to cancel/flush
* something that may be running/waiting there.
*/
schedule_work(&conn->destructor);
else
rxrpc_clean_up_connection(&conn->destructor);
}
```
If `timer_pending` returns false (the timer callback is running and has been dequeued) and `in_softirq()` is false, and `work_busy` is also false, it calls `rxrpc_clean_up_connection` directly. Since `rxrpc_clean_up
|
| 53/2 |
2026/08/02 01:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/refcount.h Name:__refcount_inc]
Results: map[SourceCode: 364: static inline void __refcount_inc(refcount_t *r, int *oldp)
365: {
366: __refcount_add(1, r, oldp);
367: }
SourceFile:include/linux/refcount.h]
|
| 54/2 |
2026/08/02 01:20 |
llm |
expert |
0m
Model:
Tokens:
input: 36684
output: 37
thoughts: 0
|
| 55/2 |
2026/08/02 01:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/refcount.h Name:__refcount_add]
Results: map[SourceCode: 280: static inline
281: void __refcount_add(int i, refcount_t *r, int *oldp)
282: {
283: int old = atomic_fetch_add_relaxed(i, &r->refs);
284:
285: if (oldp)
286: *oldp = old;
287:
288: if (unlikely(!old))
289: refcount_warn_saturate(r, REFCOUNT_ADD_UAF);
290: else if (unlikely(old < 0 || old + i < 0))
291: refcount_warn_saturate(r, REFCOUNT_ADD_OVF);
292: }
SourceFile:include/linux/refcount.h]
|
| 56/2 |
2026/08/02 01:20 |
llm |
expert |
0m
Model:
Tokens:
input: 36961
output: 38
thoughts: 52
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing `refcount_warn_saturate`**
Right, so `refcount_warn_saturate`β¦ thatβs going to clamp the reference count to `REFCOUNT_SATURATED`. Okay, and if I recall correctly, `REFCOUNT_SATURATED` is defined as `INT_MIN / 2`. Interesting choice. The division by 2 is likely there to avoid immediate overflow in certain operations, though I'd need to review the exact context to be certain of the reasoning behind this design choice. Let's delve into the implementation of `refcount_warn_saturate` itself and understand what's triggering that particular saturation point.
|
| 57/2 |
2026/08/02 01:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:lib/refcount.c Name:refcount_warn_saturate]
Results: map[SourceCode: 13: void refcount_warn_saturate(refcount_t *r, enum refcount_saturation_type t)
14: {
15: refcount_set(r, REFCOUNT_SATURATED);
16:
17: switch (t) {
18: case REFCOUNT_ADD_NOT_ZERO_OVF:
19: REFCOUNT_WARN("saturated; leaking memory");
20: break;
21: case REFCOUNT_ADD_OVF:
22: REFCOUNT_WARN("saturated; leaking memory");
23: break;
24: case REFCOUNT_ADD_UAF:
25: REFCOUNT_WARN("addition on 0; use-after-free");
26: break;
27: case REFCOUNT_SUB_UAF:
28: REFCOUNT_WARN("underflow; use-after-free");
29: break;
30: case REFCOUNT_DEC_LEAK:
31: REFCOUNT_WARN("decrement hit 0; leaking memory");
32: break;
33: default:
34: REFCOUNT_WARN("unknown saturation event!?");
35: }
36: }
SourceFile:lib/refcount.c]
|
| 58/2 |
2026/08/02 01:20 |
llm |
expert |
0m
Model:
Tokens:
input: 37568
output: 41
thoughts: 59
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in the weeds of kernel-level networking code:
**Diving into Refcounting Behavior**
Right, so the refcount has hit `REFCOUNT_SATURATED`. That's the signal, isn't it? Time for the next stage. Now, the I/O thread, when it's handling a connection and calling `rxrpc_put_connection`, *that's* the trigger. `rxrpc_put_connection` will be the one calling `__refcount_dec_and_test`. That's where the magic happens. I need to be absolutely sure how `__refcount_dec_and_test` behaves in this context. It's the critical path, so I need to check the code there now. I need to understand what this decrement and test does. I'm going to look at that function definition directly.
|
| 59/2 |
2026/08/02 01:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/refcount.h Name:__refcount_dec_and_test]
Results: map[SourceCode: 430: static inline __must_check bool __refcount_dec_and_test(refcount_t *r, int *oldp)
431: {
432: return __refcount_sub_and_test(1, r, oldp);
433: }
SourceFile:include/linux/refcount.h]
|
| 60/2 |
2026/08/02 01:20 |
llm |
expert |
0m
Model:
Tokens:
input: 37975
output: 41
thoughts: 0
|
| 61/2 |
2026/08/02 01:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/refcount.h Name:__refcount_sub_and_test]
Results: map[SourceCode: 386: static inline __must_check
387: bool __refcount_sub_and_test(int i, refcount_t *r, int *oldp)
388: {
389: int old = atomic_fetch_sub_release(i, &r->refs);
390:
391: if (oldp)
392: *oldp = old;
393:
394: if (old > 0 && old == i) {
395: smp_acquire__after_ctrl_dep();
396: return true;
397: }
398:
399: if (unlikely(old <= 0 || old - i < 0))
400: refcount_warn_saturate(r, REFCOUNT_SUB_UAF);
401:
402: return false;
403: }
SourceFile:include/linux/refcount.h]
|
| 62/2 |
2026/08/02 01:20 |
llm |
expert |
0m
Model:
Tokens:
input: 38302
output: 42
thoughts: 884
Thoughts: **Analyzing the Potential Use-After-Free Vulnerability**
Okay, let's break this down step-by-step. I'm focusing on a refcount saturation scenario here. If the refcount hits `REFCOUNT_SATURATED`, which is negative, the subtraction logic in `__refcount_sub_and_test` will correctly return `false` because the comparison `old == i` will fail, and it will trigger a warning. This prevents a potential double free, which is good.
Now, tracing the execution with the refcount getting to zero, the connection's timer is deleted, and `rxrpc_clean_up_connection` is called. The timer callback, however, runs concurrently on another CPU and calls `rxrpc_get_connection`, which increments the refcount. This increment will bump the refcount to saturation, and the kernel will throw a warning, which is what we expect. At the same time, the timer callback adds the connection to `conn_attend_q`.
`rxrpc_clean_up_connection` then calls `timer_delete_sync` and eventually frees the connection through RCU. Then the I/O thread comes along, processes `conn_attend_q`, and accesses the freed connection. The call to `rxrpc_put_connection` will then correctly see the saturated refcount and prevent a double free, but it's *after* the I/O thread has accessed the memory.
Crucially, because `rxrpc_clean_up_connection` uses RCU, there's a window where the I/O thread *could* access the connection *before* the RCU grace period. This is where the problem lies. The call to `list_del_init` within the while loop will modify `conn->attend_link.next` and `conn->attend_link.prev`, which is a write to freed memory. On top of that, `rxrpc_input_conn_event(conn, NULL)` is called, which will read and write to the freed connection.
This is a classic use-after-free, plain and simple, and I need to verify exactly what this function does. I need to dive into `rxrpc_input_conn_event` to verify it accesses `conn` and identify the specific fields, because that makes it exploitable. This is critical.
|
| 63/2 |
2026/08/02 01:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rxrpc/conn_event.c Name:rxrpc_input_conn_event]
Results: map[SourceCode: 533: /*
534: * Input a connection event.
535: */
536: void rxrpc_input_conn_event(struct rxrpc_connection *conn, struct sk_buff *skb)
537: {
538: unsigned int loop;
539:
540: if (test_and_clear_bit(RXRPC_CONN_EV_ABORT_CALLS, &conn->events))
541: rxrpc_abort_calls(conn);
542:
543: if (conn->tx_response) {
544: struct sk_buff *skb;
545:
546: spin_lock_irq(&conn->local->lock);
547: skb = conn->tx_response;
548: conn->tx_response = NULL;
549: spin_unlock_irq(&conn->local->lock);
550:
551: if (conn->state != RXRPC_CONN_ABORTED)
552: rxrpc_send_response(conn, skb);
553: rxrpc_free_skb(skb, rxrpc_skb_put_response);
554: }
555:
556: if (skb) {
557: switch (skb->mark) {
558: case RXRPC_SKB_MARK_SERVICE_CONN_SECURED:
559: if (conn->state != RXRPC_CONN_SERVICE)
560: break;
561:
562: for (loop = 0; loop < RXRPC_MAXCALLS; loop++)
563: rxrpc_call_is_secure(conn->channels[loop].call);
564: break;
565: }
566: }
567:
568: /* Process delayed ACKs whose time has come. */
569: if (conn->flags & RXRPC_CONN_FINAL_ACK_MASK)
570: rxrpc_process_delayed_final_acks(conn, false);
571: }
SourceFile:net/rxrpc/conn_event.c]
|
| 64/2 |
2026/08/02 01:20 |
llm |
expert |
0m
Model:
Tokens:
input: 40347
output: 43
thoughts: 2043
Thoughts: **Vulnerability Analysis: AF_RXRPC Connection Use-After-Free**
Okay, so looking at this `rxrpc_input_conn_event` function, it's clear it's reading and writing connection-specific data like `conn->events`, `conn->tx_response`, `conn->state`, and `conn->flags`. Specifically, a use-after-free scenario emerges here. The `conn` structure, whether from a slab cache or `kmalloc`, can be manipulated by an attacker who controls the heap, which allows for potentially hijacking the control flow or arbitrarily corrupting memory. The immediate problem is that `conn->local->lock` will be locked, which allows for the attacker to control the lock on an arbitrary address.
The critical thing is can an unprivileged process trigger this? Yes, any process, without special capabilities, can create an AF_RXRPC socket and initiate a client connection. And user namespaces don't change this as the functionality is available and does not require capabilities.
Let's dig in a bit deeper. Can this be triggered by an incoming packet? No. The code isn't set up to trigger this from server connections, because the timer isn't armed.
It's clear it's a denial of service (DoS) situation, likely kernel panic on a garbage pointer dereference, or if `list_del_init` corrupts the list.
The AF_RXRPC configuration looks relevant and likely enabled on some kernels, or auto-loadable if it's a module. So, we have to assume it's accessible.
Let's summarize the analysis: it's exploitable due to the use-after-free, providing write and read opportunities to the attacker, leading to control flow hijacking or memory corruption. It's also a DoS. It's accessible to unprivileged processes, and works from user namespaces. It requires a local socket operation, so it's not a remote trigger.
But, a further question emerges: does `rxrpc_clean_up_connection` always get called? The destructor runs in a workqueue, but the timer callback runs in softirq context. This introduces a race condition. The timer callback puts `conn` on `conn_attend_q`, and the destructor may execute prior to the I/O thread processing the queue. If so, a memory access happens after a free. Even if the refcount prevents the immediate free, the race still exists. The attacker could potentially win the race by manipulating timing.
The situation gets worse when `rxrpc_clean_up_connection` is called directly, because it can happen during the process context. This will cause the destructor to wait for the timer and then free the connection, and then the I/O thread runs, accessing the freed connection. The problem is very reliable in this case.
The analysis of the call stack shows a likely race, and the code confirms it.
The final question: can the I/O thread be prevented from accessing the freed connection? `rxrpc_clean_up_connection` removes `conn` from `proc_link`, but not `local->conn_attend_q`. This means the connection remains in the I/O thread's queue *after* it's been freed. The I/O thread will grab `local->lock`, remove the connection from the queue, and then access freed memory. This confirms the use-after-free.
|
| 65/2 |
2026/08/02 01:21 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rxrpc/conn_object.c Name:rxrpc_rcu_free_connection]
Results: map[SourceCode: 284: /*
285: * destroy a virtual connection
286: */
287: static void rxrpc_rcu_free_connection(struct rcu_head *rcu)
288: {
289: struct rxrpc_connection *conn =
290: container_of(rcu, struct rxrpc_connection, rcu);
291: struct rxrpc_net *rxnet = conn->rxnet;
292:
293: _enter("{%d,u=%d}", conn->debug_id, refcount_read(&conn->ref));
294:
295: trace_rxrpc_conn(conn->debug_id, refcount_read(&conn->ref),
296: rxrpc_conn_free);
297: kfree(conn);
298:
299: if (atomic_dec_and_test(&rxnet->nr_conns))
300: wake_up_var(&rxnet->nr_conns);
301: }
SourceFile:net/rxrpc/conn_object.c]
|
| 66/2 |
2026/08/02 01:21 |
llm |
expert |
0m
Model:
Tokens:
input: 43477
output: 42
thoughts: 2253
Thoughts: **Exploiting a Use-After-Free in the RXRPC Connection Handling**
Okay, so the `kfree(conn)` call is the crux of the issue. The I/O thread, in its processing, then accesses this freed memory. This is a clear use-after-free scenario. The I/O thread takes `conn`, potentially a dangling pointer, adds it to `conn_attend_q`, and then processes it. This involves traversing a list. If `conn` has been freed and its memory reallocated, the `attend_link` fields (which are part of the list structure) could be overwritten with arbitrary data, leading to a crash during list traversal or, more dangerously, an arbitrary write primitive if an attacker controls the overwritten pointers. Even if list hardening (e.g., `CONFIG_LIST_HARDENED`) is in place, it might be bypassed with crafting `conn->attend_link` correctly, crashing or leading to a denial-of-service, or even leading to control flow hijacking.
The attacker could reallocate the freed memory, setting up a "valid" list node, such that when the I/O thread accesses it, it will pass through list validation and the attacker-controlled `conn` object will be used. Then, `rxrpc_input_conn_event` will be called with the attacker-controlled `conn`. Within that function, there's a check `test_and_clear_bit(RXRPC_CONN_EV_ABORT_CALLS, &conn->events)`. This then calls `rxrpc_abort_calls(conn)` if the bit is set. Critically, there's also an attempt to lock `conn->local->lock` and access `conn->tx_response`. Since the attacker can forge the `conn` object and control `conn->local`, this allows an attacker to lock on an arbitrary address, making it highly exploitable.
Effectively, this use-after-free leads to arbitrary write primitive and privilege escalation. The race condition, stemming from when `rxrpc_put_connection` is called and when the timer fires, requires careful timing. The attacker can win this race through repeated attempts. They'll have plenty of time since they're an unprivileged local user.
Looking at the exploit parameters: This is exploitable, results in a denial of service, unprivileged, accessible from user namespaces. It's *not* triggered by a guest, a network, or a filesystem directly. Although a malicious server *could* trigger it through a race if the local user connected to it, the primary threat is local privilege escalation, so `NetworkTrigger` should be false. The focus should be on how the local process can escalate from unprivileged state, not how the ingress network traffic could be maliciously crafted to trigger it.
Checking `rxrpc_poke_conn` again: it adds to `local->conn_attend_q` and calls `rxrpc_wake_up_io_thread(local)`. The key observation is that the race condition occurs when `rxrpc_put_connection` is called *before* the timer expires and calls the `rxrpc_poke_conn` callback. It is because `timer_delete` doesn't wait for the callback to complete.
The bug is in the fact that `rxrpc_poke_conn` takes a new reference to `conn`. The fix would be to use `refcount_inc_not_zero`, so `rxrpc_poke_conn` just returns without doing anything if the connection is dead.
|
| 67/2 |
2026/08/02 01:21 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/rxrpc/conn_object.c Name:rxrpc_get_connection_maybe]
Results: map[SourceCode: 256: /*
257: * Try to get a ref on a connection.
258: */
259: struct rxrpc_connection *
260: rxrpc_get_connection_maybe(struct rxrpc_connection *conn,
261: enum rxrpc_conn_trace why)
262: {
263: int r;
264:
265: if (conn) {
266: if (__refcount_inc_not_zero(&conn->ref, &r))
267: trace_rxrpc_conn(conn->debug_id, r + 1, why);
268: else
269: conn = NULL;
270: }
271: return conn;
272: }
SourceFile:net/rxrpc/conn_object.c]
|
| 68/2 |
2026/08/02 01:21 |
llm |
expert |
0m
Model:
Tokens:
input: 46775
output: 66
thoughts: 186
Thoughts: **Bug Fix Insight Confirmed**
Alright, so `rxrpc_get_connection_maybe` *does* exist β fantastic! That's the key. Looking at that snippet of code, the lightbulb just went off. If `rxrpc_poke_conn` had utilized `rxrpc_get_connection_maybe`, this entire problem would have vanished. It's so clear now. This confirms my analysis of the bug and the critical component causing the vulnerability. I've got a complete picture.
I have everything I need for the results now:
* Exploitable: Absolutely, yes
* DenialOfService: Without a doubt, true
* Unprivileged: Correct
* UserNamespace: Confirmed
* VMGuestTrigger: False
* VMHostTrigger: False
* NetworkTrigger: False
* RemoteTrigger: False
* PeripheralTrigger: False
* FilesystemTrigger: False
|
| 69/2 |
2026/08/02 01:21 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 70/2 |
2026/08/02 01:21 |
llm |
expert |
0m
Model:
Tokens:
input: 47292
output: 684
thoughts: 498
Thoughts: **My Assessment of the AF_RXRPC Vulnerability**
Okay, the analysis is done. Time to synthesize my understanding into a concise explanation. This is a classic, nasty memory management bug. We're looking at a use-after-free and potential double-free in the AF_RXRPC protocol stack. The root cause? A race condition between the deconstruction of an `rxrpc_connection` object and its associated timer callback, `rxrpc_connection_timer`.
Here's the breakdown: When the last reference to an `rxrpc_connection` is dropped β likely through the normal socket cleanup process β `rxrpc_put_connection` is called. This function *first* calls `timer_delete(&conn->timer)`, which, crucially, is *non-blocking*. After that, it schedules `rxrpc_clean_up_connection` to run, which is responsible for cleaning up the connection's resources.
The trouble arises if the timer callback, `rxrpc_connection_timer`, is already running on another CPU concurrently. This callback calls `rxrpc_poke_conn`, and attempts to grab a *new* reference to the connection using `rxrpc_get_connection`. A key point here is that this call uses `refcount_inc`, which doesn't check for a zero value first. Now, if the `rxrpc_connection`'s reference count is 0, the callback increases it from 0 to 1. This triggers a warning, and worse, adds the connection to the `local->conn_attend_q` list. That list is processed by the I/O thread.
Simultaneously, the destructor, `rxrpc_clean_up_connection`, waits for the timer callback to finish using `timer_delete_sync` (which *does* block). Once the timer callback completes, the destructor continues and, importantly, frees the `rxrpc_connection` memory using RCU.
The problem? The I/O thread, in its *next* turn, processes the `conn_attend_q` list and accesses the *now-freed* connection memory. That's our use-after-free. It can then call `rxrpc_put_connection` on the freed memory, which leads to further memory corruption.
The implications are serious. This is a local privilege escalation vulnerability. An attacker can spray the heap, gain control over the freed `rxrpc_connection` object, and manipulate it. Specifically, the attacker could forge the `conn->local` pointer to cause a lock on an arbitrary address. The resulting memory corruption then allows for arbitrary code execution. This is a pretty straightforward route to privilege escalation.
What's really concerning is the exploitability. Any unprivileged local user can trigger this. Simply creating an AF_RXRPC socket (using `socket(AF_RXRPC, SOCK_DGRAM, PF_INET)`) and starting a client connection is enough to get the ball rolling. With some careful timing of the connection's destruction and the timer expiration, the race condition is easily exploitable.
|
| 71/1 |
2026/08/02 01:21 |
action |
format-explanation |
0m
Results: map[Explanation:Based on the analysis of the bug report and the kernel source code, here is the
assessment of the vulnerability:
**Vulnerability Analysis:**
The bug is a Use-After-Free (UAF) vulnerability in the AF_RXRPC network protocol
subsystem, specifically caused by a race condition between the destruction of an
`rxrpc_connection` object and its timer callback `rxrpc_connection_timer`.
1. When the last reference to an `rxrpc_connection` is dropped,
`rxrpc_put_connection()` is called. This function calls
`timer_delete(&conn->timer)` (which is non-blocking) and then schedules the
connection's destructor (`rxrpc_clean_up_connection()`).
2. If the timer callback is already running concurrently on another CPU, it
executes `rxrpc_poke_conn()`, which attempts to take a new reference on the
connection using `rxrpc_get_connection()`.
3. `rxrpc_get_connection()` uses `refcount_inc()` instead of
`refcount_inc_not_zero()`. This increments the reference count from 0 to 1
(triggering the `refcount_t: addition on 0; use-after-free` warning) and adds
the connection to the `local->conn_attend_q` list to be processed by the I/O
thread.
4. Meanwhile, the destructor `rxrpc_clean_up_connection()` waits for the timer
callback to finish using `timer_delete_sync()`, and then proceeds to free the
connection memory via RCU.
5. Later, the I/O thread (`rxrpc_io_thread()`) processes the `conn_attend_q`
list, accesses the freed connection memory (Use-After-Free), and calls
`rxrpc_put_connection()` again, which can lead to further memory corruption.
**Exploitability:**
This vulnerability is **highly exploitable**. An attacker can spray the heap to
reallocate and control the freed `rxrpc_connection` object. When the I/O thread
accesses the attacker-controlled object, it can be manipulated to hijack control
flow or corrupt arbitrary memory (e.g., by forging the `conn->local` pointer to
cause a spinlock on an arbitrary memory address).
**Access and Trigger:**
The bug can be triggered by any **unprivileged local user**. Creating an
AF_RXRPC socket (`socket(AF_RXRPC, SOCK_DGRAM, PF_INET)`) does not require any
special capabilities (no `capable()` or `ns_capable()` checks are present in
`rxrpc_create()`). The user can initiate a client connection and trigger the
race condition by carefully timing the connection's destruction with the timer's
expiration. Since it is accessible to unprivileged users in the initial
namespace, it is also accessible within user namespaces.
It is not considered a remote network trigger because the timer
(`rxrpc_reduce_conn_timer`) is only armed for client connections initiated
locally, meaning a remote attacker cannot trigger this independently without a
local process initiating the connection.
**Conclusion:**
This is a critical local privilege escalation (LPE) and Denial-of-Service (DoS)
vulnerability accessible to unprivileged users.]
|