| 0/0 |
2026/07/14 00:44 |
flow |
assessment-security |
39m
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:
**Bug Mechanism and Impact:**
The bug report indicates an RCU stall (`rcu_preempt kthread starved`)
accompanied by repeated warnings from the bridge subsystem (`bridge0: received
packet on ... with own address as source address`).
This is the classic symptom of a **bridging loop** causing a **broadcast
storm**. When a bridging loop is created (e.g., connecting two bridges together
via multiple `veth` pairs) and Spanning Tree Protocol (STP) is disabled (which
is the default), broadcast packets are continuously flooded and multiplied
across the loop.
This exponential growth of packets keeps the network backlog queues constantly
full. The kernel processes these packets in the softirq context
(`net_rx_action`). Although `net_rx_action` has a budget and time limit to yield
to `ksoftirqd` and task context, the sheer volume of packets causes a **softirq
livelock**. Every time the CPU returns to task context, the next timer interrupt
fires almost immediately, and upon exiting the interrupt (`irq_exit_rcu`), the
kernel sees pending softirqs and processes them again on the spot. This
completely starves normal tasks, including the `rcu_preempt` kthread, leading to
an RCU stall, system unresponsiveness, and eventually an Out-Of-Memory (OOM)
condition or kernel panic.
**Exploitability:**
This is a pure resource exhaustion issue (CPU livelock). There is no memory
corruption, use-after-free, out-of-bounds access, or information leak involved.
Therefore, it is **not exploitable** for privilege escalation or arbitrary code
execution, but it is a highly effective **Denial of Service (DoS)**.
**Accessibility:**
- **Unprivileged Processes:** Creating a bridge and enslaving interfaces to it
requires `CAP_NET_ADMIN` in the network namespace. A typical unprivileged
process without user namespaces cannot trigger this.
- **User Namespaces:** An unprivileged user can create a new user namespace and
a new network namespace, gaining `CAP_NET_ADMIN` within that namespace. They can
then create the bridges, `veth` pairs, form the loop, and inject a broadcast
packet to crash the host kernel. Thus, it is accessible from user namespaces.
**Other Triggers:**
- **Network / Peripheral Trigger:** If a system administrator misconfigures the
physical network (or an attacker plugs a loopback cable between two ports of the
machine, or connects a dumb switch with a loop), a bridging loop is formed on
the physical interfaces. In this scenario, any incoming broadcast packet from
the network will trigger the exact same broadcast storm and softirq livelock.
- **VM Guest Trigger:** A user inside a VM guest can perform the same namespace
tricks to crash the guest kernel. Exploitable:false FilesystemTrigger:false NetworkTrigger:true PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:true VMGuestTrigger:true VMHostTrigger:false]
|
| 1/1 |
2026/07/14 00:44 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/07/14 00:44 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/a3dbc50496d8af9fdfd3ade925857e324feb2b21]
|
| 3/1 |
2026/07/14 00:45 |
action |
kernel-builder |
23m
Results: map[KernelObj:/app/workdir/cache/build/e8ab20d5e22aed5384a54656d98e7e5c7ad938a5]
|
| 4/1 |
2026/07/14 01:09 |
action |
codesearch-prepare |
9m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/07/14 01:19 |
agent |
expert |
5m
Model:
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:true PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:true VMGuestTrigger:true 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:
bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)
bridge0: received packet on veth1_to_bridge with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)
rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
rcu: (detected by 0, t=10502 jiffies, g=45253, q=1695 ncpus=2)
rcu: All QSes seen, last rcu_preempt kthread activity 6346 (4294978027-4294971681), jiffies_till_next_fqs=1, root ->qsmask 0x0
rcu: rcu_preempt kthread starved for 6347 jiffies! g45253 f0x2 RCU_GP_WAIT_FQS(5) ->state=0x0 ->cpu=0
rcu: Unless rcu_preempt kthread gets sufficient CPU time, OOM is now expected behavior.
rcu: RCU grace-period kthread stack dump:
task:rcu_preempt state:R running task stack:27656 pid:16 tgid:16 ppid:2 task_flags:0x208040 flags:0x00080000
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5510 [inline]
__schedule+0x17d9/0x56c0 kernel/sched/core.c:7234
preempt_schedule_irq+0x4d/0xa0 kernel/sched/core.c:7556
irqentry_exit_to_kernel_mode include/linux/irq-entry-common.h:539 [inline]
irqentry_exit+0x14f/0x8f0 kernel/entry/common.c:167
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674
RIP: 0010:preempt_schedule_thunk+0x0/0x40 arch/x86/entry/thunk.S:12
Code: 03 93 00 48 c7 c0 80 5a e0 8b c3 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 <55> 48 89 e5 57 56 52 51 50 41 50 41 51 41 52 41 53 e8 8a d9 9c 0a
RSP: 0018:ffffc90000157b60 EFLAGS: 00010246
RAX: 0000000000000000 RBX: 0000000000000282 RCX: 0000000000000000
RDX: 0000000000000000 RSI: ffffffff8e22a5a0 RDI: 00000000ffffffff
RBP: 0000000000000000 R08: ffffffff905775f7 R09: 1ffffffff20aeebe
R10: dffffc0000000000 R11: fffffbfff20aeebf R12: ffff88801dec0000
R13: 1ffff9200002af95 R14: ffffffff8eb5fd58 R15: ffffc90000157ca0
__raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:179 [inline]
_raw_spin_unlock_irqrestore+0x74/0x80 kernel/locking/spinlock.c:198
prepare_to_swait_event+0x322/0x350 kernel/sched/swait.c:120
rcu_gp_fqs_loop+0x2d7/0x11f0 kernel/rcu/tree.c:2123
rcu_gp_kthread+0x9e/0x2b0 kernel/rcu/tree.c:2325
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
rcu: Stack dump where RCU GP kthread last ran:
CPU: 0 UID: 0 PID: 5714 Comm: kworker/0:4 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/09/2026
Workqueue: events_power_efficient wg_ratelimiter_gc_entries
RIP: 0010:lock_release+0x5/0x3c0 kernel/locking/lockdep.c:5876
Code: e9 4f fe ff ff 41 bf 2f 00 00 00 e9 06 ff ff ff 0f 1f 44 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 55 <41> 57 41 56 41 55 41 54 53 48 83 ec 30 49 89 f5 49 89 fe 65 48 8b
RSP: 0018:ffffc900000065b8 EFLAGS: 00000202
RAX: 0000000000000001 RBX: ffffc900000066d8 RCX: 0000000080000101
RDX: ffffc90000006701 RSI: ffffffff8176f27f RDI: ffffffff8eb59c60
RBP: 1ffffffff21dd001 R08: ffffc900000066b8 R09: 0000000000000000
R10: ffffc90000006718 R11: fffff52000000ce5 R12: ffffc90000000000
R13: ffffffff8176f27f R14: ffffc900000066c8 R15: ffffc900000066c8
FS: 0000000000000000(0000) GS:ffff888124f58000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000000110c3e4e8d CR3: 00000000574d4000 CR4: 00000000003526f0
Call Trace:
<IRQ>
rcu_lock_release include/linux/rcupdate.h:310 [inline]
rcu_read_unlock include/linux/rcupdate.h:871 [inline]
class_rcu_destructor include/linux/rcupdate.h:1183 [inline]
unwind_next_frame+0x1baa/0x2550 arch/x86/kernel/unwind_orc.c:709
__unwind_start+0x514/0x660 arch/x86/kernel/unwind_orc.c:787
unwind_start arch/x86/include/asm/unwind.h:64 [inline]
arch_stack_walk+0xe3/0x150 arch/x86/kernel/stacktrace.c:24
stack_trace_save+0xa9/0x100 kernel/stacktrace.c:122
kasan_save_stack mm/kasan/common.c:57 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:78
kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2705 [inline]
slab_free mm/slub.c:6405 [inline]
kmem_cache_free+0x182/0x650 mm/slub.c:6532
skb_ext_del include/linux/skbuff.h:5100 [inline]
nf_bridge_info_free net/bridge/br_netfilter_hooks.c:157 [inline]
br_nf_dev_queue_xmit+0x4f6/0x2540 net/bridge/br_netfilter_hooks.c:856
NF_HOOK+0x64e/0x6e0 include/linux/netfilter.h:318
br_nf_post_routing+0xa53/0xee0 net/bridge/br_netfilter_hooks.c:964
nf_hook_entry_hookfn include/linux/netfilter.h:158 [inline]
nf_hook_slow+0xc5/0x220 net/netfilter/core.c:619
nf_hook include/linux/netfilter.h:273 [inline]
NF_HOOK+0x23e/0x3f0 include/linux/netfilter.h:316
br_forward_finish+0xd3/0x130 net/bridge/br_forward.c:66
br_nf_hook_thresh net/bridge/br_netfilter_hooks.c:1165 [inline]
br_nf_forward_finish+0xa76/0xea0 net/bridge/br_netfilter_hooks.c:667
NF_HOOK+0x64e/0x6e0 include/linux/netfilter.h:318
br_nf_forward_ip+0x7eb/0xa30 net/bridge/br_netfilter_hooks.c:721
nf_hook_entry_hookfn include/linux/netfilter.h:158 [inline]
nf_hook_slow+0xc5/0x220 net/netfilter/core.c:619
nf_hook include/linux/netfilter.h:273 [inline]
NF_HOOK+0x23e/0x3f0 include/linux/netfilter.h:316
__br_forward+0x397/0x540 net/bridge/br_forward.c:115
deliver_clone net/bridge/br_forward.c:131 [inline]
maybe_deliver net/bridge/br_forward.c:191 [inline]
br_flood+0x3e6/0x8d0 net/bridge/br_forward.c:245
br_handle_frame_finish+0x1175/0x1990 net/bridge/br_input.c:229
br_nf_hook_thresh+0x3dd/0x4c0 net/bridge/br_netfilter_hooks.c:-1
br_nf_pre_routing_finish_ipv6+0x90c/0xc40 net/bridge/br_netfilter_ipv6.c:-1
NF_HOOK include/linux/netfilter.h:318 [inline]
br_nf_pre_routing_ipv6+0x382/0x6f0 net/bridge/br_netfilter_ipv6.c:183
nf_hook_entry_hookfn include/linux/netfilter.h:158 [inline]
nf_hook_bridge_pre net/bridge/br_input.c:291 [inline]
br_handle_frame+0x127f/0x1510 net/bridge/br_input.c:442
__netif_receive_skb_core+0x989/0x30b0 net/core/dev.c:6099
__netif_receive_skb_one_core net/core/dev.c:6210 [inline]
__netif_receive_skb net/core/dev.c:6325 [inline]
process_backlog+0x706/0x1860 net/core/dev.c:6676
__napi_poll+0xaa/0x330 net/core/dev.c:7735
napi_poll net/core/dev.c:7798 [inline]
net_rx_action+0x61d/0xf50 net/core/dev.c:7955
handle_softirqs+0x225/0x840 kernel/softirq.c:622
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xca/0x220 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+0xa6/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:__might_resched+0xbb/0x4d0 kernel/sched/core.c:-1
Code: 74 28 80 3d 16 57 ab 0e 00 75 1f c6 05 0d 57 ab 0e 01 48 c7 c7 80 04 ec 8b be c7 23 00 00 48 c7 c2 00 19 ec 8b e8 75 ed 0c 00 <48> bd 00 00 00 00 00 fc ff df 65 44 8b 2d 83 ff d6 11 41 81 e5 ff
RSP: 0018:ffffc90003edfa50 EFLAGS: 00000246
RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000046
RDX: 0000000000000000 RSI: ffffffff8e4af5f8 RDI: ffffffff8c4bd380
RBP: dffffc0000000000 R08: ffffffff8f774d43 R09: 1ffffffff1eee9a8
R10: dffffc0000000000 R11: fffffbfff1eee9a9 R12: ffff8880571b7bf8
R13: dffffc0000000000 R14: 0000000000000f7f R15: 0000000000000000
wg_ratelimiter_gc_entries+0x3a4/0x450 drivers/net/wireguard/ratelimiter.c:78
process_one_work kernel/workqueue.c:3322 [inline]
process_scheduled_works+0xa8e/0x14e0 kernel/workqueue.c:3405
worker_thread+0xa47/0xfb0 kernel/workqueue.c:3486
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
net_ratelimit: 3411 callbacks suppressed
bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)
bridge0: received packet on veth1_to_bridge with own address as source address (addr:fa:11:7d:48:06:49, vlan:0)
bridge0: received packet on veth1_to_bridge with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)
bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:1c, vlan:0)
bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)
bridge0: received packet on veth1_to_bridge with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)
bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)
bridge0: received packet on veth1_to_bridge with own address as source address (addr:fa:11:7d:48:06:49, vlan:0)
bridge0: received packet on veth1_to_bridge with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)
bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:1c, vlan:0)
net_ratelimit: 8393 callbacks suppressed
bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:1c, vlan:0)
bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)
bridge0: received packet on veth1_to_bridge with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)
bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)
bridge0: received packet on veth1_to_bridge with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)
bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)
bridge0: received packet on veth1_to_bridge with own address as source address (addr:fa:11:7d:48:06:49, vlan:0)
bridge0: received packet on veth1_to_bridge with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)
bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:1c, vlan:0)
bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)
Reply: Based on the analysis of the bug report and the kernel source code, here is the assessment of the vulnerability:
**Bug Mechanism and Impact:**
The bug report indicates an RCU stall (`rcu_preempt kthread starved`) accompanied by repeated warnings from the bridge subsystem (`bridge0: received packet on ... with own address as source address`).
This is the classic symptom of a **bridging loop** causing a **broadcast storm**. When a bridging loop is created (e.g., connecting two bridges together via multiple `veth` pairs) and Spanning Tree Protocol (STP) is disabled (which is the default), broadcast packets are continuously flooded and multiplied across the loop.
This exponential growth of packets keeps the network backlog queues constantly full. The kernel processes these packets in the softirq context (`net_rx_action`). Although `net_rx_action` has a budget and time limit to yield to `ksoftirqd` and task context, the sheer volume of packets causes a **softirq livelock**. Every time the CPU returns to task context, the next timer interrupt fires almost immediately, and upon exiting the interrupt (`irq_exit_rcu`), the kernel sees pending softirqs and processes them again on the spot. This completely starves normal tasks, including the `rcu_preempt` kthread, leading to an RCU stall, system unresponsiveness, and eventually an Out-Of-Memory (OOM) condition or kernel panic.
**Exploitability:**
This is a pure resource exhaustion issue (CPU livelock). There is no memory corruption, use-after-free, out-of-bounds access, or information leak involved. Therefore, it is **not exploitable** for privilege escalation or arbitrary code execution, but it is a highly effective **Denial of Service (DoS)**.
**Accessibility:**
- **Unprivileged Processes:** Creating a bridge and enslaving interfaces to it requires `CAP_NET_ADMIN` in the network namespace. A typical unprivileged process without user namespaces cannot trigger this.
- **User Namespaces:** An unprivileged user can create a new user namespace and a new network namespace, gaining `CAP_NET_ADMIN` within that namespace. They can then create the bridges, `veth` pairs, form the loop, and inject a broadcast packet to crash the host kernel. Thus, it is accessible from user namespaces.
**Other Triggers:**
- **Network / Peripheral Trigger:** If a system administrator misconfigures the physical network (or an attacker plugs a loopback cable between two ports of the machine, or connects a dumb switch with a loop), a bridging loop is formed on the physical interfaces. In this scenario, any incoming broadcast packet from the network will trigger the exact same broadcast storm and softirq livelock.
- **VM Guest Trigger:** A user inside a VM guest can perform the same namespace tricks to crash the guest kernel.
|
| 6/2 |
2026/07/14 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 11072
output: 33
thoughts: 2231
Thoughts: **RCU Stall Analysis: A Deep Dive into a Packet Processing Conundrum**
Okay, so I've got an RCU stall report, specifically a `rcu_preempt kthread` starved for a significant number of jiffies. The stack trace is pointing squarely at a CPU stuck in softirq processing, specifically network packet handling. It's clear that the CPU is buried within the `__netif_receive_skb_core` path, digging through bridge packets, and looks like it is receiving packets on a bridge interface, specifically with its own address as the source.
The immediate thought is packet loop, a classic bridge issue leading to a denial-of-service, a classic issue if Spanning Tree Protocol (STP) is disabled. The repeated "received packet with own address as source address" messages strongly suggest this. I see the stack with netfilter hooks, calls to `br_flood`, and all of this reinforces my initial impression of a loop, possibly from a misconfigured bridge. I also see that it is freeing skbs, which indicates, it's a loop.
A quick search confirms this "own address" message is a standard bridge warning during loop detection. The RCU stall is a consequence, from excessive packet processing without yield, or an infinite loop. But hold on, the stack is interesting because I see the `br_flood` function in the call path. It iterates and clones the packet for each port. That shouldn't be infinite, even with lots of ports, because eventually, the limits of the NAPI polling would intervene.
Now, could it be a veth interface situation? If a veth is looped back in the bridge, that would be a path for a loop. But the softirq should be limited by the budget of the time limits of the NAPI poll. So I still have to figure out why the stall.
I see it is a `rcu_preempt kthread` that starved. The CPU was busy doing softirqs. If this went on for so long, and `ksoftirqd` didn't kick in, there is something interesting there. I need to understand that the softirq is triggered from an interrupt (like the timer). But the stack trace also shows something else. The timer is interrupting `wg_ratelimiter_gc_entries` which is called from a workqueue. Okay, I need to see what `wg_ratelimiter_gc_entries` does. Could this be the root cause? Does this function, perhaps, disable preemption or hold a lock? That is the question.
|
| 7/2 |
2026/07/14 01:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:wg_ratelimiter_gc_entries]
Results: map[SourceCode: 54: /* Calling this function with a NULL work uninits all entries. */
55: static void wg_ratelimiter_gc_entries(struct work_struct *work)
56: {
57: const u64 now = ktime_get_coarse_boottime_ns();
58: struct ratelimiter_entry *entry;
59: struct hlist_node *temp;
60: unsigned int i;
61:
62: for (i = 0; i < table_size; ++i) {
63: spin_lock(&table_lock);
64: hlist_for_each_entry_safe(entry, temp, &table_v4[i], hash) {
65: if (unlikely(!work) ||
66: now - entry->last_time_ns > NSEC_PER_SEC)
67: entry_uninit(entry);
68: }
69: #if IS_ENABLED(CONFIG_IPV6)
70: hlist_for_each_entry_safe(entry, temp, &table_v6[i], hash) {
71: if (unlikely(!work) ||
72: now - entry->last_time_ns > NSEC_PER_SEC)
73: entry_uninit(entry);
74: }
75: #endif
76: spin_unlock(&table_lock);
77: if (likely(work))
78: cond_resched();
79: }
80: if (likely(work))
81: queue_delayed_work(system_power_efficient_wq, &gc_work, HZ);
82: }
SourceFile:drivers/net/wireguard/ratelimiter.c]
|
| 8/2 |
2026/07/14 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 14378
output: 29
thoughts: 742
Thoughts: **Analyzing a Kernel Stack Trace and Bridging Loop**
Okay, so looking at this stack trace from CPU 0, the problem seems to be originating within `wg_ratelimiter_gc_entries` and specifically from `cond_resched()`. This is interesting, as that's a point where the kernel is deliberately giving up the CPU. The trace then indicates a timer interrupt, followed by `net_rx_action` softirq processing. The RCU stall is the key, and the `rcu_preempt` kthread is starved. That's the tell. It's *running* (R state), but hasn't been scheduled for an extended period β over 6 seconds at HZ=1000, potentially much longer at a lower HZ, which is bad.
My initial thoughts run towards the usual culprits β a packet storm overwhelming the system. But the softirq processing should be limited; `net_rx_action` has a budget, and eventually should defer to `ksoftirqd`. Hmm. Both `ksoftirqd` and `rcu_preempt` normally run at normal priority. If a massive packet storm is swamping the system, `ksoftirqd` could be consuming the CPU, starving the other threads, which in turn explains the RCU thread starvation. This RCU starvation is a common symptom of a heavy load.
Then, there's the log entry: `bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0)`. That's a huge red flag: a bridging loop. Classic network loop behavior. Packets are flooding the interfaces. Is this a kernel bug, or is it a misconfiguration? Usually, this is a misconfiguration of the network topology. But, given the context, this could be induced by syzkaller. The system is probably creating a bridge, adding veth interfaces, and inadvertently creating this loop.
So the loop is the key here. This is a denial of service. The system is being swamped and starved. Does this create a security vulnerability? If an unprivileged user can create a bridging loop and crash the system, it *is* a DoS. Can an unprivileged user create a bridge and veth interfaces? In a user namespace with `CAP_NET_ADMIN`, yes. I need to check whether `br_add_if` requires `CAP_NET_ADMIN` in the initial namespace or just the network namespace.
|
| 9/2 |
2026/07/14 01:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:br_add_if]
Results: map[SourceCode: 559: /* called with RTNL */
560: int br_add_if(struct net_bridge *br, struct net_device *dev,
561: struct netlink_ext_ack *extack)
562: {
563: struct net_bridge_port *p;
564: int err = 0;
565: unsigned br_hr, dev_hr;
566: bool changed_addr, fdb_synced = false;
567:
568: /* Don't allow bridging non-ethernet like devices. */
569: if ((dev->flags & IFF_LOOPBACK) ||
570: dev->type != ARPHRD_ETHER || dev->addr_len != ETH_ALEN ||
571: !is_valid_ether_addr(dev->dev_addr))
572: return -EINVAL;
573:
574: /* No bridging of bridges */
575: if (dev->netdev_ops->ndo_start_xmit == br_dev_xmit) {
576: NL_SET_ERR_MSG(extack,
577: "Can not enslave a bridge to a bridge");
578: return -ELOOP;
579: }
580:
581: /* Device has master upper dev */
582: if (netdev_master_upper_dev_get(dev))
583: return -EBUSY;
584:
585: /* No bridging devices that dislike that (e.g. wireless) */
586: if (dev->priv_flags & IFF_DONT_BRIDGE) {
587: NL_SET_ERR_MSG(extack,
588: "Device does not allow enslaving to a bridge");
589: return -EOPNOTSUPP;
590: }
591:
592: p = new_nbp(br, dev);
593: if (IS_ERR(p))
594: return PTR_ERR(p);
595:
596: call_netdevice_notifiers(NETDEV_JOIN, dev);
597:
598: err = dev_set_allmulti(dev, 1);
599: if (err) {
600: br_multicast_del_port(p);
601: netdev_put(dev, &p->dev_tracker);
602: kfree(p); /* kobject not yet init'd, manually free */
603: goto err1;
604: }
605:
606: err = kobject_init_and_add(&p->kobj, &brport_ktype, &(dev->dev.kobj),
607: SYSFS_BRIDGE_PORT_ATTR);
608: if (err)
609: goto err2;
610:
611: err = br_sysfs_addif(p);
612: if (err)
613: goto err2;
614:
615: err = br_netpoll_enable(p);
616: if (err)
617: goto err3;
618:
619: err = netdev_rx_handler_register(dev, br_get_rx_handler(dev), p);
620: if (err)
621: goto err4;
622:
623: dev->priv_flags |= IFF_BRIDGE_PORT;
624:
625: err = netdev_master_upper_dev_link(dev, br->dev, NULL, NULL, extack);
626: if (err)
627: goto err5;
628:
629: dev_disable_lro(dev);
630:
631: list_add_rcu(&p->list, &br->port_list);
632:
633: nbp_update_port_count(br);
634: if (!br_promisc_port(p) && (p->dev->priv_flags & IFF_UNICAST_FLT)) {
635: /* When updating the port count we also update all ports'
636: * promiscuous mode.
637: * A port leaving promiscuous mode normally gets the bridge's
638: * fdb synced to the unicast filter (if supported), however,
639: * `br_port_clear_promisc` does not distinguish between
640: * non-promiscuous ports and *new* ports, so we need to
641: * sync explicitly here.
642: */
643: fdb_synced = br_fdb_sync_static(br, p) == 0;
644: if (!fdb_synced)
645: netdev_err(dev, "failed to sync bridge static fdb addresses to this port\n");
646: }
647:
648: br_hr = br->dev->needed_headroom;
649: dev_hr = netdev_get_fwd_headroom(dev);
650: if (br_hr < dev_hr)
651: update_headroom(br, dev_hr);
652: else
653: netdev_set_rx_headroom(dev, br_hr);
654:
655: if (br_fdb_add_local(br, p, dev->dev_addr, 0))
656: netdev_err(dev, "failed insert local address bridge forwarding table\n");
657:
658: if (br->dev->addr_assign_type != NET_ADDR_SET) {
659: /* Ask for permission to use this MAC address now, even if we
660: * don't end up choosing it below.
661: */
662: err = netif_pre_changeaddr_notify(br->dev, dev->dev_addr,
663: extack);
664: if (err)
665: goto err6;
666: }
667:
668: err = nbp_vlan_init(p, extack);
669: if (err) {
670: netdev_err(dev, "failed to initialize vlan filtering on this port\n");
671: goto err6;
672: }
673:
674: spin_lock_bh(&br->lock);
675: changed_addr = br_stp_recalculate_bridge_id(br);
676:
677: if (netif_running(dev) && netif_oper_up(dev) &&
678: (br->dev->flags & IFF_UP))
679: br_stp_enable_port(p);
680: spin_unlock_bh(&br->lock);
681:
682: br_ifinfo_notify(RTM_NEWLINK, NULL, p);
683:
684: if (changed_addr)
685: call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
686:
687: br_mtu_auto_adjust(br);
688:
689: netdev_compute_master_upper_features(br->dev, false);
690:
691: kobject_uevent(&p->kobj, KOBJ_ADD);
692:
693: return 0;
694:
695: err6:
696: if (fdb_synced)
697: br_fdb_unsync_static(br, p);
698: list_del_rcu(&p->list);
699: br_fdb_delete_by_port(br, p, 0, 1);
700: nbp_update_port_count(br);
701: netdev_upper_dev_unlink(dev, br->dev);
702: err5:
703: dev->priv_flags &= ~IFF_BRIDGE_PORT;
704: netdev_rx_handler_unregister(dev);
705: err4:
706: br_netpoll_disable(p);
707: err3:
708: sysfs_remove_link(br->ifobj, p->dev->name);
709: err2:
710: br_multicast_del_port(p);
711: netdev_put(dev, &p->dev_tracker);
712: kobject_put(&p->kobj);
713: dev_set_allmulti(dev, -1);
714: err1:
715: return err;
716: }
SourceFile:net/bridge/br_if.c]
|
| 10/2 |
2026/07/14 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 18213
output: 28
thoughts: 469
Thoughts: **Analyzing a Potential Kernel DoS Vulnerability**
Okay, so I'm looking at how `br_add_if` gets called. It looks like it can be triggered through netlink, specifically `br_add_slave` or something similar, which in turn checks for `ns_capable(net->user_ns, CAP_NET_ADMIN)`. Alright, that means an unprivileged user can manipulate network namespaces. That opens the door to creating a bridge, setting up veth pairs, connecting them to the bridge... and bam, a bridging loop. Classic network configuration mistake, really.
The report indicates this loop leads to an RCU stall. A packet storm, okay, I can see that. But does this specific storm directly cause the RCU stall? Hmm, an RCU stall is a kernel-level hang, essentially a denial-of-service. So, is this a *security vulnerability*? If a user namespace can induce a complete system stall, that's typically considered a DoS vulnerability, yes.
But hold on, let's drill down. Is this packet storm consuming 100% CPU on *one* CPU starving RCU, or is it more of a massive influx of softirqs? If it's just softirqs, `ksoftirqd` *should* handle the load and prevent a complete stall. But the stack trace shows `br_nf_dev_queue_xmit`...that's interesting. Does this indicate something more synchronous about the loop?
Wait... could this packet loop be *synchronous*? No, the veth interaction through `dev_queue_xmit` should put the packet on the other veth's RX queue and schedule either NAPI or a softirq. Aha! Thinking this through, if the bridge is sending to a veth, `veth_xmit` ends up calling `__dev_forward_skb` and then `netif_rx`. And `netif_rx` queues the packet to the backlog, scheduling the softirq. But wait, if `netif_rx` is called from softirq context, it *just adds to the backlog*. Then, in that same softirq run (`process_backlog`), it *will* dequeue the packet and process it! This feels significant. I need to check `process_backlog` and see what the implications of this synchronous queuing and processing loop are. This could very well be the root cause of the RCU stall.
|
| 11/2 |
2026/07/14 01:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:process_backlog]
Results: map[SourceCode:6654: static int process_backlog(struct napi_struct *napi, int quota)
6655: {
6656: struct softnet_data *sd = container_of(napi, struct softnet_data, backlog);
6657: bool again = true;
6658: int work = 0;
6659:
6660: /* Check if we have pending ipi, its better to send them now,
6661: * not waiting net_rx_action() end.
6662: */
6663: if (sd_has_rps_ipi_waiting(sd)) {
6664: local_irq_disable();
6665: net_rps_action_and_irq_enable(sd);
6666: }
6667:
6668: napi->weight = READ_ONCE(net_hotdata.dev_rx_weight);
6669: while (again) {
6670: struct sk_buff *skb;
6671:
6672: local_lock_nested_bh(&softnet_data.process_queue_bh_lock);
6673: while ((skb = __skb_dequeue(&sd->process_queue))) {
6674: local_unlock_nested_bh(&softnet_data.process_queue_bh_lock);
6675: rcu_read_lock();
6676: __netif_receive_skb(skb);
6677: rcu_read_unlock();
6678: if (++work >= quota) {
6679: rps_input_queue_head_add(sd, work);
6680: return work;
6681: }
6682:
6683: local_lock_nested_bh(&softnet_data.process_queue_bh_lock);
6684: }
6685: local_unlock_nested_bh(&softnet_data.process_queue_bh_lock);
6686:
6687: backlog_lock_irq_disable(sd);
6688: if (skb_queue_empty(&sd->input_pkt_queue)) {
6689: /*
6690: * Inline a custom version of __napi_complete().
6691: * only current cpu owns and manipulates this napi,
6692: * and NAPI_STATE_SCHED is the only possible flag set
6693: * on backlog.
6694: * We can use a plain write instead of clear_bit(),
6695: * and we dont need an smp_mb() memory barrier.
6696: */
6697: napi->state &= NAPIF_STATE_THREADED;
6698: again = false;
6699: } else {
6700: local_lock_nested_bh(&softnet_data.process_queue_bh_lock);
6701: skb_queue_splice_tail_init(&sd->input_pkt_queue,
6702: &sd->process_queue);
6703: local_unlock_nested_bh(&softnet_data.process_queue_bh_lock);
6704: }
6705: backlog_unlock_irq_enable(sd);
6706: }
6707:
6708: if (work)
6709: rps_input_queue_head_add(sd, work);
6710: return work;
6711: }
SourceFile:net/core/dev.c]
|
| 12/2 |
2026/07/14 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 20207
output: 34
thoughts: 307
Thoughts: **Analyzing the Network Packet Processing Stall**
Okay, so I'm looking at this network processing issue, and it seems like the `process_backlog` function is playing a key role here. It's capped by the `quota`, which originates from `napi_poll`. In the `net_rx_action` context, this `quota` is equivalent to the `weight`, which, by default, is `dev_rx_weight` and set to 64. That means `process_backlog` should handle a maximum of 64 packets before returning.
The `net_rx_action` function then checks its budget and time constraints β specifically, 300 for the budget and 2 jiffies for the time limit. If those limits are exceeded, it's supposed to break and schedule `ksoftirqd`. The intention is to prevent the processing from stalling the CPU indefinitely, which is precisely what's happening. But the question is: why is it stalling?
Let's re-examine that stack trace⦠aha! I see something interesting here: `br_nf_pre_routing_ipv6` is called. That doesn't seem right. It's related to the bridge netfilter, something that could be very time-consuming. I need to dive deeper into `br_nf_pre_routing_ipv6` to understand its role in this and see if it is the bottleneck.
|
| 13/2 |
2026/07/14 01:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:br_nf_pre_routing_ipv6]
Results: map[SourceCode: 159: /* Replicate the checks that IPv6 does on packet reception and pass the packet
160: * to ip6tables.
161: */
162: unsigned int br_nf_pre_routing_ipv6(void *priv,
163: struct sk_buff *skb,
164: const struct nf_hook_state *state)
165: {
166: struct nf_bridge_info *nf_bridge;
167:
168: if (br_validate_ipv6(state->net, skb))
169: return NF_DROP_REASON(skb, SKB_DROP_REASON_IP_INHDR, 0);
170:
171: nf_bridge = nf_bridge_alloc(skb);
172: if (!nf_bridge)
173: return NF_DROP_REASON(skb, SKB_DROP_REASON_NOMEM, 0);
174: if (!setup_pre_routing(skb, state->net))
175: return NF_DROP_REASON(skb, SKB_DROP_REASON_DEV_READY, 0);
176:
177: nf_bridge = nf_bridge_info_get(skb);
178: nf_bridge->ipv6_daddr = ipv6_hdr(skb)->daddr;
179:
180: skb->protocol = htons(ETH_P_IPV6);
181: skb->transport_header = skb->network_header + sizeof(struct ipv6hdr);
182:
183: NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, state->net, state->sk, skb,
184: skb->dev, NULL,
185: br_nf_pre_routing_finish_ipv6);
186:
187: return NF_STOLEN;
188: }
SourceFile:net/bridge/br_netfilter_ipv6.c]
|
| 14/2 |
2026/07/14 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 21427
output: 36
thoughts: 45
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing the Netfilter Hook Sequence**
Alright, so the `NF_HOOK` is firing, which immediately implies we're in the netfilter framework. The hook in question then directly calls `br_nf_pre_routing_finish_ipv6`. That function call is key, so that's where my focus needs to be. This means we're in the bridge netfilter context, and dealing with IPv6 packets before the routing decision, but after the initial ingress hook. This is right before the packets can be routed out. I need to understand what `br_nf_pre_routing_finish_ipv6` is doing in this phase, what its expected behavior is, and whether the processing that happens there is expected.
|
| 15/2 |
2026/07/14 01:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:br_nf_pre_routing_finish_ipv6]
Results: map[SourceCode: 97: /* PF_BRIDGE/PRE_ROUTING: Undo the changes made for ip6tables
98: * PREROUTING and continue the bridge PRE_ROUTING hook. See comment
99: * for br_nf_pre_routing_finish(), same logic is used here.
100: */
101: static int br_nf_pre_routing_finish_ipv6(struct net *net, struct sock *sk, struct sk_buff *skb)
102: {
103: struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
104: struct rtable *rt;
105: struct net_device *dev = skb->dev, *br_indev;
106:
107: br_indev = nf_bridge_get_physindev(skb, net);
108: if (!br_indev) {
109: kfree_skb(skb);
110: return 0;
111: }
112:
113: nf_bridge->frag_max_size = IP6CB(skb)->frag_max_size;
114:
115: if (nf_bridge->pkt_otherhost) {
116: skb->pkt_type = PACKET_OTHERHOST;
117: nf_bridge->pkt_otherhost = false;
118: }
119: nf_bridge->in_prerouting = 0;
120: if (br_nf_ipv6_daddr_was_changed(skb, nf_bridge)) {
121: skb_dst_drop(skb);
122: ip6_route_input(skb);
123:
124: if (skb_dst(skb)->error) {
125: kfree_skb(skb);
126: return 0;
127: }
128:
129: if (skb_dst(skb)->dev == dev) {
130: skb->dev = br_indev;
131: nf_bridge_update_protocol(skb);
132: nf_bridge_push_encap_header(skb);
133: br_nf_hook_thresh(NF_BR_PRE_ROUTING,
134: net, sk, skb, skb->dev, NULL,
135: br_nf_pre_routing_finish_bridge);
136: return 0;
137: }
138: ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr);
139: skb->pkt_type = PACKET_HOST;
140: } else {
141: rt = bridge_parent_rtable(br_indev);
142: if (!rt) {
143: kfree_skb(skb);
144: return 0;
145: }
146: skb_dst_drop(skb);
147: skb_dst_set_noref(skb, &rt->dst);
148: }
149:
150: skb->dev = br_indev;
151: nf_bridge_update_protocol(skb);
152: nf_bridge_push_encap_header(skb);
153: br_nf_hook_thresh(NF_BR_PRE_ROUTING, net, sk, skb,
154: skb->dev, NULL, br_handle_frame_finish);
155:
156: return 0;
157: }
SourceFile:net/bridge/br_netfilter_ipv6.c]
|
| 16/2 |
2026/07/14 01:19 |
llm |
expert |
0m
Model:
Tokens:
input: 22744
output: 31
thoughts: 85
Thoughts: Here's my analysis of the provided code snippet:
**Diving into the IPv6 Pre-Routing Finish**
Okay, so I see that `br_nf_pre_routing_finish_ipv6` kicks off the process with a call to `br_nf_hook_thresh`. This suggests a crucial point in the IPv6 bridging pre-routing stage, likely involving Network Filter (NF) hooks and thresholds. Specifically, it's using the `NF_BR_PRE_ROUTING` hook, which is where we'd expect to see bridge-specific packet manipulations *before* routing decisions are made. It passes the network namespace (`net`), socket (`sk`), socket buffer (`skb`), input device (`skb->dev`), and a `NULL` output device, alongside the `br_handle_frame_finish` callback function. This setup tells me that `br_handle_frame_finish` is the critical function that will be executed after any NF-related processing completes. So, the next logical step is to dive right into understanding what's happening within `br_handle_frame_finish`. Let's see what that function is doing.
|
| 17/2 |
2026/07/14 01:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:br_handle_frame_finish]
Results: map[SourceCode: 75: /* note: already called with rcu_read_lock */
76: int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
77: {
78: enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
79: struct net_bridge_port *p = br_port_get_rcu(skb->dev);
80: enum br_pkt_type pkt_type = BR_PKT_UNICAST;
81: struct net_bridge_fdb_entry *dst = NULL;
82: struct net_bridge_mcast_port *pmctx;
83: struct net_bridge_mdb_entry *mdst;
84: bool local_rcv, mcast_hit = false;
85: struct net_bridge_mcast *brmctx;
86: struct net_bridge_vlan *vlan;
87: struct net_bridge *br;
88: bool promisc;
89: u16 vid = 0;
90: u8 state;
91:
92: if (!p)
93: goto drop;
94:
95: br = p->br;
96:
97: if (br_mst_is_enabled(p)) {
98: state = BR_STATE_FORWARDING;
99: } else {
100: if (p->state == BR_STATE_DISABLED) {
101: reason = SKB_DROP_REASON_BRIDGE_INGRESS_STP_STATE;
102: goto drop;
103: }
104:
105: state = p->state;
106: }
107:
108: brmctx = &p->br->multicast_ctx;
109: pmctx = &p->multicast_ctx;
110: if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid,
111: &state, &vlan))
112: goto out;
113:
114: if (test_bit(BR_PORT_LOCKED_BIT, &p->flags)) {
115: struct net_bridge_fdb_entry *fdb_src =
116: br_fdb_find_rcu(br, eth_hdr(skb)->h_source, vid);
117:
118: if (!fdb_src) {
119: /* FDB miss. Create locked FDB entry if MAB is enabled
120: * and drop the packet.
121: */
122: if (test_bit(BR_PORT_MAB_BIT, &p->flags))
123: br_fdb_update(br, p, eth_hdr(skb)->h_source,
124: vid, BIT(BR_FDB_LOCKED));
125: goto drop;
126: } else if (READ_ONCE(fdb_src->dst) != p ||
127: test_bit(BR_FDB_LOCAL, &fdb_src->flags)) {
128: /* FDB mismatch. Drop the packet without roaming. */
129: goto drop;
130: } else if (test_bit(BR_FDB_LOCKED, &fdb_src->flags)) {
131: /* FDB match, but entry is locked. Refresh it and drop
132: * the packet.
133: */
134: br_fdb_update(br, p, eth_hdr(skb)->h_source, vid,
135: BIT(BR_FDB_LOCKED));
136: goto drop;
137: }
138: }
139:
140: nbp_switchdev_frame_mark(p, skb);
141:
142: /* insert into forwarding database after filtering to avoid spoofing */
143: if (test_bit(BR_LEARNING_BIT, &p->flags))
144: br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, 0);
145:
146: promisc = !!(br->dev->flags & IFF_PROMISC);
147: local_rcv = promisc;
148:
149: if (is_multicast_ether_addr(eth_hdr(skb)->h_dest)) {
150: /* by definition the broadcast is also a multicast address */
151: if (is_broadcast_ether_addr(eth_hdr(skb)->h_dest)) {
152: pkt_type = BR_PKT_BROADCAST;
153: local_rcv = true;
154: } else {
155: pkt_type = BR_PKT_MULTICAST;
156: if (br_multicast_rcv(&brmctx, &pmctx, vlan, skb, vid))
157: goto drop;
158: }
159: }
160:
161: if (state == BR_STATE_LEARNING) {
162: reason = SKB_DROP_REASON_BRIDGE_INGRESS_STP_STATE;
163: goto drop;
164: }
165:
166: BR_INPUT_SKB_CB(skb)->brdev = br->dev;
167: BR_INPUT_SKB_CB(skb)->src_port_isolated = test_bit(BR_ISOLATED_BIT, &p->flags);
168:
169: if (IS_ENABLED(CONFIG_INET) &&
170: (skb->protocol == htons(ETH_P_ARP) ||
171: skb->protocol == htons(ETH_P_RARP))) {
172: br_do_proxy_suppress_arp(skb, br, vid, p);
173: } else if (ipv6_mod_enabled() &&
174: skb->protocol == htons(ETH_P_IPV6) &&
175: br_opt_get(br, BROPT_NEIGH_SUPPRESS_ENABLED) &&
176: pskb_may_pull(skb, sizeof(struct ipv6hdr) +
177: sizeof(struct nd_msg)) &&
178: ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
179: struct nd_msg *msg, _msg;
180:
181: msg = br_is_nd_neigh_msg(skb, &_msg);
182: if (msg)
183: br_do_suppress_nd(skb, br, vid, p, msg);
184: }
185:
186: switch (pkt_type) {
187: case BR_PKT_MULTICAST:
188: mdst = br_mdb_entry_skb_get(brmctx, skb, vid);
189: if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
190: br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst)) {
191: if ((mdst && mdst->host_joined) ||
192: br_multicast_is_router(brmctx, skb) ||
193: br->dev->flags & IFF_ALLMULTI) {
194: local_rcv = true;
195: DEV_STATS_INC(br->dev, multicast);
196: }
197: mcast_hit = true;
198: } else {
199: local_rcv = true;
200: DEV_STATS_INC(br->dev, multicast);
201: }
202: break;
203: case BR_PKT_UNICAST:
204: dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid);
205: if (unlikely(!dst && vid &&
206: br_opt_get(br, BROPT_FDB_LOCAL_VLAN_0))) {
207: dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, 0);
208: if (dst &&
209: (!test_bit(BR_FDB_LOCAL, &dst->flags) ||
210: test_bit(BR_FDB_ADDED_BY_USER, &dst->flags)))
211: dst = NULL;
212: }
213: break;
214: default:
215: break;
216: }
217:
218: if (dst) {
219: unsigned long now = jiffies;
220:
221: if (test_bit(BR_FDB_LOCAL, &dst->flags))
222: return br_pass_frame_up(skb, false);
223:
224: if (now != READ_ONCE(dst->used))
225: WRITE_ONCE(dst->used, now);
226: br_forward(READ_ONCE(dst->dst), skb, local_rcv, false);
227: } else {
228: if (!mcast_hit)
229: br_flood(br, skb, pkt_type, local_rcv, false, vid);
230: else
231: br_multicast_flood(mdst, skb, brmctx, local_rcv, false);
232: }
233:
234: if (local_rcv)
235: return br_pass_frame_up(skb, promisc);
236:
237: out:
238: return 0;
239: drop:
240: kfree_skb_reason(skb, reason);
241: goto out;
242: }
SourceFile:net/bridge/br_input.c]
|
| 18/2 |
2026/07/14 01:20 |
llm |
expert |
0m
Model:
Tokens:
input: 25988
output: 27
thoughts: 42
Thoughts: **Analyzing the Delivery Process**
Okay, so I'm looking at how we handle packet delivery when a specific destination (`dst`) isn't directly known. The crucial flow here hinges on the `br_flood` function. My understanding is that if `dst` isn't in the forwarding table, then `br_flood` gets invoked. That makes perfect sense - it's our fallback mechanism.
Now, `br_flood` is key to understand. My next step will be to thoroughly examine what `br_flood` does, specifically how it interfaces with each port and uses `maybe_deliver` to handle potential traffic. This is where the magic happens, or fails, and I need to be sure I comprehend it fully. I suspect `maybe_deliver` is the function that does the actual work for forwarding traffic and my next step will be to confirm my understanding of this function.
|
| 19/2 |
2026/07/14 01:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:br_flood]
Results: map[SourceCode: 200: /* called under rcu_read_lock */
201: void br_flood(struct net_bridge *br, struct sk_buff *skb,
202: enum br_pkt_type pkt_type, bool local_rcv, bool local_orig,
203: u16 vid)
204: {
205: enum skb_drop_reason reason = SKB_DROP_REASON_NO_TX_TARGET;
206: struct net_bridge_port *prev = NULL;
207: struct net_bridge_port *p;
208:
209: br_tc_skb_miss_set(skb, pkt_type != BR_PKT_BROADCAST);
210:
211: list_for_each_entry_rcu(p, &br->port_list, list) {
212: /* Do not flood unicast traffic to ports that turn it off, nor
213: * other traffic if flood off, except for traffic we originate
214: */
215: switch (pkt_type) {
216: case BR_PKT_UNICAST:
217: if (!test_bit(BR_FLOOD_BIT, &p->flags))
218: continue;
219: break;
220: case BR_PKT_MULTICAST:
221: if (!test_bit(BR_MCAST_FLOOD_BIT, &p->flags) && skb->dev != br->dev)
222: continue;
223: break;
224: case BR_PKT_BROADCAST:
225: if (!test_bit(BR_BCAST_FLOOD_BIT, &p->flags) && skb->dev != br->dev)
226: continue;
227: break;
228: }
229:
230: /* Do not flood to ports that enable proxy ARP */
231: if (test_bit(BR_PROXYARP_BIT, &p->flags))
232: continue;
233: if (BR_INPUT_SKB_CB(skb)->proxyarp_replied) {
234: if (test_bit(BR_PROXYARP_WIFI_BIT, &p->flags))
235: continue;
236: /* For gratuitous ARPs/NAs, check neigh_forward_grat.
237: * For regular ARPs/NDs, check only neigh_suppress.
238: */
239: if (br_is_neigh_suppress_enabled(p, vid) &&
240: (!BR_INPUT_SKB_CB(skb)->grat_arp ||
241: !br_is_neigh_forward_grat_enabled(p, vid)))
242: continue;
243: }
244:
245: prev = maybe_deliver(prev, p, skb, local_orig);
246: if (IS_ERR(prev)) {
247: reason = PTR_ERR(prev) == -ENOMEM ? SKB_DROP_REASON_NOMEM :
248: SKB_DROP_REASON_NOT_SPECIFIED;
249: goto out;
250: }
251: }
252:
253: if (!prev)
254: goto out;
255:
256: if (local_rcv)
257: deliver_clone(prev, skb, local_orig);
258: else
259: __br_forward(prev, skb, local_orig);
260: return;
261:
262: out:
263: if (!local_rcv)
264: kfree_skb_reason(skb, reason);
265: }
SourceFile:net/bridge/br_forward.c]
|
| 20/2 |
2026/07/14 01:20 |
llm |
expert |
0m
Model:
Tokens:
input: 27348
output: 31
thoughts: 393
Thoughts: **Analyzing Potential Causes of RCU Stalls in Bridged Network Traffic**
Okay, so I'm diving into this `br_flood` function and trying to get to the bottom of these RCU stalls. It's clear that the cloning and flooding of packets are the core of the issue. A loop in the bridge is the obvious suspect, with packets bouncing around, but I need to understand the mechanics beyond the basic loop scenario.
Right, so when it comes to loops and flooding, the packets sent out of a veth port are queued on the other side's backlog. That goes through `process_backlog`, which has a quota of only 64 packets. Then `net_rx_action` takes over, handling a larger batch of packets β up to 300 or 2 jiffies before it schedules `ksoftirqd`. But, this still doesn't explain what causes the stalls. Something more synchronous is likely at play here.
Could a packet be sent *synchronously* out of a bridge port and then received immediately? The loopback device is an obvious candidate, but `br_add_if` explicitly rejects it. Aha, so it must be a veth interface on both sides of the bridge! But, `veth_xmit` calls `__dev_forward_skb`, which ultimately calls `netif_rx`. I need to ensure this is asynchronous, which it is.
Okay, what about other possibilities? What if a `dummy` device is part of the bridge? I checked, and `dummy_xmit` just drops the packet, so not it. What if it's a `macvlan` device configured in bridge mode? That might be the culprit. Wait. It uses `netif_rx` or `netif_receive_skb`. So, that shouldn't be synchronous. I should check `macvlan_process_broadcast` for confirmation.
|
| 21/2 |
2026/07/14 01:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:macvlan_process_broadcast]
Results: map[SourceCode: 327: static void macvlan_process_broadcast(struct work_struct *w)
328: {
329: struct macvlan_port *port = container_of(w, struct macvlan_port,
330: bc_work);
331: struct sk_buff *skb;
332: struct sk_buff_head list;
333:
334: __skb_queue_head_init(&list);
335:
336: spin_lock_bh(&port->bc_queue.lock);
337: skb_queue_splice_tail_init(&port->bc_queue, &list);
338: spin_unlock_bh(&port->bc_queue.lock);
339:
340: while ((skb = __skb_dequeue(&list))) {
341: const struct macvlan_dev *src = MACVLAN_SKB_CB(skb)->src;
342:
343: rcu_read_lock();
344: macvlan_multicast_rx(port, src, skb);
345: rcu_read_unlock();
346:
347: if (src)
348: dev_put(src->dev);
349: consume_skb(skb);
350:
351: cond_resched();
352: }
353: }
SourceFile:drivers/net/macvlan.c]
|
| 22/2 |
2026/07/14 01:20 |
llm |
expert |
0m
Model:
Tokens:
input: 28616
output: 28
thoughts: 208
Thoughts: **Analyzing Network Packet Reception on a Bridge**
Okay, so I'm looking at this network packet issue and the first thing that jumps out is the workqueue usage. Interesting... Then my attention is drawn to `bridge_slave_1` and `veth1_to_bridge`. The log clearly flags packets on both these interfaces, specifically mentioning my own MAC address (aa:aa:aa:aa:aa:0c) as the source, which is definitely unusual.
`veth1_to_bridge` is immediately recognizable as a veth device, obviously. But `bridge_slave_1`... that's potentially another veth interface. If that's the case, we're likely dealing with `netif_rx` calls. However, now I'm thinking about the kernel version and how `netif_rx` behaves differently in newer kernels. I believe that it might directly call `netif_receive_skb` when running in a softirq context. Time to double-check the `netif_rx` implementation and confirm this hypothesis.
|
| 23/2 |
2026/07/14 01:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:netif_rx]
Results: map[SourceCode:5755: /**
5756: * netif_rx - post buffer to the network code
5757: * @skb: buffer to post
5758: *
5759: * This function receives a packet from a device driver and queues it for
5760: * the upper (protocol) levels to process via the backlog NAPI device. It
5761: * always succeeds. The buffer may be dropped during processing for
5762: * congestion control or by the protocol layers.
5763: * The network buffer is passed via the backlog NAPI device. Modern NIC
5764: * driver should use NAPI and GRO.
5765: * This function can used from interrupt and from process context. The
5766: * caller from process context must not disable interrupts before invoking
5767: * this function.
5768: *
5769: * return values:
5770: * NET_RX_SUCCESS (no congestion)
5771: * NET_RX_DROP (packet was dropped)
5772: *
5773: */
5774: int netif_rx(struct sk_buff *skb)
5775: {
5776: bool need_bh_off = !(hardirq_count() | softirq_count());
5777: int ret;
5778:
5779: if (need_bh_off)
5780: local_bh_disable();
5781: trace_netif_rx_entry(skb);
5782: ret = netif_rx_internal(skb);
5783: trace_netif_rx_exit(ret);
5784: if (need_bh_off)
5785: local_bh_enable();
5786: return ret;
5787: }
SourceFile:net/core/dev.c]
|
| 24/2 |
2026/07/14 01:20 |
llm |
expert |
0m
Model:
Tokens:
input: 29619
output: 30
thoughts: 12
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Diving into `netif_rx_internal`**
Alright, time to dig into `netif_rx_internal`. I need to get a clear picture of how this function is handling the ingress path. Let's see how it's dealing with the received packets, and how it interacts with the rest of the network stack. I know this is a critical spot for performance and potential bottlenecks, so I want to understand every nuance of its logic. Iβm thinking about what the lock contention issues are likely to be, as well as considering how the interrupt handling relates to this function and its operation. Specifically, I'm going to focus on where the packet is enqueued, how it's processed, and which data structures are involved along the way. I'm also going to check for anything that jumps out at me as a potential point for optimization or a likely source of errors or security vulnerabilities. My main concern is to verify the function's assumptions about its input and output, especially in light of the driver's interface. Let's get to it.
|
| 25/2 |
2026/07/14 01:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:netif_rx_internal]
Results: map[SourceCode:5702: static int netif_rx_internal(struct sk_buff *skb)
5703: {
5704: int ret;
5705:
5706: net_timestamp_check(READ_ONCE(net_hotdata.tstamp_prequeue), skb);
5707:
5708: trace_netif_rx(skb);
5709:
5710: #ifdef CONFIG_RPS
5711: if (static_branch_unlikely(&rps_needed)) {
5712: struct rps_dev_flow voidflow, *rflow = &voidflow;
5713: int cpu;
5714:
5715: rcu_read_lock();
5716:
5717: cpu = get_rps_cpu(skb->dev, skb, &rflow);
5718: if (cpu < 0)
5719: cpu = smp_processor_id();
5720:
5721: ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail);
5722:
5723: rcu_read_unlock();
5724: } else
5725: #endif
5726: {
5727: unsigned int qtail;
5728:
5729: ret = enqueue_to_backlog(skb, smp_processor_id(), &qtail);
5730: }
5731: return ret;
5732: }
SourceFile:net/core/dev.c]
|
| 26/2 |
2026/07/14 01:20 |
llm |
expert |
0m
Model:
Tokens:
input: 30357
output: 30
thoughts: 23
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing the Enqueue Operation**
Right, so we've hit a point where we're calling `enqueue_to_backlog`. My first instinct is to dive into that function and understand its behavior. The name itself suggests we're pushing something onto a backlog, which is typical. But the specifics are always the key. We need to ascertain:
* What *exactly* is being enqueued? Is it a raw task, a pointer, or perhaps a more complex data structure? Data structure types can provide essential cues about possible bottlenecks or design choices.
* What is the underlying data structure of the backlog? Is it a simple queue (FIFO), a priority queue, a work-stealing queue, or something custom? The choice of queue dictates its performance characteristics under various workloads.
* What happens if the enqueue operation fails? Are there retry mechanisms, error logging, or perhaps the task gets routed elsewhere? Failure modes can tell me a lot about the system's robustness.
* Is this a thread-safe operation? If multiple threads might be calling this function concurrently, we need to immediately consider the implications of synchronization (locks, atomic operations, lock-free data structures, etc.). Concurrency bugs are notorious.
* How is the backlog size managed? Are there limits, and how are these limits enforced? Backlogs can become resource-intensive and unmanaged growth is a recipe for trouble.
* Where does this backlog get *consumed*? Knowing the consumer is critical. Is it a dedicated thread pool, a worker process, or something else entirely? The consumer's processing speed relative to the enqueue rate determines whether this is a potential bottleneck.
* And finally, the bigger picture: what's the purpose of the backlog in the overall system design? Understanding the high-level context will help me quickly zero in on any potential issues or inefficiencies.
|
| 27/2 |
2026/07/14 01:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:enqueue_to_backlog]
Results: map[SourceCode:5377: /*
5378: * enqueue_to_backlog is called to queue an skb to a per CPU backlog
5379: * queue (may be a remote CPU queue).
5380: */
5381: static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
5382: unsigned int *qtail)
5383: {
5384: enum skb_drop_reason reason;
5385: struct softnet_data *sd;
5386: unsigned long flags;
5387: unsigned int qlen;
5388: int max_backlog;
5389: u32 tail;
5390:
5391: reason = SKB_DROP_REASON_DEV_READY;
5392:
5393: sd = &per_cpu(softnet_data, cpu);
5394:
5395: qlen = skb_queue_len_lockless(&sd->input_pkt_queue);
5396: max_backlog = READ_ONCE(net_hotdata.max_backlog);
5397: if (unlikely(qlen > max_backlog) ||
5398: skb_flow_limit(skb, qlen, max_backlog))
5399: goto cpu_backlog_drop;
5400: backlog_lock_irq_save(sd, &flags);
5401: qlen = skb_queue_len(&sd->input_pkt_queue);
5402: if (likely(qlen <= max_backlog)) {
5403: if (unlikely(!netif_running(skb->dev))) {
5404: backlog_unlock_irq_restore(sd, flags);
5405: goto bad_dev;
5406: }
5407: if (!qlen) {
5408: /* Schedule NAPI for backlog device. We can use
5409: * non atomic operation as we own the queue lock.
5410: */
5411: if (!__test_and_set_bit(NAPI_STATE_SCHED,
5412: &sd->backlog.state))
5413: napi_schedule_rps(sd);
5414: }
5415: __skb_queue_tail(&sd->input_pkt_queue, skb);
5416: tail = rps_input_queue_tail_incr(sd);
5417: backlog_unlock_irq_restore(sd, flags);
5418:
5419: /* save the tail outside of the critical section */
5420: rps_input_queue_tail_save(qtail, tail);
5421: return NET_RX_SUCCESS;
5422: }
5423:
5424: backlog_unlock_irq_restore(sd, flags);
5425:
5426: cpu_backlog_drop:
5427: reason = SKB_DROP_REASON_CPU_BACKLOG;
5428: numa_drop_add(&sd->drop_counters, 1);
5429: bad_dev:
5430: dev_core_stats_rx_dropped_inc(skb->dev);
5431: kfree_skb_reason(skb, reason);
5432: return NET_RX_DROP;
5433: }
SourceFile:net/core/dev.c]
|
| 28/2 |
2026/07/14 01:20 |
llm |
expert |
0m
Model:
Tokens:
input: 31777
output: 31
thoughts: 431
Thoughts: **Analyzing Potential Bridging Loops**
Okay, so I'm thinking through how packet queuing and potential loops work within the bridging code. `enqueue_to_backlog` is the key here; it's what pushes packets onto the `sd->input_pkt_queue`. That's fine, but the `max_backlog` limit β which defaults to 1000 β is the important bit. A simple bridging loop with veth interfaces will just queue packets up to that limit, and then start dropping them. No infinite loop, no RCU stall.
But wait a minute... what if the bridge port isn't a veth? What if it's *another bridge*? I recall that `br_add_if` explicitly rejects bridging a bridge to another bridge. Good, that's a check. The code prevents that: it explicitly says "Can not enslave a bridge to a bridge". Okay, so that's handled.
But what about a VLAN device *on top* of a bridge? Suppose I create `vlan0` on `bridge0` and `vlan1` on `bridge1`. Then I enslave `vlan0` to `bridge1` and `vlan1` to `bridge0`. Let's trace the packet flow. `br_dev_xmit` sends to the VLAN device. That calls `vlan_dev_hard_start_xmit`, which then calls `dev_queue_xmit` on the underlying device β the other bridge. And *that* `dev_queue_xmit` calls `__dev_queue_xmit`, which finally calls `dev_hard_start_xmit`, and that calls `br_dev_xmit` again. Synchronous loop!
The question now becomes: is `dev_queue_xmit` *really* synchronous? I think that's the crucial detail. `dev_queue_xmit` does call `__dev_queue_xmit`. If the device doesn't have its own queue, like a bridge or a VLAN, then `__dev_queue_xmit` jumps straight to `dev_hard_start_xmit`. That's where I need to look closer.
|
| 29/2 |
2026/07/14 01:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:__dev_queue_xmit]
Results: map[SourceCode:4750: /**
4751: * __dev_queue_xmit() - transmit a buffer
4752: * @skb: buffer to transmit
4753: * @sb_dev: suboordinate device used for L2 forwarding offload
4754: *
4755: * Queue a buffer for transmission to a network device. The caller must
4756: * have set the device and priority and built the buffer before calling
4757: * this function. The function can be called from an interrupt.
4758: *
4759: * When calling this method, interrupts MUST be enabled. This is because
4760: * the BH enable code must have IRQs enabled so that it will not deadlock.
4761: *
4762: * Regardless of the return value, the skb is consumed, so it is currently
4763: * difficult to retry a send to this method. (You can bump the ref count
4764: * before sending to hold a reference for retry if you are careful.)
4765: *
4766: * Return:
4767: * * 0 - buffer successfully transmitted
4768: * * positive qdisc return code - NET_XMIT_DROP etc.
4769: * * negative errno - other errors
4770: */
4771: int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
4772: {
4773: struct net_device *dev = skb->dev;
4774: struct netdev_queue *txq = NULL;
4775: enum skb_drop_reason reason;
4776: int cpu, rc = -ENOMEM;
4777: bool again = false;
4778: struct Qdisc *q;
4779:
4780: skb_reset_mac_header(skb);
4781: skb_assert_len(skb);
4782:
4783: if (unlikely(skb_shinfo(skb)->tx_flags &
4784: (SKBTX_SCHED_TSTAMP | SKBTX_BPF)))
4785: __skb_tstamp_tx(skb, NULL, NULL, skb->sk, SCM_TSTAMP_SCHED);
4786:
4787: reason = qdisc_pkt_len_segs_init(skb);
4788: if (unlikely(reason)) {
4789: dev_core_stats_tx_dropped_inc(dev);
4790: kfree_skb_reason(skb, reason);
4791: return -EINVAL;
4792: }
4793: /* Disable soft irqs for various locks below. Also
4794: * stops preemption for RCU.
4795: */
4796: rcu_read_lock_bh();
4797:
4798: skb_update_prio(skb);
4799:
4800: tcx_set_ingress(skb, false);
4801: #ifdef CONFIG_NET_EGRESS
4802: if (static_branch_unlikely(&egress_needed_key)) {
4803: if (nf_hook_egress_active()) {
4804: skb = nf_hook_egress(skb, &rc, dev);
4805: if (!skb)
4806: goto out;
4807: }
4808:
4809: netdev_xmit_skip_txqueue(false);
4810:
4811: nf_skip_egress(skb, true);
4812: skb = sch_handle_egress(skb, &rc, dev);
4813: if (!skb)
4814: goto out;
4815: nf_skip_egress(skb, false);
4816:
4817: if (netdev_xmit_txqueue_skipped())
4818: txq = netdev_tx_queue_mapping(dev, skb);
4819: }
4820: #endif
4821: /* If device/qdisc don't need skb->dst, release it right now while
4822: * its hot in this cpu cache.
4823: */
4824: if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
4825: skb_dst_drop(skb);
4826: else
4827: skb_dst_force(skb);
4828:
4829: if (!txq)
4830: txq = netdev_core_pick_tx(dev, skb, sb_dev);
4831:
4832: q = rcu_dereference_bh(txq->qdisc);
4833:
4834: trace_net_dev_queue(skb);
4835: if (q->enqueue) {
4836: rc = __dev_xmit_skb(skb, q, dev, txq);
4837: goto out;
4838: }
4839:
4840: /* The device has no queue. Common case for software devices:
4841: * loopback, all the sorts of tunnels...
4842:
4843: * Really, it is unlikely that netif_tx_lock protection is necessary
4844: * here. (f.e. loopback and IP tunnels are clean ignoring statistics
4845: * counters.)
4846: * However, it is possible, that they rely on protection
4847: * made by us here.
4848:
4849: * Check this and shot the lock. It is not prone from deadlocks.
4850: *Either shot noqueue qdisc, it is even simpler 8)
4851: */
4852: if (unlikely(!(dev->flags & IFF_UP))) {
4853: reason = SKB_DROP_REASON_DEV_READY;
4854: goto drop;
4855: }
4856:
4857: cpu = smp_processor_id(); /* ok because BHs are off */
4858:
4859: if (likely(!netif_tx_owned(txq, cpu))) {
4860: bool is_list = false;
4861:
4862: if (dev_xmit_recursion())
4863: goto recursion_alert;
4864:
4865: skb = validate_xmit_skb(skb, dev, &again);
4866: if (IS_ERR_OR_NULL(skb)) {
4867: if (PTR_ERR(skb) == -EINPROGRESS)
4868: rc = NET_XMIT_SUCCESS;
4869: goto out;
4870: }
4871:
4872: HARD_TX_LOCK(dev, txq, cpu);
4873:
4874: if (!netif_xmit_stopped(txq)) {
4875: is_list = !!skb->next;
4876:
4877: dev_xmit_recursion_inc();
4878: skb = dev_hard_start_xmit(skb, dev, txq, &rc);
4879: dev_xmit_recursion_dec();
4880:
4881: /* GSO segments a single SKB into a list of frames.
4882: * TCP expects error to mean none of the data was sent.
4883: */
4884: if (is_list)
4885: rc = NETDEV_TX_OK;
4886: }
4887: HARD_TX_UNLOCK(dev, txq);
4888: if (!skb) /* xmit completed */
4889: goto out;
4890:
4891: net_crit_ratelimited("Virtual device %s asks to queue packet!\n",
4892: dev->name);
4893: /* NETDEV_TX_BUSY or queue was stopped */
4894: if (!is_list)
4895: rc = -ENETDOWN;
4896: } else {
4897: /* Recursion is detected! It is possible unfortunately. */
4898: recursion_alert:
4899: net_crit_ratelimited("Dead loop on virtual device %s (net %llu), fix it urgently!\n",
4900: dev->name, dev_net(dev)->net_cookie);
4901:
4902: rc = -ENETDOWN;
4903: }
4904:
4905: reason = SKB_DROP_REASON_RECURSION_LIMIT;
4906: drop:
4907: rcu_read_unlock_bh();
4908:
4909: dev_core_stats_tx_dropped_inc(dev);
4910: kfree_skb_list_reason(skb, reason);
4911: return rc;
4912: out:
4913: rcu_read_unlock_bh();
4914: return rc;
4915: }
SourceFile:net/core/dev.c]
|
| 30/2 |
2026/07/14 01:20 |
llm |
expert |
0m
Model:
Tokens:
input: 35297
output: 34
thoughts: 1633
Thoughts: **Analyzing a Network Stall: A Deep Dive**
Okay, so I'm looking at this network stall, and here's what I'm thinking. The initial focus is on the recursion check within `__dev_queue_xmit`. It looks like there's a limit, `XMIT_RECURSION_LIMIT` of 8, to prevent packets from endlessly looping synchronously. The code increments the recursion counter, so a packet that loops repeatedly in the same context will eventually be dropped. That much makes sense. I can see the call chain through the bridging code, the VLAN device handling, everything seems to point to `dev_queue_xmit` being called in a controlled way, and thus the recursion limit should prevent synchronous loops.
But then, asynchronous loops come into play. If the loop is asynchronous, say, via a veth pair, the packet gets queued to the backlog. The `process_backlog` function takes over, then `net_rx_action`. `net_rx_action` has limits (packets, or time) before it returns. So far, so good. Then, the system scheduler, with `ksoftirqd` is involved, and it can be preempted by `rcu_preempt`, but they're both regular threads, it should be fine. However, there's a stall message about `rcu_preempt` being starved. That suggests *something* is preventing `rcu_preempt` from running and I have to understand what.
The stall message gives crucial hints: the process is runnable (`->state=0x0`) and on CPU 0. It also shows that the CPU is running `wg_ratelimiter_gc_entries` within a workqueue that's under `system_power_efficient_wq`. Now, inside `wg_ratelimiter_gc_entries`, there's a spinlock, and importantly, `cond_resched()`. That's the key. `cond_resched()` calls `__might_resched()`, which is supposed to yield if there are other runnable tasks. That should have allowed `rcu_preempt` to run. But it *didn't*. The code should be giving up the CPU!
So why wasn't `cond_resched()` yielding? The key is to look at the stack trace. The trace shows a timer interrupt *inside* `__might_resched()`. The interrupt fired, which triggered a bunch of softirqs. Specifically, it seems like `net_rx_action` got called, processing a large number of packets. It's supposed to return after a certain number or a certain amount of time, and it should yield at some point to other runnable processes, correct? Now, if `net_rx_action` is getting stuck in an *internal* loop, the timer interrupt would have allowed execution to return to the process, but the same thing is running again. So I need to go back and check if there's some kind of infinite loop within `net_rx_action`, or maybe within `br_nf_dev_queue_xmit` or `br_handle_frame`!
|
| 31/2 |
2026/07/14 01:21 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:br_nf_dev_queue_xmit]
Results: map[SourceCode: 835: static int br_nf_dev_queue_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
836: {
837: struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
838: unsigned int mtu, mtu_reserved;
839: int ret;
840:
841: mtu_reserved = nf_bridge_mtu_reduction(skb);
842: mtu = skb->dev->mtu;
843:
844: if (nf_bridge->pkt_otherhost) {
845: skb->pkt_type = PACKET_OTHERHOST;
846: nf_bridge->pkt_otherhost = false;
847: }
848:
849: if (nf_bridge->frag_max_size && nf_bridge->frag_max_size < mtu)
850: mtu = nf_bridge->frag_max_size;
851:
852: nf_bridge_update_protocol(skb);
853: nf_bridge_push_encap_header(skb);
854:
855: if (skb_is_gso(skb) || skb->len + mtu_reserved <= mtu) {
856: nf_bridge_info_free(skb);
857: return br_dev_queue_push_xmit(net, sk, skb);
858: }
859:
860: /* Fragmentation on metadata/template dst is not supported */
861: if (unlikely(!skb_valid_dst(skb)))
862: goto drop;
863:
864: /* This is wrong! We should preserve the original fragment
865: * boundaries by preserving frag_list rather than refragmenting.
866: */
867: if (IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) &&
868: skb->protocol == htons(ETH_P_IP)) {
869: struct brnf_frag_data *data;
870:
871: if (br_validate_ipv4(net, skb))
872: goto drop;
873:
874: IPCB(skb)->frag_max_size = nf_bridge->frag_max_size;
875:
876: local_lock_nested_bh(&brnf_frag_data_storage.bh_lock);
877: data = this_cpu_ptr(&brnf_frag_data_storage);
878:
879: if (skb_vlan_tag_present(skb)) {
880: data->vlan_tci = skb->vlan_tci;
881: data->vlan_proto = skb->vlan_proto;
882: } else {
883: data->vlan_proto = 0;
884: }
885:
886: data->encap_size = nf_bridge_encap_header_len(skb);
887: data->size = ETH_HLEN + data->encap_size;
888:
889: skb_copy_from_linear_data_offset(skb, -data->size, data->mac,
890: data->size);
891:
892: ret = br_nf_ip_fragment(net, sk, skb, br_nf_push_frag_xmit);
893: local_unlock_nested_bh(&brnf_frag_data_storage.bh_lock);
894: return ret;
895: }
896: if (IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) &&
897: skb->protocol == htons(ETH_P_IPV6)) {
898: struct brnf_frag_data *data;
899:
900: if (br_validate_ipv6(net, skb))
901: goto drop;
902:
903: IP6CB(skb)->frag_max_size = nf_bridge->frag_max_size;
904:
905: local_lock_nested_bh(&brnf_frag_data_storage.bh_lock);
906: data = this_cpu_ptr(&brnf_frag_data_storage);
907: data->encap_size = nf_bridge_encap_header_len(skb);
908: data->size = ETH_HLEN + data->encap_size;
909:
910: skb_copy_from_linear_data_offset(skb, -data->size, data->mac,
911: data->size);
912:
913: ret = ip6_fragment(net, sk, skb, br_nf_push_frag_xmit);
914: local_unlock_nested_bh(&brnf_frag_data_storage.bh_lock);
915: return ret;
916: }
917: nf_bridge_info_free(skb);
918: return br_dev_queue_push_xmit(net, sk, skb);
919: drop:
920: kfree_skb(skb);
921: return 0;
922: }
SourceFile:net/bridge/br_netfilter_hooks.c]
|
| 32/2 |
2026/07/14 01:21 |
llm |
expert |
0m
Model:
Tokens:
input: 39174
output: 30
thoughts: 1168
Thoughts: **Analyzing a Performance Bottleneck in Bridged Network Traffic**
Okay, here's what's running through my head. I'm looking at this stack trace, and `br_nf_dev_queue_xmit` is calling `nf_bridge_info_free(skb)` and then `br_dev_queue_push_xmit`. That's normal, freeing the `nf_bridge_info` extension. But the problem is this is taking too long.
I need to understand what's making this slow. Could `br_flood` be the culprit? Is it looping over too many ports? A bridge with a ton of ports causes the packet to be cloned and forwarded for each port, calling `__br_forward` many times. But that's not necessarily an infinite loop.
Wait, what about a bridging loop? If a packet gets forwarded to a bridge port that's a veth, and the other end of that veth is in *the same* bridge, the packet comes right back. It's received again, but only in the next `process_backlog` iteration, which should be limited by the `quota`. So that's not the problem.
But what if the other end isn't a veth, but something that creates a *synchronous* loop? Like a macvlan? No, macvlans use a workqueue for broadcasts, so that should be asynchronous. What about a VLAN interface on top of the bridge? If I add a VLAN device on top of `bridge0` to `bridge0` itself...
`br_add_if` checks for this. It calls `netdev_master_upper_dev_get(dev)`. If `dev` is a VLAN on top of `bridge0`, its master won't be set, but its lower device *is* `bridge0`. If I try to add that VLAN device to the bridge, will it work?
Let's check `br_add_if` itself. It calls `netdev_master_upper_dev_link(dev, br->dev, ...)`. This is the loop detection, I knew it! It calls `__netdev_upper_dev_link`, which checks for `netdev_has_upper_dev(upper_dev, dev)`. If `bridge0` has the VLAN as an upper device? No, the VLAN has `bridge0` as a lower device. So `bridge0` has the VLAN as an *upper* device.
If I try to link the VLAN to `bridge0` *as a lower device*, that means the VLAN will have `bridge0` as an upper device! But `bridge0` *already* has the VLAN as an upper device. `__netdev_upper_dev_link` will catch that and return an error. So I can't create a *synchronous* loop like that, that's good!
Okay, so what about a *normal* bridging loop with veths? What if I have `bridge0` and `bridge1`, and have `veth0` in `bridge0`, paired with `veth1` in `bridge1`, and *also* `veth2` in `bridge1`, paired with `veth3` in `bridge0`? That's a classic bridging loop. When a broadcast packet goes out, it goes to `veth0`, then to `veth1`, and `bridge1` floods it to `veth2`, and so on. This should work, but it will be *asynchronous*
Each step goes through `process_backlog` and `net_rx_action`. Since `net_rx_action` has a budget and a time limit, it will yield to `ksoftirqd`. If `ksoftirqd` runs, it will process the packets.
But the stack trace shows the softirq is running in `sysvec_apic_timer_interrupt`! And it's interrupting `wg_ratelimiter_gc_entries` which is running in a workqueue. Okay, that's not good. If `net_rx_action` yields, it sets the `NET_RX_SOFTIRQ` bit again and returns, and `__do_softirq` will loop, right? I need to check `__do_softirq`...
|
| 33/2 |
2026/07/14 01:21 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:__do_softirq]
Results: map[SourceCode: 654: asmlinkage __visible void __softirq_entry __do_softirq(void)
655: {
656: handle_softirqs(false);
657: }
SourceFile:kernel/softirq.c]
|
| 34/2 |
2026/07/14 01:21 |
llm |
expert |
0m
Model:
Tokens:
input: 41395
output: 29
thoughts: 11
Thoughts: Okay, here's my attempt at summarizing the thought process, as if I were thinking it:
**Diving into `handle_softirqs`**
Right, let's take a look at `handle_softirqs`. I need to refresh my understanding of how this crucial function operates. It's the core of soft interrupt handling, so pinpointing the flow and any potential bottlenecks is key. I'll need to remember the interrupt context, how the softirq vectors are processed, and the interactions with the scheduler. I'll mentally map the code in my head, focusing on things like the `irq_work` mechanism, any lock contention that might arise, and potential race conditions. I'm going to want to verify the atomic operations used to protect shared data structures and ensure they are all correctly implemented. Any potential issues with nested softirq calls are of particular interest. Let's see how well it scales under load, the interrupt latency. I want to look at where the priorities are set and how the system responds to changing load conditions. Essentially, I'm thinking about the entire softirq processing chain and mentally simulating how it behaves under varying circumstances.
|
| 35/2 |
2026/07/14 01:21 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:handle_softirqs]
Results: map[SourceCode: 579: static void handle_softirqs(bool ksirqd)
580: {
581: unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
582: unsigned long old_flags = current->flags;
583: int max_restart = MAX_SOFTIRQ_RESTART;
584: struct softirq_action *h;
585: bool in_hardirq;
586: __u32 pending;
587: int softirq_bit;
588:
589: /*
590: * Mask out PF_MEMALLOC as the current task context is borrowed for the
591: * softirq. A softirq handled, such as network RX, might set PF_MEMALLOC
592: * again if the socket is related to swapping.
593: */
594: current->flags &= ~PF_MEMALLOC;
595:
596: pending = local_softirq_pending();
597:
598: softirq_handle_begin();
599: in_hardirq = lockdep_softirq_start();
600: account_softirq_enter(current);
601:
602: restart:
603: /* Reset the pending bitmask before enabling irqs */
604: set_softirq_pending(0);
605:
606: local_irq_enable();
607:
608: h = softirq_vec;
609:
610: while ((softirq_bit = ffs(pending))) {
611: unsigned int vec_nr;
612: int prev_count;
613:
614: h += softirq_bit - 1;
615:
616: vec_nr = h - softirq_vec;
617: prev_count = preempt_count();
618:
619: kstat_incr_softirqs_this_cpu(vec_nr);
620:
621: trace_softirq_entry(vec_nr);
622: h->action();
623: trace_softirq_exit(vec_nr);
624: if (unlikely(prev_count != preempt_count())) {
625: pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",
626: vec_nr, softirq_to_name[vec_nr], h->action,
627: prev_count, preempt_count());
628: preempt_count_set(prev_count);
629: }
630: h++;
631: pending >>= softirq_bit;
632: }
633:
634: if (!IS_ENABLED(CONFIG_PREEMPT_RT) && ksirqd)
635: rcu_softirq_qs();
636:
637: local_irq_disable();
638:
639: pending = local_softirq_pending();
640: if (pending) {
641: if (time_before(jiffies, end) && !need_resched() &&
642: --max_restart)
643: goto restart;
644:
645: wakeup_softirqd();
646: }
647:
648: account_softirq_exit(current);
649: lockdep_softirq_end(in_hardirq);
650: softirq_handle_end();
651: current_restore_flags(old_flags, PF_MEMALLOC);
652: }
SourceFile:kernel/softirq.c]
|
| 36/2 |
2026/07/14 01:21 |
llm |
expert |
0m
Model:
Tokens:
input: 42787
output: 30
thoughts: 392
Thoughts: **Analyzing Softirq and RCU Preempt Starvation**
Okay, let's break this down. My initial observation is that `handle_softirqs` has a loop with a limited number of iterations β up to 10 β or a time limit of 2 milliseconds. Critically, it checks `need_resched()` within this loop. The key is that if `need_resched()` is true, the loop breaks and calls `wakeup_softirqd()`.
Now, here's where it gets interesting, I see that `__might_resched` gets interrupted, and that's the point. If `__might_resched` is interrupted, there's a good chance that `need_resched()` is going to be true. So, `handle_softirqs` breaks, `ksoftirqd` gets woken up, and we exit out of the interrupt context. Back in the normal context, `__might_resched` continues, `cond_resched()` is called which leads to a `schedule()`. This is where the scheduler picks the next task to run.
The core question is why this is relevant. The task that may be picked includes `ksoftirqd` and `rcu_preempt`. I see that `rcu_preempt` was starved for 6347 jiffies! Why? If `ksoftirqd` is running, it's processing softirqs, which is its job. I wonder, does `ksoftirqd` yield to `rcu_preempt`? Both run at `SCHED_NORMAL` priority, unless `rcu_preempt` is boosted. Under heavy softirq load, `ksoftirqd` is going to run a lot. But it's calling `cond_resched()` after each softirq processing loop, so it *should* yield at some point.
Let's check `run_ksoftirqd` to see what we can learn there. This probably points to the root cause of the starvation.
|
| 37/2 |
2026/07/14 01:21 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:run_ksoftirqd]
Results: map[SourceCode:1068: static void run_ksoftirqd(unsigned int cpu)
1069: {
1070: ksoftirqd_run_begin();
1071: if (local_softirq_pending()) {
1072: /*
1073: * We can safely run softirq on inline stack, as we are not deep
1074: * in the task stack here.
1075: */
1076: handle_softirqs(true);
1077: ksoftirqd_run_end();
1078: cond_resched();
1079: return;
1080: }
1081: ksoftirqd_run_end();
1082: }
SourceFile:kernel/softirq.c]
|
| 38/2 |
2026/07/14 01:21 |
llm |
expert |
0m
Model:
Tokens:
input: 43877
output: 29
thoughts: 473
Thoughts: **Analyzing the RCU Preempt Starvation in a Network Loop**
Okay, so I'm trying to figure out why `rcu_preempt` seems to be starved in this network loop. The initial assumption was that `run_ksoftirqd` calls `cond_resched()`, so it *should* yield to `rcu_preempt`, right? If `ksoftirqd` yields, `rcu_preempt` should get its chance. The first thought was hard IRQs could be the culprit, a constant stream of them preventing `rcu_preempt` from running. A packet storm could do that, flooding the CPU. But in this case, the packets are looping internally via veth and bridge, no hard IRQs are involved!
I need to trace the loop itself: `process_backlog` -> `netif_receive_skb` -> `br_handle_frame` -> `br_flood` -> `br_forward` -> `br_dev_queue_push_xmit` -> `dev_queue_xmit` -> `veth_xmit` -> `__dev_forward_skb` -> `netif_rx` -> `enqueue_to_backlog`. `enqueue_to_backlog` queues the packet and schedules NAPI. Then `process_backlog` hits its quota, returning, then `net_rx_action`'s budget is met and it returns, then `handle_softirqs` returns (or loops a few times). *Then* `ksoftirqd` runs, calls `handle_softirqs`, and then `cond_resched()`. So `cond_resched()` *should* be giving `rcu_preempt` a chance!
So, what's going on? Is this loop happening synchronously, preventing the scheduler from getting a chance to preempt? I had a thought about `__dev_queue_xmit`, since it has a recursion limit of 8. But wait... `veth_xmit` doesn't call `dev_queue_xmit`! It calls `__dev_forward_skb` and `netif_rx`! Okay, I need to go back and check `veth_xmit` again. I need to understand that piece better.
|
| 39/2 |
2026/07/14 01:21 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:veth_xmit]
Results: map[SourceCode: 347: static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
348: {
349: struct veth_priv *rcv_priv, *priv = netdev_priv(dev);
350: struct veth_rq *rq = NULL;
351: struct netdev_queue *txq;
352: struct net_device *rcv;
353: int length = skb->len;
354: bool use_napi = false;
355: int ret, rxq;
356:
357: rcu_read_lock();
358: rcv = rcu_dereference(priv->peer);
359: if (unlikely(!rcv) || !pskb_may_pull(skb, ETH_HLEN)) {
360: kfree_skb(skb);
361: goto drop;
362: }
363:
364: rcv_priv = netdev_priv(rcv);
365: rxq = skb_get_queue_mapping(skb);
366: if (rxq < rcv->real_num_rx_queues) {
367: rq = &rcv_priv->rq[rxq];
368:
369: /* The napi pointer is available when an XDP program is
370: * attached or when GRO is enabled
371: * Don't bother with napi/GRO if the skb can't be aggregated
372: */
373: use_napi = rcu_access_pointer(rq->napi) &&
374: veth_skb_is_eligible_for_gro(dev, rcv, skb);
375: }
376:
377: skb_tx_timestamp(skb);
378:
379: ret = veth_forward_skb(rcv, skb, rq, use_napi);
380: switch (ret) {
381: case NET_RX_SUCCESS: /* same as NETDEV_TX_OK */
382: if (!use_napi)
383: dev_sw_netstats_tx_add(dev, 1, length);
384: else
385: __veth_xdp_flush(rq);
386: break;
387: case NETDEV_TX_BUSY:
388: /* If a qdisc is attached to our virtual device, returning
389: * NETDEV_TX_BUSY is allowed.
390: */
391: txq = netdev_get_tx_queue(dev, rxq);
392:
393: if (qdisc_txq_has_no_queue(txq)) {
394: dev_kfree_skb_any(skb);
395: goto drop;
396: }
397: /* Restore Eth hdr pulled by dev_forward_skb/eth_type_trans */
398: __skb_push(skb, ETH_HLEN);
399: netif_tx_stop_queue(txq);
400: /* Makes sure NAPI peer consumer runs. Consumer is responsible
401: * for starting txq again, until then ndo_start_xmit (this
402: * function) will not be invoked by the netstack again.
403: */
404: __veth_xdp_flush(rq);
405: break;
406: case NET_RX_DROP: /* same as NET_XMIT_DROP */
407: drop:
408: atomic64_inc(&priv->dropped);
409: ret = NET_XMIT_DROP;
410: break;
411: default:
412: net_crit_ratelimited("%s(%s): Invalid return code(%d)",
413: __func__, dev->name, ret);
414: }
415: rcu_read_unlock();
416:
417: return ret;
418: }
SourceFile:drivers/net/veth.c]
|
| 40/2 |
2026/07/14 01:21 |
llm |
expert |
0m
Model:
Tokens:
input: 46068
output: 31
thoughts: 13
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person:
**Analyzing `veth_forward_skb`**
Alright, let's dive into this. The mental model I'm building right now involves `veth_forward_skb`. My immediate thought is, "Okay, we're talking about forwarding a socket buffer (SKB) through a veth pair. That means a packet, likely encapsulated, has arrived on one end of a virtual Ethernet link and needs to be pushed out the other side." I'm already anticipating the typical challenges: considering the possibility of the packet's path through the network stack, any potential encapsulation or decapsulation, and the implications of the veth's role within a more complex networking setup like a containerized environment, or even something more complex like a network of virtual machines. Is this function involved in any network namespaces, and if so, how does that affect the overall path? We'll have to consider this at every step. Let's see how the packet is actually moved!
|
| 41/2 |
2026/07/14 01:21 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:veth_forward_skb]
Results: map[SourceCode: 320: static int veth_forward_skb(struct net_device *dev, struct sk_buff *skb,
321: struct veth_rq *rq, bool xdp)
322: {
323: return __dev_forward_skb(dev, skb) ?: xdp ?
324: veth_xdp_rx(rq, skb) :
325: __netif_rx(skb);
326: }
SourceFile:drivers/net/veth.c]
|
| 42/2 |
2026/07/14 01:21 |
llm |
expert |
0m
Model:
Tokens:
input: 46502
output: 29
thoughts: 25
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Diving into the Packet Reception Path**
Alright, so the system's just called `__netif_rx(skb)`. That's where things get interesting, right? This is the core function for receiving a packet, so it's the right place to start digging deeper. I need to understand what's happening at this crucial network interface level. Let's immediately get into how `__netif_rx` handles the packet and where this packet goes next in the reception queue. It's the standard entry point, but there are a hundred edge cases to consider when dealing with network stacks. I want to see the queueing logic and any immediate processing or validation it does.
|
| 43/2 |
2026/07/14 01:21 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:__netif_rx]
Results: map[SourceCode:5734: /**
5735: * __netif_rx - Slightly optimized version of netif_rx
5736: * @skb: buffer to post
5737: *
5738: * This behaves as netif_rx except that it does not disable bottom halves.
5739: * As a result this function may only be invoked from the interrupt context
5740: * (either hard or soft interrupt).
5741: */
5742: int __netif_rx(struct sk_buff *skb)
5743: {
5744: int ret;
5745:
5746: lockdep_assert_once(hardirq_count() | softirq_count());
5747:
5748: trace_netif_rx_entry(skb);
5749: ret = netif_rx_internal(skb);
5750: trace_netif_rx_exit(ret);
5751: return ret;
5752: }
SourceFile:net/core/dev.c]
|
| 44/2 |
2026/07/14 01:22 |
llm |
expert |
0m
Model:
Tokens:
input: 47038
output: 29
thoughts: 1854
Thoughts: **Analyzing a Potential RCU Stall in Network Packet Processing**
Okay, let's break this down. We're looking at a scenario where an RCU (Read-Copy-Update) stall is occurring, and it appears to be related to network packet processing. `netif_rx_internal` kicks things off, ultimately leading to packets being queued to `sd->input_pkt_queue` via `enqueue_to_backlog`. This is an asynchronous process because of the backlog queue, right? If the queue fills up, packets get dropped. That much makes sense.
But why is an asynchronous loop potentially causing an RCU stall? `process_backlog` handles the queued packets, up to a quota, and then `net_rx_action` takes over, processing even more. `net_rx_action` yields to `ksoftirqd` which is meant to process softirqs after a certain budget or time. This is where things get interesting. `ksoftirqd` calls `cond_resched()`, which *should* allow `rcu_preempt` to run. Why isn't this happening?
Wait, what if `ksoftirqd` *isn't* running? If softirqs are processed within the timer interrupt, and given the stack trace, it appears that is the case. This means the softirq processing might be happening during the interrupt return path. If a timer interrupt is happening every 1ms, and it takes 2ms to process the softirq then the next timer interrupt is pending. When `handle_softirqs` returns, it wakes up `ksoftirqd`. But `irq_exit_rcu` returns, and the task resumes. Then `cond_resched()` calls `schedule()`. That means `ksoftirqd` will be picked. So, we're not running into the case where `rcu_preempt` is starved because other tasks are running constantly. But then, what if `wg_ratelimiter_gc_entries` is running with preemption disabled? That isn't the case, `cond_resched()` implies preemption.
The crucial piece is this: a packet storm can mean `ksoftirqd` consumes 100% CPU. If `rcu_preempt` has the same priority (SCHED_NORMAL), it'll share. The question is, does the storm create an *exponential* increase in packets? That seems to be the key to triggering this.
If we have a bridging loop (say, `bridge0` to `bridge1` and back), a broadcast packet floods to both ports on a bridge. Each port sends the packet to the other bridge. In this case, 2 packets are generated for every original one. This turns into a broadcast storm and causes backlog queues to fill up, and packets are dropped. If the queues are constantly full, the system is constantly processing packets. This is a classic broadcast storm DoS. So is this a kernel vulnerability? It looks like if an unprivileged user can trigger this to crash or hang the system, then yes.
Can an unprivileged user create the bridges and veths? Yes, within a user namespace. The key is if `br_add_if` can be called from an unprivileged user in a user namespace. `br_add_if` is called either from an ioctl or netlink. Both require `CAP_NET_ADMIN`. An unprivileged user can create a user and network namespace and gain `CAP_NET_ADMIN` in the new network namespace. Then, they can build the bridging loop, leading to the storm.
Is this a bug that needs fixing in the kernel? STP is there to prevent these loops, but it's disabled by default. A bridging loop *is* expected behavior without STP. The problem is if the host kernel RCU stalls and panics. But the panic is unlikely to occur since `panic_on_rcu_stall` is 0. So, it's just going to generate warnings and run slowly.
Now, the RCU stall message itself is a clue. It says RCU callbacks are not being executed, so memory isn't being freed and we get OOM. It's a known issue with packet storms. So, is there a *specific* bug here, or is it just the generic broadcast storm?
The log entry with `bridge0: received packet on bridge_slave_1 with own address as source address` is a key hint. `br_handle_frame` *should* drop packets where the source MAC is the bridge's own MAC. If we look at the code, it has a check where if a packet with the same source and destination MAC is encountered on the bridge, then it's *supposed* to be dropped and a rate-limited warning is printed
|
| 45/2 |
2026/07/14 01:22 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:br_handle_frame]
Results: map[SourceCode: 335: /*
336: * Return NULL if skb is handled
337: * note: already called with rcu_read_lock
338: */
339: static rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
340: {
341: enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
342: struct net_bridge_port *p;
343: struct sk_buff *skb = *pskb;
344: const unsigned char *dest = eth_hdr(skb)->h_dest;
345:
346: if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
347: return RX_HANDLER_PASS;
348:
349: if (!is_valid_ether_addr(eth_hdr(skb)->h_source)) {
350: reason = SKB_DROP_REASON_MAC_INVALID_SOURCE;
351: goto drop;
352: }
353:
354: skb = skb_share_check(skb, GFP_ATOMIC);
355: if (!skb)
356: return RX_HANDLER_CONSUMED;
357:
358: memset(skb->cb, 0, sizeof(struct br_input_skb_cb));
359: br_tc_skb_miss_set(skb, false);
360:
361: p = br_port_get_rcu(skb->dev);
362: if (test_bit(BR_VLAN_TUNNEL_BIT, &p->flags))
363: br_handle_ingress_vlan_tunnel(skb, p, nbp_vlan_group_rcu(p));
364:
365: if (unlikely(is_link_local_ether_addr(dest))) {
366: u16 fwd_mask = p->br->group_fwd_mask_required;
367:
368: /*
369: * See IEEE 802.1D Table 7-10 Reserved addresses
370: *
371: * Assignment Value
372: * Bridge Group Address 01-80-C2-00-00-00
373: * (MAC Control) 802.3 01-80-C2-00-00-01
374: * (Link Aggregation) 802.3 01-80-C2-00-00-02
375: * 802.1X PAE address 01-80-C2-00-00-03
376: *
377: * 802.1AB LLDP 01-80-C2-00-00-0E
378: *
379: * Others reserved for future standardization
380: */
381: fwd_mask |= p->group_fwd_mask;
382: switch (dest[5]) {
383: case 0x00: /* Bridge Group Address */
384: /* If STP is turned off,
385: then must forward to keep loop detection */
386: if (p->br->stp_enabled == BR_NO_STP ||
387: fwd_mask & (1u << dest[5]))
388: goto forward;
389: *pskb = skb;
390: __br_handle_local_finish(skb);
391: return RX_HANDLER_PASS;
392:
393: case 0x01: /* IEEE MAC (Pause) */
394: reason = SKB_DROP_REASON_MAC_IEEE_MAC_CONTROL;
395: goto drop;
396:
397: case 0x0E: /* 802.1AB LLDP */
398: fwd_mask |= p->br->group_fwd_mask;
399: if (fwd_mask & (1u << dest[5]))
400: goto forward;
401: *pskb = skb;
402: __br_handle_local_finish(skb);
403: return RX_HANDLER_PASS;
404:
405: default:
406: /* Allow selective forwarding for most other protocols */
407: fwd_mask |= p->br->group_fwd_mask;
408: if (fwd_mask & (1u << dest[5]))
409: goto forward;
410: }
411:
412: BR_INPUT_SKB_CB(skb)->promisc = false;
413:
414: /* The else clause should be hit when nf_hook():
415: * - returns < 0 (drop/error)
416: * - returns = 0 (stolen/nf_queue)
417: * Thus return 1 from the okfn() to signal the skb is ok to pass
418: */
419: if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN,
420: dev_net(skb->dev), NULL, skb, skb->dev, NULL,
421: br_handle_local_finish) == 1) {
422: return RX_HANDLER_PASS;
423: } else {
424: return RX_HANDLER_CONSUMED;
425: }
426: }
427:
428: if (unlikely(br_process_frame_type(p, skb)))
429: return RX_HANDLER_PASS;
430:
431: forward:
432: if (br_mst_is_enabled(p))
433: goto defer_stp_filtering;
434:
435: switch (p->state) {
436: case BR_STATE_FORWARDING:
437: case BR_STATE_LEARNING:
438: defer_stp_filtering:
439: if (ether_addr_equal(p->br->dev->dev_addr, dest))
440: skb->pkt_type = PACKET_HOST;
441:
442: return nf_hook_bridge_pre(skb, pskb);
443: default:
444: reason = SKB_DROP_REASON_BRIDGE_INGRESS_STP_STATE;
445: drop:
446: kfree_skb_reason(skb, reason);
447: }
448: return RX_HANDLER_CONSUMED;
449: }
SourceFile:net/bridge/br_input.c]
|
| 46/2 |
2026/07/14 01:22 |
llm |
expert |
0m
Model:
Tokens:
input: 51895
output: 31
thoughts: 42
Thoughts: **Debugging a Subtle Warning**
Okay, hold on a second. I need to figure out *where* that darn warning message is actually being printed. It seems familiar, but I'm not seeing it right away in the obvious places. Let me retrace my steps... Ah ha! The lightbulb just came on! I seem to recall now, I think the issue originates in `br_handle_frame_finish`. I'll have to go back and take a look at `br_handle_frame_finish` again, and make sure I'm not missing anything.
|
| 47/2 |
2026/07/14 01:22 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:br_handle_frame_finish]
Results: map[SourceCode: 75: /* note: already called with rcu_read_lock */
76: int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
77: {
78: enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
79: struct net_bridge_port *p = br_port_get_rcu(skb->dev);
80: enum br_pkt_type pkt_type = BR_PKT_UNICAST;
81: struct net_bridge_fdb_entry *dst = NULL;
82: struct net_bridge_mcast_port *pmctx;
83: struct net_bridge_mdb_entry *mdst;
84: bool local_rcv, mcast_hit = false;
85: struct net_bridge_mcast *brmctx;
86: struct net_bridge_vlan *vlan;
87: struct net_bridge *br;
88: bool promisc;
89: u16 vid = 0;
90: u8 state;
91:
92: if (!p)
93: goto drop;
94:
95: br = p->br;
96:
97: if (br_mst_is_enabled(p)) {
98: state = BR_STATE_FORWARDING;
99: } else {
100: if (p->state == BR_STATE_DISABLED) {
101: reason = SKB_DROP_REASON_BRIDGE_INGRESS_STP_STATE;
102: goto drop;
103: }
104:
105: state = p->state;
106: }
107:
108: brmctx = &p->br->multicast_ctx;
109: pmctx = &p->multicast_ctx;
110: if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid,
111: &state, &vlan))
112: goto out;
113:
114: if (test_bit(BR_PORT_LOCKED_BIT, &p->flags)) {
115: struct net_bridge_fdb_entry *fdb_src =
116: br_fdb_find_rcu(br, eth_hdr(skb)->h_source, vid);
117:
118: if (!fdb_src) {
119: /* FDB miss. Create locked FDB entry if MAB is enabled
120: * and drop the packet.
121: */
122: if (test_bit(BR_PORT_MAB_BIT, &p->flags))
123: br_fdb_update(br, p, eth_hdr(skb)->h_source,
124: vid, BIT(BR_FDB_LOCKED));
125: goto drop;
126: } else if (READ_ONCE(fdb_src->dst) != p ||
127: test_bit(BR_FDB_LOCAL, &fdb_src->flags)) {
128: /* FDB mismatch. Drop the packet without roaming. */
129: goto drop;
130: } else if (test_bit(BR_FDB_LOCKED, &fdb_src->flags)) {
131: /* FDB match, but entry is locked. Refresh it and drop
132: * the packet.
133: */
134: br_fdb_update(br, p, eth_hdr(skb)->h_source, vid,
135: BIT(BR_FDB_LOCKED));
136: goto drop;
137: }
138: }
139:
140: nbp_switchdev_frame_mark(p, skb);
141:
142: /* insert into forwarding database after filtering to avoid spoofing */
143: if (test_bit(BR_LEARNING_BIT, &p->flags))
144: br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, 0);
145:
146: promisc = !!(br->dev->flags & IFF_PROMISC);
147: local_rcv = promisc;
148:
149: if (is_multicast_ether_addr(eth_hdr(skb)->h_dest)) {
150: /* by definition the broadcast is also a multicast address */
151: if (is_broadcast_ether_addr(eth_hdr(skb)->h_dest)) {
152: pkt_type = BR_PKT_BROADCAST;
153: local_rcv = true;
154: } else {
155: pkt_type = BR_PKT_MULTICAST;
156: if (br_multicast_rcv(&brmctx, &pmctx, vlan, skb, vid))
157: goto drop;
158: }
159: }
160:
161: if (state == BR_STATE_LEARNING) {
162: reason = SKB_DROP_REASON_BRIDGE_INGRESS_STP_STATE;
163: goto drop;
164: }
165:
166: BR_INPUT_SKB_CB(skb)->brdev = br->dev;
167: BR_INPUT_SKB_CB(skb)->src_port_isolated = test_bit(BR_ISOLATED_BIT, &p->flags);
168:
169: if (IS_ENABLED(CONFIG_INET) &&
170: (skb->protocol == htons(ETH_P_ARP) ||
171: skb->protocol == htons(ETH_P_RARP))) {
172: br_do_proxy_suppress_arp(skb, br, vid, p);
173: } else if (ipv6_mod_enabled() &&
174: skb->protocol == htons(ETH_P_IPV6) &&
175: br_opt_get(br, BROPT_NEIGH_SUPPRESS_ENABLED) &&
176: pskb_may_pull(skb, sizeof(struct ipv6hdr) +
177: sizeof(struct nd_msg)) &&
178: ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
179: struct nd_msg *msg, _msg;
180:
181: msg = br_is_nd_neigh_msg(skb, &_msg);
182: if (msg)
183: br_do_suppress_nd(skb, br, vid, p, msg);
184: }
185:
186: switch (pkt_type) {
187: case BR_PKT_MULTICAST:
188: mdst = br_mdb_entry_skb_get(brmctx, skb, vid);
189: if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
190: br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst)) {
191: if ((mdst && mdst->host_joined) ||
192: br_multicast_is_router(brmctx, skb) ||
193: br->dev->flags & IFF_ALLMULTI) {
194: local_rcv = true;
195: DEV_STATS_INC(br->dev, multicast);
196: }
197: mcast_hit = true;
198: } else {
199: local_rcv = true;
200: DEV_STATS_INC(br->dev, multicast);
201: }
202: break;
203: case BR_PKT_UNICAST:
204: dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid);
205: if (unlikely(!dst && vid &&
206: br_opt_get(br, BROPT_FDB_LOCAL_VLAN_0))) {
207: dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, 0);
208: if (dst &&
209: (!test_bit(BR_FDB_LOCAL, &dst->flags) ||
210: test_bit(BR_FDB_ADDED_BY_USER, &dst->flags)))
211: dst = NULL;
212: }
213: break;
214: default:
215: break;
216: }
217:
218: if (dst) {
219: unsigned long now = jiffies;
220:
221: if (test_bit(BR_FDB_LOCAL, &dst->flags))
222: return br_pass_frame_up(skb, false);
223:
224: if (now != READ_ONCE(dst->used))
225: WRITE_ONCE(dst->used, now);
226: br_forward(READ_ONCE(dst->dst), skb, local_rcv, false);
227: } else {
228: if (!mcast_hit)
229: br_flood(br, skb, pkt_type, local_rcv, false, vid);
230: else
231: br_multicast_flood(mdst, skb, brmctx, local_rcv, false);
232: }
233:
234: if (local_rcv)
235: return br_pass_frame_up(skb, promisc);
236:
237: out:
238: return 0;
239: drop:
240: kfree_skb_reason(skb, reason);
241: goto out;
242: }
SourceFile:net/bridge/br_input.c]
|
| 48/2 |
2026/07/14 01:22 |
llm |
expert |
0m
Model:
Tokens:
input: 54972
output: 24
thoughts: 41
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience:
**Debugging a Network Frame Handling Issue**
Alright, I'm digging into this `br_handle_frame_finish` function, and I'm not seeing the specific warning I was expecting. It's odd, because I know there's a problem, so the system *should* be logging something. Let's try a different approach. Instead of assuming the warning is present, let's search the codebase explicitly for that warning message. I'm going to look for the exact string, "received packet on %s with own address as source address". This should allow me to pinpoint where the message *should* be generated, assuming it actually is. Maybe I missed something in the initial glance, or perhaps the log level is set too low for that specific message. This will allow me to narrow down where things might be going sideways.
|
| 49/2 |
2026/07/14 01:22 |
tool |
grepper |
0m
Args: map[Expression:received packet on .* with own address as source address]
Results: map[Output:net/bridge/br_fdb.c=973=void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
--
net/bridge/br_fdb.c-986- if (net_ratelimit())
net/bridge/br_fdb.c:987: br_warn(br, "received packet on %s with own address as source address (addr:%pM, vlan:%u)\n",
net/bridge/br_fdb.c-988- source->dev->name, addr, vid);
]
|
| 50/2 |
2026/07/14 01:22 |
llm |
expert |
0m
Model:
Tokens:
input: 55385
output: 30
thoughts: 28
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, and written as if they were my own immediate internal monologue:
**Quick! Let's Track Down That Update**
Right, so I've got to find out what's updating. My gut tells me this particular change is happening within the `br_fdb_update` function. Itβs got that feel.
So, let's just go straight to the source. Time to dig into the `br_fdb_update` function and see what's what. I need to understand the control flow here, understand all of the branches, and get to the bottom of the implications for the overall data structure. The dependencies are likely a web of linked elements, so I need to know where this is called from, and what else might be touched along the way.
|
| 51/2 |
2026/07/14 01:22 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:br_fdb_update]
Results: map[SourceCode: 973: void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
974: const unsigned char *addr, u16 vid, unsigned long flags)
975: {
976: struct net_bridge_fdb_entry *fdb;
977:
978: /* some users want to always flood. */
979: if (hold_time(br) == 0)
980: return;
981:
982: fdb = fdb_find_rcu(&br->fdb_hash_tbl, addr, vid);
983: if (likely(fdb)) {
984: /* attempt to update an entry for a local interface */
985: if (unlikely(test_bit(BR_FDB_LOCAL, &fdb->flags))) {
986: if (net_ratelimit())
987: br_warn(br, "received packet on %s with own address as source address (addr:%pM, vlan:%u)\n",
988: source->dev->name, addr, vid);
989: } else {
990: unsigned long now = jiffies;
991: bool fdb_modified = false;
992:
993: if (now != READ_ONCE(fdb->updated)) {
994: WRITE_ONCE(fdb->updated, now);
995: fdb_modified = __fdb_mark_active(fdb);
996: }
997:
998: /* fastpath: update of existing entry */
999: if (unlikely(source != READ_ONCE(fdb->dst) &&
1000: !test_bit(BR_FDB_STICKY, &fdb->flags))) {
1001: br_switchdev_fdb_notify(br, fdb, RTM_DELNEIGH);
1002: WRITE_ONCE(fdb->dst, source);
1003: fdb_modified = true;
1004: /* Take over HW learned entry */
1005: if (unlikely(test_bit(BR_FDB_ADDED_BY_EXT_LEARN,
1006: &fdb->flags)))
1007: clear_bit(BR_FDB_ADDED_BY_EXT_LEARN,
1008: &fdb->flags);
1009: /* Clear locked flag when roaming to an
1010: * unlocked port.
1011: */
1012: if (unlikely(test_bit(BR_FDB_LOCKED, &fdb->flags)))
1013: clear_bit(BR_FDB_LOCKED, &fdb->flags);
1014: }
1015:
1016: if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags))) {
1017: set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
1018: if (test_and_clear_bit(BR_FDB_DYNAMIC_LEARNED,
1019: &fdb->flags))
1020: atomic_dec(&br->fdb_n_learned);
1021: }
1022: if (unlikely(fdb_modified)) {
1023: trace_br_fdb_update(br, source, addr, vid, flags);
1024: fdb_notify(br, fdb, RTM_NEWNEIGH, true);
1025: }
1026: }
1027: } else {
1028: spin_lock(&br->hash_lock);
1029: fdb = fdb_create(br, source, addr, vid, flags);
1030: if (fdb) {
1031: trace_br_fdb_update(br, source, addr, vid, flags);
1032: fdb_notify(br, fdb, RTM_NEWNEIGH, true);
1033: }
1034: /* else we lose race and someone else inserts
1035: * it first, don't bother updating
1036: */
1037: spin_unlock(&br->hash_lock);
1038: }
1039: }
SourceFile:net/bridge/br_fdb.c]
|
| 52/2 |
2026/07/14 01:22 |
llm |
expert |
0m
Model:
Tokens:
input: 56831
output: 31
thoughts: 2642
Thoughts: **Analysis of a Bridging Loop Denial of Service Vulnerability**
Okay, so here's what I've got. This `br_fdb_update` warning triggered by a packet with a source MAC matching a local FDB entry is the key. It means a packet is arriving with the bridge's own MAC as the source, and it's *not* being dropped β it's being forwarded, likely due to a broadcast or unknown destination. This suggests a bridging loop is happening. If a packet comes in with a source mac matching the brige, the packet is forwarded and keeps getting looped around and around!
The looped packet with the bridge MAC will cause this warning to be continually logged and the repeated packet will lead to a broadcast storm, because of this loop. This is a classic bridging loop Denial of Service (DoS) situation. The storm then pushes the CPU to 100% within the `softirq` context, which starves `rcu_preempt` and results in an RCU stall.
From a security perspective, this is a network misconfiguration, but the syzkaller reproducer makes it concerning. The syzkaller tool can craft user namespaces, bridges, and virtual ethernet interfaces to create the loop, allowing an unprivileged user (with a user namespace) to trigger a DoS on the host. I believe the exploit does *not* need the user namespace, because bridge creation and interface addition need `CAP_NET_ADMIN`.
To summarize:
* **Denial of Service:** Yes
* **Exploitable:** No
* **Accessible From Unprivileged Processes:** No (requires `CAP_NET_ADMIN`)
* **Accessible From User Namespaces:** Yes
* **VM Guest Trigger:** Yes
* **VM Host Trigger:** No
* **Ethernet Network Trigger:** Yes (a physical bridging loop can be caused by external packets)
* **Other Remote Trigger:** No
* **Peripheral Trigger:** Yes (plugging a loopback cable/looping switch)
* **Malicious Filesystem Trigger:** No
I also need to check for synchronous loops, but the code shows it is asynchronous. The issue becomes clear with an asynchronous loop causing an RCU stall, indicating that the softirq processing is not yielding. Either it is not yielding at all, or the sheer volume of packets is overwhelming the system. The backlog queues are full, so the softirq processing loop will always be busy.
The system will execute `ksoftirqd`, and if this is the only runnable process, `cond_resched` should eventually pick up `rcu_preempt`! However, the trace says `rcu_preempt` is starved. CPU 0 is the issue here. But what's running on CPU 0? Wait, `wg_ratelimiter_gc_entries` calls `cond_resched()`. This function loops and makes `cond_resched()` calls! It locks, does some work, unlocks, and calls `cond_resched()`.
The important bit is that timer interrupts can happen during that loop. Now, the `sysvec_apic_timer_interrupt` function is called and processes softirqs when the system's `irq_exit_rcu` triggers `invoke_softirq`. Here's the catch: If a flood is happening, `invoke_softirq` runs for some time. Then it returns to the loop in `wg_ratelimiter_gc_entries`. `cond_resched()` gets called and in turn calls `schedule()`. But what is `schedule()` is interrupted before it runs?
The stack trace shows the timer interrupt happened *inside* `__might_resched`. This means after softirqs finish, a pending timer interrupt can immediately interrupt task context *before* the scheduler has a chance to run. If `invoke_softirq` is interrupted by a timer while it is in the softirq stack `in_interrupt()` would be true. But after the stack is exited and the function returns, interrupts can be re-enabled and now `invoke_softirq` gets called again!
This means the system never gets out of softirq context. This is what's known as a "softirq storm" or "softirq livelock." The task context β and especially the scheduler β never runs because the system is constantly being interrupted by timer interrupts, meaning the timer runs again right away! The constant flooding of softirq packets will starve the system. That's why `rcu_preempt` is starved, even though `cond_resched()` is called. This flood/storm does not let other process context execute.
|
| 53/2 |
2026/07/14 01:23 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:irq_exit_rcu]
Results: map[SourceCode: 745: /**
746: * irq_exit_rcu() - Exit an interrupt context without updating RCU
747: *
748: * Also processes softirqs if needed and possible.
749: */
750: void irq_exit_rcu(void)
751: {
752: __irq_exit_rcu();
753: /* must be last! */
754: lockdep_hardirq_exit();
755: }
SourceFile:kernel/softirq.c]
|
| 54/2 |
2026/07/14 01:23 |
llm |
expert |
0m
Model:
Tokens:
input: 60680
output: 32
thoughts: 14
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person:
**Investigating `__irq_exit_rcu`**
Alright, time to dive into `__irq_exit_rcu`. I need to refresh my understanding of its role within the RCU (Read-Copy-Update) context. My initial thought is to figure out the exact point it's inserted in the interrupt handling flow, and why it's there. I'm assuming it has something to do with the graceful exit from an interrupt context, probably relating to deferred grace periods. I want to confirm whether it acts as a synchronization point, a point for deferred work to be queued, or both. Let's see if this function is related to the critical section protection and if it ensures the proper completion of RCU read-side critical sections. My intuition leans toward it being crucial for ensuring that preemption within interrupts won't corrupt RCU's integrity. I need to dissect the code itself, tracing its calls and understanding its relationship with the RCU callbacks and grace period mechanisms. I should also check how it interfaces with the various RCU flavors, if any. I bet there are some subtleties regarding nested interrupts or preemptible interrupts. Time to dig in!
|
| 55/2 |
2026/07/14 01:23 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:__irq_exit_rcu]
Results: map[SourceCode: 720: static inline void __irq_exit_rcu(void)
721: {
722: #ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED
723: local_irq_disable();
724: #else
725: lockdep_assert_irqs_disabled();
726: #endif
727: account_hardirq_exit(current);
728: preempt_count_sub(HARDIRQ_OFFSET);
729: if (!in_interrupt() && local_softirq_pending()) {
730: /*
731: * If we left hrtimers unarmed, make sure to arm them now,
732: * before enabling interrupts to run SoftIRQ.
733: */
734: hrtimer_rearm_deferred();
735: invoke_softirq();
736: }
737:
738: if (IS_ENABLED(CONFIG_IRQ_FORCED_THREADING) && force_irqthreads() &&
739: local_timers_pending_force_th() && !(in_nmi() | in_hardirq()))
740: wake_timersd();
741:
742: tick_irq_exit();
743: }
SourceFile:kernel/softirq.c]
|
| 56/2 |
2026/07/14 01:23 |
llm |
expert |
0m
Model:
Tokens:
input: 61385
output: 29
thoughts: 476
Thoughts: **The Softirq Livlock Problem**
Okay, here's what's hitting me. I've been tracing the logic and I've nailed down a nasty softirq issue. It stems from how `__irq_exit_rcu` interacts with deferred softirq processing. It checks if we're not in an interrupt and if `local_softirq_pending()` is true. And it looks like, when `handle_softirqs` defers to `ksoftirqd`, it *leaves* that `local_softirq_pending()` flag set!
So, imagine this: the next timer interrupt triggers, it sees `local_softirq_pending()` is true, and boom, it calls `invoke_softirq()` *again*. Hang on... does `ksoftirqd` clear `local_softirq_pending()` when it wakes up and processes the work? No, it handles them while it runs. But the key is that `ksoftirqd` *hasn't* run yet, because we're still in the original interrupted task (`wg_ratelimiter_gc_entries` in this case), and that flag is *still* set.
This means *every* subsequent timer interrupt is going to call `invoke_softirq()`, which in turn calls `handle_softirqs()`. And here's the kicker: `handle_softirqs()` processes for, say, 2ms, and *then* defers to `ksoftirqd` *again*, leaving `local_softirq_pending()` set. And *then* it returns. The timer interrupt interval is 1ms (HZ=1000).
Because `handle_softirqs` takes 2ms, the next interrupt is already pending! As soon as we call `local_irq_enable()` (say, in the task context), the timer interrupt fires immediately! It goes through `__irq_exit_rcu`, finds `local_softirq_pending()`, and calls `invoke_softirq()` *again*!
This is a complete 100% CPU livelock! The task context will NEVER get to execute a single instruction. Each time it comes back from an interrupt, another is pending, and spends 2ms processing softirq, guaranteeing the next one is pending too.
I know this, this is a known issue in the Linux kernel softirq design. I've seen it addressed partially by patches, but in some kernels, it still shows its ugly head.
Wait, I need to double-check something, there's a check in `invoke_softirq`...Let's dig into that `invoke_softirq` code.
|
| 57/2 |
2026/07/14 01:23 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile: Name:invoke_softirq]
Results: map[SourceCode: 487: static inline void invoke_softirq(void)
488: {
489: if (!force_irqthreads() || !__this_cpu_read(ksoftirqd)) {
490: #ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK
491: /*
492: * We can safely execute softirq on the current stack if
493: * it is the irq stack, because it should be near empty
494: * at this stage.
495: */
496: __do_softirq();
497: #else
498: /*
499: * Otherwise, irq_exit() is called on the task stack that can
500: * be potentially deep already. So call softirq in its own stack
501: * to prevent from any overrun.
502: */
503: do_softirq_own_stack();
504: #endif
505: } else {
506: wakeup_softirqd();
507: }
508: }
SourceFile:kernel/softirq.c]
|
| 58/2 |
2026/07/14 01:23 |
llm |
expert |
0m
Model:
Tokens:
input: 62822
output: 66
thoughts: 1760
Thoughts: **Analysis of the Softirq Livelock Vulnerability**
Okay, so `invoke_softirq` directly calls `__do_softirq()`. Hmm, no check if `ksoftirqd` is already active? Nope, just checks for `force_irqthreads()`. Meaning even if `ksoftirqd` is up and running, `invoke_softirq` will *still* call `__do_softirq()`! That's it β this confirms the livelock scenario. A broadcast storm will continuously trigger the timer interrupt, which calls `__do_softirq()`, starving the task context, *especially* starving `rcu_preempt`. Ah, this explains why `rcu_preempt` stalls! It's a task that's not getting CPU time. Classic Denial of Service.
Can an unprivileged user exploit this? Looking back, `br_add_if` needs `CAP_NET_ADMIN`, but an unprivileged user *can* create a user namespace, then a network namespace inside that, and get `CAP_NET_ADMIN` in the new network namespace. Then, they can set up a bridge and virtual Ethernet interfaces (veths), and create a loop. Sending a broadcast packet kicks off the storm, causing the softirq livelock on the host CPU. This starves `rcu_preempt`, leads to an RCU stall, and potentially OOM or even a kernel panic. So, a DoS vulnerability accessible from user namespaces.
Let's quickly confirm whether `unprivileged_userns_clone` is enabled by default. Yes, in most modern distributions, unprivileged user namespaces are accessible.
The prompt requires:
* "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... Assume that user namespaces are not accessible..."
* "Accessible From User Namespaces: Determine if the bug can be reached within a user-namespace where the process has all capabilities..."
For "Accessible From Unprivileged Processes", the answer is NO because `CAP_NET_ADMIN` is needed.
For "Accessible From User Namespaces", the answer is YES because we can achieve this with a user namespace.
Is this exploitable in any way besides DoS? No, it's a CPU livelock. No memory corruption or information leak.
VM Guest Trigger? Yes, a user inside a VM guest can trigger it in the guest kernel.
VM Host Trigger in the Confidential Computing Context? No, it's a local network stack issue and doesn't involve the host.
Ethernet Network Trigger? Yes, an existing bridging loop on physical Ethernet interfaces (or even just an improperly configured switch) will allow a broadcast packet to trigger the storm.
Peripheral Trigger? Yes, plugging a loopback cable or a misconfigured switch into a physical port creates a bridging loop and causes the storm.
Malicious Filesystem Trigger? No.
Other Remote Trigger? No.
Okay, let me review the questions and my answers:
* Exploitable: No
* Denial Of Service: Yes
* Accessible From Unprivileged Processes: No
* Accessible From User Namespaces: Yes
* VM Guest Trigger: Yes
* VM Host Trigger: No
* Ethernet Network Trigger: Yes
* Other Remote Trigger: No
* Peripheral Trigger: Yes
* Malicious Filesystem Trigger: No
Wait, any *other* way to trigger this *without* user namespaces? Can an unprivileged user create a bridging loop *without* `CAP_NET_ADMIN`? No, creating bridges and adding interfaces *requires* `CAP_NET_ADMIN`. Can an unprivileged user send a packet that causes a loop on an *existing* bridge? If an administrator *already* messed up and set up a bridging loop (which is a misconfiguration), then *anyone* can send a broadcast packet to trigger the storm. But the vulnerability is the ability to *create* the loop and crash the system. If the loop is already there, the network is already broken. So the primary attack vector is creating the loop via user namespaces.
Let's quickly check for any memory corruption. The stack trace shows `kasan_slab_free` and `kmem_cache_free`, but it's just normal freeing of the `nf_bridge_info` extension when the packet is transmitted. No use-after-free or out-of-bounds access. The report is an RCU stall. It confirms DoS, not memory corruption.
Any *other* way to trigger this? What if `br_nf_dev_queue_xmit` has a bug that creates an infinite loop for a single packet?
```c
if (skb
|
| 59/2 |
2026/07/14 01:23 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:true PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:true VMGuestTrigger:true VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:true PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:true VMGuestTrigger:true VMHostTrigger:false]
|
| 60/2 |
2026/07/14 01:23 |
llm |
expert |
0m
Model:
Tokens:
input: 65737
output: 614
thoughts: 247
Thoughts: **My Analysis of the RCU Stall Bug**
Okay, the data's in. Time to synthesize my final assessment. This is an RCU stall, plain and simple, and the root cause lies in a nasty softirq livelock. The trigger? A bridging loop β a classic broadcast storm scenario. It's a textbook example, really. That loop generates an avalanche of packets, overloading the backlog queues and resulting in an exponential increase in packets.
`net_rx_action` is the unlucky function trying to process all these packets. Even though it's supposed to yield after hitting its time or work limit, the problem is the timer interrupt. It relentlessly interrupts the task context and re-enters the softirq processing (specifically, `invoke_softirq` called from `irq_exit_rcu`). This keeps the `net_rx_action` busy and prevents the task context, and consequently `rcu_preempt`, from getting any CPU time. This, of course, is what leads to the RCU stall and the potential for an out-of-memory condition or even a kernel panic.
The really interesting aspect, from an attack surface perspective, is that an unprivileged user can trigger this. All they need to do is set up a network namespace, create a bridge and veth interfaces, and then form a loop β classic DoS vector. However, while it is accessible from user namespaces, it's *not* a privilege escalation bug, nor does it lead to memory corruption. Which is a relief.
And, of course, this isn't just limited to artificially created loops. It can also be triggered by a genuine bridging loop physically present on the network, or even by plugging in a loopback cable β so, it's not just a software issue, itβs a network configuration one as well.
|
| 61/1 |
2026/07/14 01:24 |
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:
**Bug Mechanism and Impact:**
The bug report indicates an RCU stall (`rcu_preempt kthread starved`)
accompanied by repeated warnings from the bridge subsystem (`bridge0: received
packet on ... with own address as source address`).
This is the classic symptom of a **bridging loop** causing a **broadcast
storm**. When a bridging loop is created (e.g., connecting two bridges together
via multiple `veth` pairs) and Spanning Tree Protocol (STP) is disabled (which
is the default), broadcast packets are continuously flooded and multiplied
across the loop.
This exponential growth of packets keeps the network backlog queues constantly
full. The kernel processes these packets in the softirq context
(`net_rx_action`). Although `net_rx_action` has a budget and time limit to yield
to `ksoftirqd` and task context, the sheer volume of packets causes a **softirq
livelock**. Every time the CPU returns to task context, the next timer interrupt
fires almost immediately, and upon exiting the interrupt (`irq_exit_rcu`), the
kernel sees pending softirqs and processes them again on the spot. This
completely starves normal tasks, including the `rcu_preempt` kthread, leading to
an RCU stall, system unresponsiveness, and eventually an Out-Of-Memory (OOM)
condition or kernel panic.
**Exploitability:**
This is a pure resource exhaustion issue (CPU livelock). There is no memory
corruption, use-after-free, out-of-bounds access, or information leak involved.
Therefore, it is **not exploitable** for privilege escalation or arbitrary code
execution, but it is a highly effective **Denial of Service (DoS)**.
**Accessibility:**
- **Unprivileged Processes:** Creating a bridge and enslaving interfaces to it
requires `CAP_NET_ADMIN` in the network namespace. A typical unprivileged
process without user namespaces cannot trigger this.
- **User Namespaces:** An unprivileged user can create a new user namespace and
a new network namespace, gaining `CAP_NET_ADMIN` within that namespace. They can
then create the bridges, `veth` pairs, form the loop, and inject a broadcast
packet to crash the host kernel. Thus, it is accessible from user namespaces.
**Other Triggers:**
- **Network / Peripheral Trigger:** If a system administrator misconfigures the
physical network (or an attacker plugs a loopback cable between two ports of the
machine, or connects a dumb switch with a loop), a bridging loop is formed on
the physical interfaces. In this scenario, any incoming broadcast packet from
the network will trigger the exact same broadcast storm and softirq livelock.
- **VM Guest Trigger:** A user inside a VM guest can perform the same namespace
tricks to crash the guest kernel.]
|