| 0/0 |
2026/05/31 17:27 |
flow |
assessment-security |
50m
Results: map[DenialOfService:true Explanation:**Analysis of the Bug Report**
The bug report indicates a softlockup (a task blocked for more than 20 seconds)
occurring in the `batman-adv` subsystem, specifically within the
`batadv_mcast_mla_update` worker thread. The stack trace shows the thread is
interrupted while freeing a list of multicast addresses
(`batadv_mcast_mla_list_free` -> `kfree` -> `debug_check_no_obj_freed`).
**Root Cause**
The root cause is an algorithmic complexity vulnerability (O(N^2)) in the
`batman-adv` multicast handling code.
The periodic worker `batadv_mcast_mla_update()` collects all multicast groups
joined on the `batadv` interface (or its bridge) by calling functions like
`batadv_mcast_mla_meshif_get_ipv4()`. For every multicast group found on the
device, it checks if the mapped MAC address is already in the local `mcast_list`
by calling `batadv_mcast_mla_is_duplicate()`.
`batadv_mcast_mla_is_duplicate()` performs a linear search over the
`mcast_list`. If there are `N` unique multicast groups joined on the interface,
building this list requires `O(N^2)` iterations. Furthermore, subsequent
functions like `batadv_mcast_mla_tt_retract()` and `batadv_mcast_mla_tt_add()`
also perform `O(N^2)` list comparisons. These loops run with `rcu_read_lock()`
held and without any `cond_resched()` calls.
**Security Impact**
* **Denial of Service (DoS):** An attacker can force the kernel to perform tens
of billions of iterations in an uninterruptible context. This monopolizes the
CPU core, triggers the softlockup detector, causes RCU stalls, and completely
blocks the `batadv_event_workqueue`. If the system is configured to panic on
softlockups or RCU stalls (`panic_on_rcu_stall`), it will crash the machine.
* **Exploitable:** No. This is purely a CPU exhaustion/algorithmic complexity
issue. There is no memory corruption, out-of-bounds access, or use-after-free
involved.
**Reachability**
* **Accessible From Unprivileged Processes:** Yes. Joining a multicast group via
`setsockopt(IP_ADD_MEMBERSHIP)` does not require any privileges (e.g.,
`CAP_NET_ADMIN`). An unprivileged user can create many sockets (up to
`RLIMIT_NOFILE`, which can be bypassed by forking multiple processes) and join
up to `net.ipv4.igmp_max_memberships` (default 20) unique multicast groups per
socket. By doing this, an attacker can easily register hundreds of thousands of
unique multicast groups on an existing `batadv` interface, triggering the O(N^2)
CPU exhaustion.
* **Accessible From User Namespaces:** Yes. If a `batadv` interface does not
exist by default, an unprivileged user can create a new user and network
namespace, acquire `CAP_NET_ADMIN` within that namespace, and create a `batadv`
interface via netlink (which also auto-loads the `batman-adv` module if
available). Once the interface is created, the worker thread is scheduled, and
the user can proceed to flood it with multicast group joins.
* **Ethernet Network Trigger:** No. While remote IGMP/MLD join packets can add
multicast groups to a bridge (which `batman-adv` also queries via
`batadv_mcast_mla_bridge_get`), the Linux bridge subsystem enforces a strict
limit on the number of snooped multicast groups
(`BR_MULTICAST_DEFAULT_HASH_MAX`, defaulting to 4096). 4096 groups are not
enough to cause a softlockup in the O(N^2) loop (it takes only a few
milliseconds). Therefore, it cannot be triggered remotely. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/05/31 17:27 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/05/31 17:27 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/788f7340accad9d97b0b78fa9f5f44d848f24ce3]
|
| 3/1 |
2026/05/31 17:29 |
action |
kernel-builder |
28m
Results: map[KernelObj:/app/workdir/cache/build/5d8514f19b9c2330e8e46324c92efc344c643eeb]
|
| 4/1 |
2026/05/31 17:57 |
action |
codesearch-prepare |
15m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/05/31 18:12 |
agent |
expert |
5m
Model:
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption or elevated privileges.
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.
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:
INFO: task syz.2.205:6698 blocked for more than 144 seconds.
Tainted: G L syzkaller #0
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:syz.2.205 state:D stack:26096 pid:6698 tgid:6694 ppid:5858 task_flags:0x400140 flags:0x00080002
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5352 [inline]
__schedule+0x1665/0x5590 kernel/sched/core.c:6964
__schedule_loop kernel/sched/core.c:7047 [inline]
schedule+0x164/0x360 kernel/sched/core.c:7062
schedule_preempt_disabled+0x13/0x30 kernel/sched/core.c:7119
rwsem_down_write_slowpath+0x883/0x1080 kernel/locking/rwsem.c:1215
__down_write_common kernel/locking/rwsem.c:1347 [inline]
__down_write kernel/locking/rwsem.c:1356 [inline]
down_write+0x1bc/0x200 kernel/locking/rwsem.c:1626
mmap_write_lock include/linux/mmap_lock.h:536 [inline]
userfaultfd_register fs/userfaultfd.c:1317 [inline]
userfaultfd_ioctl+0x21fb/0x4c70 fs/userfaultfd.c:2050
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:597 [inline]
__se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14d/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f5eb579c799
RSP: 002b:00007f5eb66c0028 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007f5eb5a15fa0 RCX: 00007f5eb579c799
RDX: 0000200000000080 RSI: 00000000c020aa00 RDI: 0000000000000005
RBP: 00007f5eb5832c99 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f5eb5a16038 R14: 00007f5eb5a15fa0 R15: 00007fff294161f8
</TASK>
Showing all locks held in the system:
1 lock held by khungtaskd/31:
#0: ffffffff8e75d6a0 (rcu_read_lock){....}-{1:3}, at: rcu_lock_acquire include/linux/rcupdate.h:312 [inline]
#0: ffffffff8e75d6a0 (rcu_read_lock){....}-{1:3}, at: rcu_read_lock include/linux/rcupdate.h:850 [inline]
#0: ffffffff8e75d6a0 (rcu_read_lock){....}-{1:3}, at: debug_show_all_locks+0x2e/0x180 kernel/locking/lockdep.c:6775
2 locks held by getty/5602:
#0: ffff888037e7f0a0 (&tty->ldisc_sem){++++}-{0:0}, at: tty_ldisc_ref_wait+0x25/0x70 drivers/tty/tty_ldisc.c:243
#1: ffffc9000322b2e8 (&ldata->atomic_read_lock){+.+.}-{4:4}, at: n_tty_read+0x45c/0x13c0 drivers/tty/n_tty.c:2211
1 lock held by udevd/6189:
1 lock held by syz.2.205/6698:
#0: ffff888034cb3538 (&mm->mmap_lock){++++}-{4:4}, at: mmap_write_lock include/linux/mmap_lock.h:536 [inline]
#0: ffff888034cb3538 (&mm->mmap_lock){++++}-{4:4}, at: userfaultfd_register fs/userfaultfd.c:1317 [inline]
#0: ffff888034cb3538 (&mm->mmap_lock){++++}-{4:4}, at: userfaultfd_ioctl+0x21fb/0x4c70 fs/userfaultfd.c:2050
1 lock held by syz.3.602/8133:
#0: ffff8880b863ae60 (&rq->__lock){-.-.}-{2:2}, at: raw_spin_rq_lock_nested+0x31/0x150 kernel/sched/core.c:647
2 locks held by syz.1.590/8138:
#0: ffff88807e3a4a40 (&sb->s_type->i_mutex_key#13){+.+.}-{4:4}, at: inode_lock include/linux/fs.h:1028 [inline]
#0: ffff88807e3a4a40 (&sb->s_type->i_mutex_key#13){+.+.}-{4:4}, at: __sock_release+0x89/0x250 net/socket.c:724
#1: ffffffff8e7638e8 (rcu_state.exp_mutex){+.+.}-{4:4}, at: exp_funnel_lock kernel/rcu/tree_exp.h:343 [inline]
#1: ffffffff8e7638e8 (rcu_state.exp_mutex){+.+.}-{4:4}, at: synchronize_rcu_expedited+0x38d/0x770 kernel/rcu/tree_exp.h:961
1 lock held by syz.0.605/8141:
=============================================
NMI backtrace for cpu 0
CPU: 0 UID: 0 PID: 31 Comm: khungtaskd Tainted: G L syzkaller #0 PREEMPT(full)
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 02/12/2026
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
nmi_cpu_backtrace+0x274/0x2d0 lib/nmi_backtrace.c:113
nmi_trigger_cpumask_backtrace+0x17a/0x300 lib/nmi_backtrace.c:62
trigger_all_cpu_backtrace include/linux/nmi.h:162 [inline]
__sys_info lib/sys_info.c:157 [inline]
sys_info+0x135/0x170 lib/sys_info.c:165
check_hung_uninterruptible_tasks kernel/hung_task.c:353 [inline]
watchdog+0x1002/0x1060 kernel/hung_task.c:561
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x51e/0xb90 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
Sending NMI from CPU 0 to CPUs 1:
NMI backtrace for cpu 1
CPU: 1 UID: 0 PID: 1323 Comm: kworker/u8:7 Tainted: G L syzkaller #0 PREEMPT(full)
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 02/12/2026
Workqueue: bat_events batadv_mcast_mla_update
RIP: 0010:__debug_check_no_obj_freed lib/debugobjects.c:1138 [inline]
RIP: 0010:debug_check_no_obj_freed+0x534/0x550 lib/debugobjects.c:1157
Code: ff df e9 4f fe ff ff 4c 8b 74 24 30 4c 8b 7c 24 28 eb 02 31 db 48 8b 7c 24 10 48 8b 74 24 08 e8 42 54 fb 06 3b 1d f0 4e 56 0b <0f> 8e b3 fb ff ff 89 1d e4 4e 56 0b e9 a8 fb ff ff 66 66 2e 0f 1f
RSP: 0018:ffffc9000614f850 EFLAGS: 00000297
RAX: 0000000080000000 RBX: 0000000000000001 RCX: 0000000000000000
RDX: 0000000000000006 RSI: ffffffff8df3e4e9 RDI: 00000000ffffffff
RBP: 0000000000000000 R08: ffffffff90122ff7 R09: 1ffffffff20245fe
R10: dffffc0000000000 R11: fffffbfff20245ff R12: ffff888078071b20
R13: ffff888078071b00 R14: ffff888078071000 R15: 0000000000000001
FS: 0000000000000000(0000) GS:ffff888125536000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f0f65bfe000 CR3: 000000004a1b6000 CR4: 00000000003526f0
Call Trace:
<TASK>
slab_free_hook mm/slub.c:2620 [inline]
slab_free mm/slub.c:6242 [inline]
kfree+0x13e/0x650 mm/slub.c:6557
batadv_mcast_mla_list_free net/batman-adv/multicast.c:667 [inline]
__batadv_mcast_mla_update net/batman-adv/multicast.c:924 [inline]
batadv_mcast_mla_update+0x3345/0x3710 net/batman-adv/multicast.c:946
process_one_work+0x9ab/0x1780 kernel/workqueue.c:3288
process_scheduled_works kernel/workqueue.c:3379 [inline]
worker_thread+0xba8/0x11e0 kernel/workqueue.c:3465
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x51e/0xb90 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
Reply: **Analysis of the Bug Report**
The bug report indicates a softlockup (a task blocked for more than 20 seconds) occurring in the `batman-adv` subsystem, specifically within the `batadv_mcast_mla_update` worker thread. The stack trace shows the thread is interrupted while freeing a list of multicast addresses (`batadv_mcast_mla_list_free` -> `kfree` -> `debug_check_no_obj_freed`).
**Root Cause**
The root cause is an algorithmic complexity vulnerability (O(N^2)) in the `batman-adv` multicast handling code.
The periodic worker `batadv_mcast_mla_update()` collects all multicast groups joined on the `batadv` interface (or its bridge) by calling functions like `batadv_mcast_mla_meshif_get_ipv4()`. For every multicast group found on the device, it checks if the mapped MAC address is already in the local `mcast_list` by calling `batadv_mcast_mla_is_duplicate()`.
`batadv_mcast_mla_is_duplicate()` performs a linear search over the `mcast_list`. If there are `N` unique multicast groups joined on the interface, building this list requires `O(N^2)` iterations. Furthermore, subsequent functions like `batadv_mcast_mla_tt_retract()` and `batadv_mcast_mla_tt_add()` also perform `O(N^2)` list comparisons. These loops run with `rcu_read_lock()` held and without any `cond_resched()` calls.
**Security Impact**
* **Denial of Service (DoS):** An attacker can force the kernel to perform tens of billions of iterations in an uninterruptible context. This monopolizes the CPU core, triggers the softlockup detector, causes RCU stalls, and completely blocks the `batadv_event_workqueue`. If the system is configured to panic on softlockups or RCU stalls (`panic_on_rcu_stall`), it will crash the machine.
* **Exploitable:** No. This is purely a CPU exhaustion/algorithmic complexity issue. There is no memory corruption, out-of-bounds access, or use-after-free involved.
**Reachability**
* **Accessible From Unprivileged Processes:** Yes. Joining a multicast group via `setsockopt(IP_ADD_MEMBERSHIP)` does not require any privileges (e.g., `CAP_NET_ADMIN`). An unprivileged user can create many sockets (up to `RLIMIT_NOFILE`, which can be bypassed by forking multiple processes) and join up to `net.ipv4.igmp_max_memberships` (default 20) unique multicast groups per socket. By doing this, an attacker can easily register hundreds of thousands of unique multicast groups on an existing `batadv` interface, triggering the O(N^2) CPU exhaustion.
* **Accessible From User Namespaces:** Yes. If a `batadv` interface does not exist by default, an unprivileged user can create a new user and network namespace, acquire `CAP_NET_ADMIN` within that namespace, and create a `batadv` interface via netlink (which also auto-loads the `batman-adv` module if available). Once the interface is created, the worker thread is scheduled, and the user can proceed to flood it with multicast group joins.
* **Ethernet Network Trigger:** No. While remote IGMP/MLD join packets can add multicast groups to a bridge (which `batman-adv` also queries via `batadv_mcast_mla_bridge_get`), the Linux bridge subsystem enforces a strict limit on the number of snooped multicast groups (`BR_MULTICAST_DEFAULT_HASH_MAX`, defaulting to 4096). 4096 groups are not enough to cause a softlockup in the O(N^2) loop (it takes only a few milliseconds). Therefore, it cannot be triggered remotely.
|
| 6/2 |
2026/05/31 18:12 |
llm |
expert |
0m
Model:
Tokens:
input: 8906
output: 46
thoughts: 974
Thoughts: **Analyzing a Potential Softlock in batman-adv Multicast Handling**
Okay, so I've got this hung task report. Immediately, my eye goes to the fact that `syz.2.205` is blocked for over 144 seconds. That's a red flag. Digging into the call trace, I see it's deep inside `userfaultfd_ioctl`, which is a common source of contention if there are issues with locking or resource allocation. The critical section where it is blocked is related to `rwsem_down_write_slowpath`.
Then, I see the NMI backtrace, and I am particularly interested because it points to CPU 1. This one is calling `kfree()` inside `batadv_mcast_mla_list_free`, specifically through `debug_check_no_obj_freed`, which indicates some kind of memory corruption or double free scenario *potentially*. That's worth pursuing; I have seen this before. The presence of the `debug_check_no_obj_freed` indicates that we may be dealing with some kind of heap corruption.
Given the timing and the fact that one process is waiting and the other is freeing, I need to check for a potential race condition or softlock within `batadv_mcast_mla_list_free`. It's a prime suspect given the context of multicast operations. Let me pull up `net/batman-adv/multicast.c` and take a look around line 667; I want to see how these multicast lists are handled and what locks are being used. It's a good starting point to investigate this potential softlock scenario.
|
| 7/2 |
2026/05/31 18:12 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/multicast.c Name:batadv_mcast_mla_list_free]
Results: map[SourceCode: 654: /**
655: * batadv_mcast_mla_list_free() - free a list of multicast addresses
656: * @mcast_list: the list to free
657: *
658: * Removes and frees all items in the given mcast_list.
659: */
660: static void batadv_mcast_mla_list_free(struct hlist_head *mcast_list)
661: {
662: struct batadv_hw_addr *mcast_entry;
663: struct hlist_node *tmp;
664:
665: hlist_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) {
666: hlist_del(&mcast_entry->list);
667: kfree(mcast_entry);
668: }
669: }
SourceFile:net/batman-adv/multicast.c]
|
| 8/2 |
2026/05/31 18:12 |
llm |
expert |
0m
Model:
Tokens:
input: 10571
output: 45
thoughts: 113
Thoughts: **Analyzing a Potential Multicast Address Deallocation Issue**
Okay, so I'm looking at this code and it seems we're freeing a list of multicast addresses. My immediate concern is, could this be an infinite loop? Is the list corrupted, leading us into an endless cycle, or are we just dealing with an unexpectedly long list? The possibilities need to be considered.
Hold on, I think I've spotted something. If the `mcast_entry` is freed but the list itself is somehow damaged, then the `tmp` pointer within the iteration might be pointing back to memory we've already released. Wait, no, that can't be right; `hlist_for_each_entry_safe` uses `tmp` to store the *next* element, so it shouldn't re-enter the previously freed elements. However, if this list were circular, *that* could create the infinite looping scenario.
Let's not jump to conclusions; I should probably start by examining `__batadv_mcast_mla_update`. That function call will have to be our next investigation point. It's time to follow the trail.
|
| 9/2 |
2026/05/31 18:12 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/multicast.c Name:__batadv_mcast_mla_update]
Results: map[SourceCode: 888: /**
889: * __batadv_mcast_mla_update() - update the own MLAs
890: * @bat_priv: the bat priv with all the mesh interface information
891: *
892: * Updates the own multicast listener announcements in the translation
893: * table as well as the own, announced multicast tvlv container.
894: *
895: * Note that non-conflicting reads and writes to bat_priv->mcast.mla_list
896: * in batadv_mcast_mla_tt_retract() and batadv_mcast_mla_tt_add() are
897: * ensured by the non-parallel execution of the worker this function
898: * belongs to.
899: */
900: static void __batadv_mcast_mla_update(struct batadv_priv *bat_priv)
901: {
902: struct net_device *mesh_iface = bat_priv->mesh_iface;
903: struct hlist_head mcast_list = HLIST_HEAD_INIT;
904: struct batadv_mcast_mla_flags flags;
905: int ret;
906:
907: flags = batadv_mcast_mla_flags_get(bat_priv);
908:
909: ret = batadv_mcast_mla_meshif_get(mesh_iface, &mcast_list, &flags);
910: if (ret < 0)
911: goto out;
912:
913: ret = batadv_mcast_mla_bridge_get(mesh_iface, &mcast_list, &flags);
914: if (ret < 0)
915: goto out;
916:
917: spin_lock(&bat_priv->mcast.mla_lock);
918: batadv_mcast_mla_tt_retract(bat_priv, &mcast_list);
919: batadv_mcast_mla_tt_add(bat_priv, &mcast_list);
920: batadv_mcast_mla_flags_update(bat_priv, &flags);
921: spin_unlock(&bat_priv->mcast.mla_lock);
922:
923: out:
924: batadv_mcast_mla_list_free(&mcast_list);
925: }
SourceFile:net/batman-adv/multicast.c]
|
| 10/2 |
2026/05/31 18:12 |
llm |
expert |
0m
Model:
Tokens:
input: 11683
output: 47
thoughts: 58
Thoughts: Here's my summary, as the thought process unfolds:
**Digging into the Multicast List Population**
Okay, so I need to understand where this `mcast_list` comes from. The code tells me it's populated by these two functions: `batadv_mcast_mla_meshif_get` and `batadv_mcast_mla_bridge_get`. Seems like the primary work happens within these.
Let's start by digging into `batadv_mcast_mla_meshif_get`. That feels like the most logical starting point to trace the source of the data and its eventual function. Knowing how the mesh interface information is retrieved will likely inform the context of the rest. I'll need to see how it collects its data, and what input it consumes to do so. From there, I'll be in a better position to connect the dots and understand the overall flow.
|
| 11/2 |
2026/05/31 18:12 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/multicast.c Name:batadv_mcast_mla_meshif_get]
Results: map[SourceCode: 499: /**
500: * batadv_mcast_mla_meshif_get() - get meshif multicast listeners
501: * @dev: the device to collect multicast addresses from
502: * @mcast_list: a list to put found addresses into
503: * @flags: flags indicating the new multicast state
504: *
505: * Collects multicast addresses of multicast listeners residing
506: * on this kernel on the given mesh interface, dev, in
507: * the given mcast_list. In general, multicast listeners provided by
508: * your multicast receiving applications run directly on this node.
509: *
510: * If there is a bridge interface on top of dev, collect from that one
511: * instead. Just like with IP addresses and routes, multicast listeners
512: * will(/should) register to the bridge interface instead of an
513: * enslaved bat0.
514: *
515: * Return: -ENOMEM on memory allocation error or the number of
516: * items added to the mcast_list otherwise.
517: */
518: static int
519: batadv_mcast_mla_meshif_get(struct net_device *dev,
520: struct hlist_head *mcast_list,
521: struct batadv_mcast_mla_flags *flags)
522: {
523: struct net_device *bridge = batadv_mcast_get_bridge(dev);
524: int ret4, ret6 = 0;
525:
526: if (bridge)
527: dev = bridge;
528:
529: ret4 = batadv_mcast_mla_meshif_get_ipv4(dev, mcast_list, flags);
530: if (ret4 < 0)
531: goto out;
532:
533: ret6 = batadv_mcast_mla_meshif_get_ipv6(dev, mcast_list, flags);
534: if (ret6 < 0) {
535: ret4 = 0;
536: goto out;
537: }
538:
539: out:
540: dev_put(bridge);
541:
542: return ret4 + ret6;
543: }
SourceFile:net/batman-adv/multicast.c]
|
| 12/2 |
2026/05/31 18:12 |
llm |
expert |
0m
Model:
Tokens:
input: 12700
output: 50
thoughts: 39
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience:
**Investigating Multicast Address Leases in BATMAN Advanced**
Right, let's dive into this. My initial thought is to cross-reference the IPv4 and IPv6 multicast address lease retrieval functions within the BATMAN Advanced code. Specifically, I want to confirm my understanding of the interactions between `batadv_mcast_mla_meshif_get_ipv4` and `batadv_mcast_mla_meshif_get_ipv6`. I want to quickly evaluate the implementation details in these functions to identify a possible area of concern for a recent discovery.
|
| 13/2 |
2026/05/31 18:13 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/multicast.c Name:batadv_mcast_mla_meshif_get_ipv4]
Results: map[SourceCode: 351: /**
352: * batadv_mcast_mla_meshif_get_ipv4() - get meshif IPv4 multicast listeners
353: * @dev: the device to collect multicast addresses from
354: * @mcast_list: a list to put found addresses into
355: * @flags: flags indicating the new multicast state
356: *
357: * Collects multicast addresses of IPv4 multicast listeners residing
358: * on this kernel on the given mesh interface, dev, in
359: * the given mcast_list. In general, multicast listeners provided by
360: * your multicast receiving applications run directly on this node.
361: *
362: * Return: -ENOMEM on memory allocation error or the number of
363: * items added to the mcast_list otherwise.
364: */
365: static int
366: batadv_mcast_mla_meshif_get_ipv4(struct net_device *dev,
367: struct hlist_head *mcast_list,
368: struct batadv_mcast_mla_flags *flags)
369: {
370: struct batadv_hw_addr *new;
371: struct in_device *in_dev;
372: u8 mcast_addr[ETH_ALEN];
373: struct ip_mc_list *pmc;
374: int ret = 0;
375:
376: if (flags->tvlv_flags & BATADV_MCAST_WANT_ALL_IPV4)
377: return 0;
378:
379: rcu_read_lock();
380:
381: in_dev = __in_dev_get_rcu(dev);
382: if (!in_dev) {
383: rcu_read_unlock();
384: return 0;
385: }
386:
387: for (pmc = rcu_dereference(in_dev->mc_list); pmc;
388: pmc = rcu_dereference(pmc->next_rcu)) {
389: if (flags->tvlv_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES &&
390: ipv4_is_local_multicast(pmc->multiaddr))
391: continue;
392:
393: if (!(flags->tvlv_flags & BATADV_MCAST_WANT_NO_RTR4) &&
394: !ipv4_is_local_multicast(pmc->multiaddr))
395: continue;
396:
397: ip_eth_mc_map(pmc->multiaddr, mcast_addr);
398:
399: if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list))
400: continue;
401:
402: new = kmalloc_obj(*new, GFP_ATOMIC);
403: if (!new) {
404: ret = -ENOMEM;
405: break;
406: }
407:
408: ether_addr_copy(new->addr, mcast_addr);
409: hlist_add_head(&new->list, mcast_list);
410: ret++;
411: }
412: rcu_read_unlock();
413:
414: return ret;
415: }
SourceFile:net/batman-adv/multicast.c]
|
| 14/2 |
2026/05/31 18:13 |
llm |
expert |
0m
Model:
Tokens:
input: 13972
output: 40
thoughts: 692
Thoughts: **Analyzing a Potential Softlockup in batman-adv**
Okay, let's break this down. My initial thought was: wait, are we using `kmalloc_obj`? No, that's not standard. Then I saw it's likely a batman-adv macro. The report points to `kfree` being the culprit, but with a twist. The stack trace leads me to `debug_check_no_obj_freed`. This suggests that the issue isn't the `kfree` itself, but the *check* that happens around it. Specifically, we're checking if any active debug objects still exist within the memory about to be freed.
The key is `batadv_mcast_mla_list_free`, which repeatedly calls `kfree`. If this `debug_check_no_obj_freed` call is slow, maybe due to contention or just the sheer volume of elements, it could cause a softlockup. This becomes especially concerning when there are many elements to process.
Now, where are these many elements coming from? I need to understand the source of these multicast entries. Okay, `batadv_mcast_mla_list_free` is called from `__batadv_mcast_mla_update`, a workqueue item. That makes it asynchronous. Could there be a loop in the `mcast_list`? No, it's a local variable initialized with `HLIST_HEAD_INIT` and newly allocated elements are added to it. So, no loop. It's just a long list.
So, how does `mcast_list` get long? I see that `in_dev->mc_list` could be long. An unprivileged user can join many multicast groups. But how many? I'm reminded of `batadv_mcast_mla_meshif_get_ipv4` iterating through `in_dev->mc_list`. This is the potential bottleneck! I start to wonder if a user could join millions of groups. Ah, there's `net.ipv4.igmp_max_memberships`. That limits the number *per socket* to 20, which doesn't seem like a large number.
However, a user can create many sockets. Wait, and `in_dev->mc_list` is per-device! That's the crucial detail. If a user creates multiple sockets, and each socket joins 20 groups, the total count of groups on the device can become massive. The bottleneck isn't the *number* of groups, but the sheer number of calls to `debug_check_no_obj_freed` due to the size of the device's multicast list. The real question is: What limits the total number of groups on a device? I need to check `ip_mc_join_group` to see where the limit is.
|
| 15/2 |
2026/05/31 18:13 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/ipv4/igmp.c Name:ip_mc_join_group]
Results: map[SourceCode:2311: /* Join ASM (Any-Source Multicast) group
2312: */
2313: int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr)
2314: {
2315: return __ip_mc_join_group(sk, imr, MCAST_EXCLUDE);
2316: }
SourceFile:net/ipv4/igmp.c]
|
| 16/2 |
2026/05/31 18:13 |
llm |
expert |
0m
Model:
Tokens:
input: 15448
output: 41
thoughts: 14
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Investigating `__ip_mc_join_group`**
Alright, time to dive into this. The objective is to evaluate the multicast join group function, specifically `__ip_mc_join_group`. I need to assess its behavior, its potential impact on the system, and its interaction with other related functions. Let's see what we can find. I want to look closely at its implementation. It is important to know the flow of the process when joining a multicast group.
|
| 17/2 |
2026/05/31 18:13 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/ipv4/igmp.c Name:__ip_mc_join_group]
Results: map[SourceCode:2259: /* Join a multicast group
2260: */
2261: static int __ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr,
2262: unsigned int mode)
2263: {
2264: __be32 addr = imr->imr_multiaddr.s_addr;
2265: struct ip_mc_socklist *iml, *i;
2266: struct in_device *in_dev;
2267: struct inet_sock *inet = inet_sk(sk);
2268: struct net *net = sock_net(sk);
2269: int ifindex;
2270: int count = 0;
2271: int err;
2272:
2273: ASSERT_RTNL();
2274:
2275: if (!ipv4_is_multicast(addr))
2276: return -EINVAL;
2277:
2278: in_dev = ip_mc_find_dev(net, imr);
2279:
2280: if (!in_dev) {
2281: err = -ENODEV;
2282: goto done;
2283: }
2284:
2285: err = -EADDRINUSE;
2286: ifindex = imr->imr_ifindex;
2287: for_each_pmc_rtnl(inet, i) {
2288: if (i->multi.imr_multiaddr.s_addr == addr &&
2289: i->multi.imr_ifindex == ifindex)
2290: goto done;
2291: count++;
2292: }
2293: err = -ENOBUFS;
2294: if (count >= READ_ONCE(net->ipv4.sysctl_igmp_max_memberships))
2295: goto done;
2296: iml = sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL);
2297: if (!iml)
2298: goto done;
2299:
2300: memcpy(&iml->multi, imr, sizeof(*imr));
2301: iml->next_rcu = inet->mc_list;
2302: iml->sflist = NULL;
2303: iml->sfmode = mode;
2304: rcu_assign_pointer(inet->mc_list, iml);
2305: ____ip_mc_inc_group(in_dev, addr, mode, GFP_KERNEL);
2306: err = 0;
2307: done:
2308: return err;
2309: }
SourceFile:net/ipv4/igmp.c]
|
| 18/2 |
2026/05/31 18:13 |
llm |
expert |
0m
Model:
Tokens:
input: 16415
output: 46
thoughts: 160
Thoughts: **Analyzing Potential Performance Bottlenecks in IGMP Multicast with User-Controlled Socket Creation**
Okay, so I'm thinking about how IGMP multicast scales under load, specifically when a user can potentially create a large number of sockets. It looks like `sysctl_igmp_max_memberships` imposes a limit on multicast groups *per socket*. My initial thought was that this would be the primary limit. But then I realized that a user can create many sockets, effectively multiplying the total number of possible groups.
Right, so the limiting factor, ultimately, *is* the number of file descriptors a user can create, which would equate to sockets in this case, `RLIMIT_NOFILE`. By default, that's typically 1024. Then, the maximum number of groups could be 1024 times the default memberships. So, if we take the default of 20, that results in about 20480 total groups.
Now, if a user actually *does* create 20,480 multicast groups across various sockets, `in_dev->mc_list` is going to have 20,480 elements. That's a lot! It's at this point that a red flag went up... given the number of potential groups, the thought of how this might be handled by the code now leads me to worry about nested loops and O(N^2) complexity.
I need to look into this more closely... Specifically, I should check the implementation of `batadv_mcast_mla_meshif_get_ipv4`. This looks like a potential point of concern with its O(N^2) loop. And the function that I will look to analyze this concerns is `batadv_mcast_mla_is_duplicate`.
|
| 19/2 |
2026/05/31 18:13 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/multicast.c Name:batadv_mcast_mla_is_duplicate]
Results: map[SourceCode: 331: /**
332: * batadv_mcast_mla_is_duplicate() - check whether an address is in a list
333: * @mcast_addr: the multicast address to check
334: * @mcast_list: the list with multicast addresses to search in
335: *
336: * Return: true if the given address is already in the given list.
337: * Otherwise returns false.
338: */
339: static bool batadv_mcast_mla_is_duplicate(u8 *mcast_addr,
340: struct hlist_head *mcast_list)
341: {
342: struct batadv_hw_addr *mcast_entry;
343:
344: hlist_for_each_entry(mcast_entry, mcast_list, list)
345: if (batadv_compare_eth(mcast_entry->addr, mcast_addr))
346: return true;
347:
348: return false;
349: }
SourceFile:net/batman-adv/multicast.c]
|
| 20/2 |
2026/05/31 18:13 |
llm |
expert |
0m
Model:
Tokens:
input: 17339
output: 41
thoughts: 129
Thoughts: **Analyzing Multicast Performance**
Okay, so I'm looking at this `batadv_mcast_mla_is_duplicate` function, and it's iterating over `mcast_list`. Right, and `batadv_mcast_mla_meshif_get_ipv4` calls that function for *every* element in `in_dev->mc_list`. Hmm, so if `in_dev->mc_list` has, let's say, N elements, then the whole process is going to be O(N squared) time complexity. That's a potential bottleneck. I'm imagining a situation with a lot of multicast groups!
Wait a second… Isn't `in_dev->mc_list` supposed to only hold *unique* multicast addresses per device? If so, then that might alleviate some of the scaling concerns, although the quadratic complexity is still a point to consider. I should probably double-check the logic in `____ip_mc_inc_group` to confirm my understanding of the data structure.
|
| 21/2 |
2026/05/31 18:13 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/ipv4/igmp.c Name:____ip_mc_inc_group]
Results: map[SourceCode:1501: /*
1502: * A socket has joined a multicast group on device dev.
1503: */
1504: static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
1505: unsigned int mode, gfp_t gfp)
1506: {
1507: struct ip_mc_list __rcu **mc_hash;
1508: struct ip_mc_list *im;
1509:
1510: ASSERT_RTNL();
1511:
1512: mc_hash = rtnl_dereference(in_dev->mc_hash);
1513: if (mc_hash) {
1514: u32 hash = hash_32((__force u32)addr, MC_HASH_SZ_LOG);
1515:
1516: for (im = rtnl_dereference(mc_hash[hash]);
1517: im;
1518: im = rtnl_dereference(im->next_hash)) {
1519: if (im->multiaddr == addr)
1520: break;
1521: }
1522: } else {
1523: for_each_pmc_rtnl(in_dev, im) {
1524: if (im->multiaddr == addr)
1525: break;
1526: }
1527: }
1528:
1529: if (im) {
1530: im->users++;
1531: ip_mc_add_src(in_dev, &addr, mode, 0, NULL, 0);
1532: goto out;
1533: }
1534:
1535: im = kzalloc_obj(*im, gfp);
1536: if (!im)
1537: goto out;
1538:
1539: im->users = 1;
1540: im->interface = in_dev;
1541: in_dev_hold(in_dev);
1542: im->multiaddr = addr;
1543: im->mca_cstamp = jiffies;
1544: im->mca_tstamp = im->mca_cstamp;
1545: /* initial mode is (EX, empty) */
1546: im->sfmode = mode;
1547: im->sfcount[mode] = 1;
1548: refcount_set(&im->refcnt, 1);
1549: spin_lock_init(&im->lock);
1550: #ifdef CONFIG_IP_MULTICAST
1551: timer_setup(&im->timer, igmp_timer_expire, 0);
1552: #endif
1553:
1554: im->next_rcu = in_dev->mc_list;
1555: in_dev->mc_count++;
1556: rcu_assign_pointer(in_dev->mc_list, im);
1557:
1558: ip_mc_hash_add(in_dev, im);
1559:
1560: #ifdef CONFIG_IP_MULTICAST
1561: igmpv3_del_delrec(in_dev, im);
1562: #endif
1563: igmp_group_added(im);
1564: inet_ifmcaddr_notify(in_dev->dev, im, RTM_NEWMULTICAST);
1565: if (!in_dev->dead)
1566: ip_rt_multicast_event(in_dev);
1567: out:
1568: return;
1569: }
SourceFile:net/ipv4/igmp.c]
|
| 22/2 |
2026/05/31 18:13 |
llm |
expert |
0m
Model:
Tokens:
input: 18802
output: 40
thoughts: 453
Thoughts: **Analyzing Potential Softlockup in Multicast Group Handling**
Okay, here's what's running through my head. I'm looking at how multicast groups are handled, specifically focusing on `in_dev->mc_list`. This list seems to store the *unique* multicast addresses a device has joined. The counter `im->users` goes up for each socket that joins a group, which makes sense.
So, the number of entries in `in_dev->mc_list` gives me the total number of distinct multicast groups joined on the device. I can have multiple sockets joining the same group; each socket is limited to 20. If I've got 1024 sockets, that's potentially 20480 unique groups. And it could be more, if they up the limit or use multiple processes.
Now the real concern. If we hit that number, 20480 unique groups, `batadv_mcast_mla_meshif_get_ipv4` will perform a massive number of comparisons. It's a combinatorial explosion: 20480 * 20480 / 2, or around 200 million comparisons. Could that cause a softlockup? It's a significant number of comparisons. And I'm seeing RCU locks. I'm wondering about the read lock. Softlockup means no scheduling for 20 seconds. It makes me think that could happen, especially if this is running under an RCU read lock, though it's PREEMPT full so maybe not a problem.
Wait! The softlockup isn't in the comparisons, it's actually in `kfree`, specifically `debug_check_no_obj_freed`. Why `kfree`? Because the list is being freed by `batadv_mcast_mla_list_free`. If we're freeing 20480 elements, `kfree` gets called 20480 times. Is that enough? Usually, 20480 `kfree` calls are quick, a matter of a millisecond or less.
But the issue is `debug_check_no_obj_freed`. That's where the problem is manifesting. Let me go check that routine.
|
| 23/2 |
2026/05/31 18:13 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:lib/debugobjects.c Name:debug_check_no_obj_freed]
Results: map[SourceCode:1154: void debug_check_no_obj_freed(const void *address, unsigned long size)
1155: {
1156: if (debug_objects_enabled)
1157: __debug_check_no_obj_freed(address, size);
1158: }
SourceFile:lib/debugobjects.c]
|
| 24/2 |
2026/05/31 18:13 |
llm |
expert |
0m
Model:
Tokens:
input: 19894
output: 41
thoughts: 17
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Debugging Memory Management**
Alright, time to get my hands dirty. My initial thought is to go straight to `__debug_check_no_obj_freed`. This suggests a memory management issue, probably with an object being prematurely deallocated. It's the most likely first check for these kinds of problems, especially if we're seeing unexpected behavior or crashes related to object lifecycle. I'm hoping this will lead me to the specific culprit; it'll be a quick way to find any issues.
|
| 25/2 |
2026/05/31 18:13 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:lib/debugobjects.c Name:__debug_check_no_obj_freed]
Results: map[SourceCode:1097: static void __debug_check_no_obj_freed(const void *address, unsigned long size)
1098: {
1099: unsigned long flags, oaddr, saddr, eaddr, paddr, chunks;
1100: int cnt, objs_checked = 0;
1101: struct debug_obj *obj, o;
1102: struct debug_bucket *db;
1103: struct hlist_node *tmp;
1104:
1105: saddr = (unsigned long) address;
1106: eaddr = saddr + size;
1107: paddr = saddr & ODEBUG_CHUNK_MASK;
1108: chunks = ((eaddr - paddr) + (ODEBUG_CHUNK_SIZE - 1));
1109: chunks >>= ODEBUG_CHUNK_SHIFT;
1110:
1111: for (;chunks > 0; chunks--, paddr += ODEBUG_CHUNK_SIZE) {
1112: db = get_bucket(paddr);
1113:
1114: repeat:
1115: cnt = 0;
1116: raw_spin_lock_irqsave(&db->lock, flags);
1117: hlist_for_each_entry_safe(obj, tmp, &db->list, node) {
1118: cnt++;
1119: oaddr = (unsigned long) obj->object;
1120: if (oaddr < saddr || oaddr >= eaddr)
1121: continue;
1122:
1123: switch (obj->state) {
1124: case ODEBUG_STATE_ACTIVE:
1125: o = *obj;
1126: raw_spin_unlock_irqrestore(&db->lock, flags);
1127: debug_print_object(&o, "free");
1128: debug_object_fixup(o.descr->fixup_free, (void *)oaddr, o.state);
1129: goto repeat;
1130: default:
1131: hlist_del(&obj->node);
1132: __free_object(obj);
1133: break;
1134: }
1135: }
1136: raw_spin_unlock_irqrestore(&db->lock, flags);
1137:
1138: if (cnt > debug_objects_maxchain)
1139: debug_objects_maxchain = cnt;
1140:
1141: objs_checked += cnt;
1142: }
1143:
1144: if (objs_checked > debug_objects_maxchecked)
1145: debug_objects_maxchecked = objs_checked;
1146:
1147: /* Schedule work to actually kmem_cache_free() objects */
1148: if (!READ_ONCE(obj_freeing) && pool_count(&pool_to_free)) {
1149: WRITE_ONCE(obj_freeing, true);
1150: schedule_delayed_work(&debug_obj_work, ODEBUG_FREE_WORK_DELAY);
1151: }
1152: }
SourceFile:lib/debugobjects.c]
|
| 26/2 |
2026/05/31 18:13 |
llm |
expert |
0m
Model:
Tokens:
input: 20984
output: 47
thoughts: 582
Thoughts: **Analyzing a Softlockup in the Kernel**
Okay, so I'm looking at this softlockup, and immediately, my mind jumps to potential causes. I see `__debug_check_no_obj_freed` in the mix, iterating over a debug bucket. That *could* take a while if there are many objects, but it doesn't seem to be the primary culprit here. The hung task `syz.2.205` is blocked in `userfaultfd_ioctl` within `mmap_write_lock`, that's a clue, but let's see what else. The softlockup itself is in `kworker/u8:7`, running `batadv_mcast_mla_update`. That feels like the more immediate concern.
The key is that this kworker has been running without yielding for over 20 seconds on CPU 1, while CPU 0 is running `khungtaskd`. Why is `batadv_mcast_mla_update` getting stuck? My initial thought is the potential O(N^2) loop in `batadv_mcast_mla_meshif_get_ipv4`. But, wait, the stack shows it's in `batadv_mcast_mla_list_free`, so that must be finished. I'm trying to remember the code. The `batadv_mcast_mla_list_free` function means the `batadv_mcast_mla_meshif_get_ipv4` and `batadv_mcast_mla_bridge_get` functions are finished already.
So, it's just freeing the list. Okay, if the list contains 20480 elements, that will take some time, but 20 seconds? That's what I'm questioning.
Let me look at the code block for the function `batadv_mcast_mla_update`. I remember the pattern: it first gets the multicast list for mesh and bridge interfaces, does a spin lock, then retracts and adds to the TT, updates flags, and then frees the list. But hold on... `batadv_mcast_mla_tt_retract` and `batadv_mcast_mla_tt_add` also iterate over the list! That's it. It's the retract and add, not the free. I need to look closer at `batadv_mcast_mla_tt_retract`.
|
| 27/2 |
2026/05/31 18:13 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/multicast.c Name:batadv_mcast_mla_tt_retract]
Results: map[SourceCode: 671: /**
672: * batadv_mcast_mla_tt_retract() - clean up multicast listener announcements
673: * @bat_priv: the bat priv with all the mesh interface information
674: * @mcast_list: a list of addresses which should _not_ be removed
675: *
676: * Retracts the announcement of any multicast listener from the
677: * translation table except the ones listed in the given mcast_list.
678: *
679: * If mcast_list is NULL then all are retracted.
680: */
681: static void batadv_mcast_mla_tt_retract(struct batadv_priv *bat_priv,
682: struct hlist_head *mcast_list)
683: {
684: struct batadv_hw_addr *mcast_entry;
685: struct hlist_node *tmp;
686:
687: hlist_for_each_entry_safe(mcast_entry, tmp, &bat_priv->mcast.mla_list,
688: list) {
689: if (mcast_list &&
690: batadv_mcast_mla_is_duplicate(mcast_entry->addr,
691: mcast_list))
692: continue;
693:
694: batadv_tt_local_remove(bat_priv, mcast_entry->addr,
695: BATADV_NO_FLAGS,
696: "mcast TT outdated", false);
697:
698: hlist_del(&mcast_entry->list);
699: kfree(mcast_entry);
700: }
701: }
SourceFile:net/batman-adv/multicast.c]
|
| 28/2 |
2026/05/31 18:13 |
llm |
expert |
0m
Model:
Tokens:
input: 22673
output: 46
thoughts: 104
Thoughts: **Optimization Opportunity Identified in Batadv Multicast Handling**
Okay, so I'm looking at this `batadv_mcast_mla_tt_retract` function, and it seems like it's calling `batadv_mcast_mla_is_duplicate`. The problem is, it's doing this for *every* element in `bat_priv->mcast.mla_list` against the `mcast_list`. I see the potential bottleneck: if both lists, heaven forbid, contain the maximum of 20480 elements, this equates to a staggering 20480 * 20480 comparisons. That's a huge O(n^2) operation! This will cause significant performance degradation.
And wait a minute... I just realized `batadv_mcast_mla_tt_add` does the *exact* same thing! Another instance of a potentially quadratic time complexity. This needs immediate attention. I need to figure out a more efficient way to perform these checks. A hash table or a more sophisticated data structure might be needed to reduce the number of comparisons. This definitely warrants further investigation.
|
| 29/2 |
2026/05/31 18:13 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/multicast.c Name:batadv_mcast_mla_tt_add]
Results: map[SourceCode: 703: /**
704: * batadv_mcast_mla_tt_add() - add multicast listener announcements
705: * @bat_priv: the bat priv with all the mesh interface information
706: * @mcast_list: a list of addresses which are going to get added
707: *
708: * Adds multicast listener announcements from the given mcast_list to the
709: * translation table if they have not been added yet.
710: */
711: static void batadv_mcast_mla_tt_add(struct batadv_priv *bat_priv,
712: struct hlist_head *mcast_list)
713: {
714: struct batadv_hw_addr *mcast_entry;
715: struct hlist_node *tmp;
716:
717: if (!mcast_list)
718: return;
719:
720: hlist_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) {
721: if (batadv_mcast_mla_is_duplicate(mcast_entry->addr,
722: &bat_priv->mcast.mla_list))
723: continue;
724:
725: if (!batadv_tt_local_add(bat_priv->mesh_iface,
726: mcast_entry->addr, BATADV_NO_FLAGS,
727: BATADV_NULL_IFINDEX, BATADV_NO_MARK))
728: continue;
729:
730: hlist_del(&mcast_entry->list);
731: hlist_add_head(&mcast_entry->list, &bat_priv->mcast.mla_list);
732: }
733: }
SourceFile:net/batman-adv/multicast.c]
|
| 30/2 |
2026/05/31 18:13 |
llm |
expert |
0m
Model:
Tokens:
input: 23614
output: 42
thoughts: 780
Thoughts: **Analyzing the Multicast Softlockup Vulnerability**
Okay, so let's break this down. My initial thought was that `batadv_mcast_mla_tt_add` might be the culprit because of the nested calls. I see it calls `batadv_mcast_mla_is_duplicate` for each element, resulting in O(N^2) comparisons. That's a red flag, but the softlockup itself wasn't directly in that function, which threw me for a loop initially. I see now that it's in `batadv_mcast_mla_list_free`, but that's called *after* a function with O(N^2) complexity.
Then it clicked: the softlockup in `batadv_mcast_mla_list_free` is likely just *where* the NMI happened to interrupt a function that was already taking too long, not the direct cause. That long function is `__batadv_mcast_mla_update`, and the loop in `batadv_mcast_mla_list_free` wasn't the problem itself because it's just a linear loop, which shouldn't take 20 seconds. Unless N is enormous or `kfree` is pathologically slow, it shouldn't be the core issue.
Wait, `kfree` *can* be slow due to `debug_check_no_obj_freed`, which has O(M) complexity, where M is the number of objects in the debug bucket. That could contribute, but the real time sink has to be from the O(N^2) loops in `batadv_mcast_mla_meshif_get_ipv4`, `batadv_mcast_mla_tt_retract`, and `batadv_mcast_mla_tt_add`, which makes the time complexity exponential.
So, how large can N get? I need to figure out the maximum number of multicast groups a user can join. I check `sysctl_igmp_max_memberships`, which defaults to 20. But how many sockets can a user create? Using the `RLIMIT_NOFILE`, which can default to 1024. Then, the total possible groups are 20 * 1024 = 20480.
Can a user increase `RLIMIT_NOFILE`? Yes, up to `sysctl_nr_open`, which defaults to over a million. However, I know unprivileged users have a lower hard limit, which may be 4096 or 1048576, which will be the upper limit. Even with a conservative 4096, 4096 * 20 = 81920.
And the nested comparison will have 81920^2 = 6.7 billion comparisons. That's definitely going to take a long time and result in a softlockup. So, yes, this is exploitable. It's a Denial of Service.
Can an unprivileged user trigger this? Yes! They can create sockets and join multicast groups, and I know this doesn't require any special capabilities. They can use `setsockopt(IP_ADD_MEMBERSHIP)` on *any* interface. This is a significant vulnerability.
|
| 31/2 |
2026/05/31 18:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/ipv4/ip_sockglue.c Name:do_ip_setsockopt]
Results: map[SourceCode: 892: int do_ip_setsockopt(struct sock *sk, int level, int optname,
893: sockptr_t optval, unsigned int optlen)
894: {
895: struct inet_sock *inet = inet_sk(sk);
896: struct net *net = sock_net(sk);
897: int val = 0, err, retv;
898: bool needs_rtnl = setsockopt_needs_rtnl(optname);
899:
900: switch (optname) {
901: case IP_PKTINFO:
902: case IP_RECVTTL:
903: case IP_RECVOPTS:
904: case IP_RECVTOS:
905: case IP_RETOPTS:
906: case IP_TOS:
907: case IP_TTL:
908: case IP_HDRINCL:
909: case IP_MTU_DISCOVER:
910: case IP_RECVERR:
911: case IP_ROUTER_ALERT:
912: case IP_FREEBIND:
913: case IP_PASSSEC:
914: case IP_TRANSPARENT:
915: case IP_MINTTL:
916: case IP_NODEFRAG:
917: case IP_BIND_ADDRESS_NO_PORT:
918: case IP_UNICAST_IF:
919: case IP_MULTICAST_TTL:
920: case IP_MULTICAST_ALL:
921: case IP_MULTICAST_LOOP:
922: case IP_RECVORIGDSTADDR:
923: case IP_CHECKSUM:
924: case IP_RECVFRAGSIZE:
925: case IP_RECVERR_RFC4884:
926: case IP_LOCAL_PORT_RANGE:
927: if (optlen >= sizeof(int)) {
928: if (copy_from_sockptr(&val, optval, sizeof(val)))
929: return -EFAULT;
930: } else if (optlen >= sizeof(char)) {
931: unsigned char ucval;
932:
933: if (copy_from_sockptr(&ucval, optval, sizeof(ucval)))
934: return -EFAULT;
935: val = (int) ucval;
936: }
937: }
938:
939: /* If optlen==0, it is equivalent to val == 0 */
940:
941: if (optname == IP_ROUTER_ALERT) {
942: retv = ip_ra_control(sk, val ? 1 : 0, NULL);
943: if (retv == 0)
944: inet_assign_bit(RTALERT, sk, val);
945: return retv;
946: }
947: if (ip_mroute_opt(optname))
948: return ip_mroute_setsockopt(sk, optname, optval, optlen);
949:
950: /* Handle options that can be set without locking the socket. */
951: switch (optname) {
952: case IP_PKTINFO:
953: inet_assign_bit(PKTINFO, sk, val);
954: return 0;
955: case IP_RECVTTL:
956: inet_assign_bit(TTL, sk, val);
957: return 0;
958: case IP_RECVTOS:
959: inet_assign_bit(TOS, sk, val);
960: return 0;
961: case IP_RECVOPTS:
962: inet_assign_bit(RECVOPTS, sk, val);
963: return 0;
964: case IP_RETOPTS:
965: inet_assign_bit(RETOPTS, sk, val);
966: return 0;
967: case IP_PASSSEC:
968: inet_assign_bit(PASSSEC, sk, val);
969: return 0;
970: case IP_RECVORIGDSTADDR:
971: inet_assign_bit(ORIGDSTADDR, sk, val);
972: return 0;
973: case IP_RECVFRAGSIZE:
974: if (sk->sk_type != SOCK_RAW && sk->sk_type != SOCK_DGRAM)
975: return -EINVAL;
976: inet_assign_bit(RECVFRAGSIZE, sk, val);
977: return 0;
978: case IP_RECVERR:
979: inet_assign_bit(RECVERR, sk, val);
980: if (!val)
981: skb_errqueue_purge(&sk->sk_error_queue);
982: return 0;
983: case IP_RECVERR_RFC4884:
984: if (val < 0 || val > 1)
985: return -EINVAL;
986: inet_assign_bit(RECVERR_RFC4884, sk, val);
987: return 0;
988: case IP_FREEBIND:
989: if (optlen < 1)
990: return -EINVAL;
991: inet_assign_bit(FREEBIND, sk, val);
992: return 0;
993: case IP_HDRINCL:
994: if (sk->sk_type != SOCK_RAW)
995: return -ENOPROTOOPT;
996: inet_assign_bit(HDRINCL, sk, val);
997: return 0;
998: case IP_MULTICAST_LOOP:
999: if (optlen < 1)
1000: return -EINVAL;
1001: inet_assign_bit(MC_LOOP, sk, val);
1002: return 0;
1003: case IP_MULTICAST_ALL:
1004: if (optlen < 1)
1005: return -EINVAL;
1006: if (val != 0 && val != 1)
1007: return -EINVAL;
1008: inet_assign_bit(MC_ALL, sk, val);
1009: return 0;
1010: case IP_TRANSPARENT:
1011: if (!!val && !sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) &&
1012: !sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
1013: return -EPERM;
1014: if (optlen < 1)
1015: return -EINVAL;
1016: inet_assign_bit(TRANSPARENT, sk, val);
1017: return 0;
1018: case IP_NODEFRAG:
1019: if (sk->sk_type != SOCK_RAW)
1020: return -ENOPROTOOPT;
1021: inet_assign_bit(NODEFRAG, sk, val);
1022: return 0;
1023: case IP_BIND_ADDRESS_NO_PORT:
1024: inet_assign_bit(BIND_ADDRESS_NO_PORT, sk, val);
1025: return 0;
1026: case IP_TTL:
1027: if (optlen < 1)
1028: return -EINVAL;
1029: if (val != -1 && (val < 1 || val > 255))
1030: return -EINVAL;
1031: WRITE_ONCE(inet->uc_ttl, val);
1032: return 0;
1033: case IP_MINTTL:
1034: if (optlen < 1)
1035: return -EINVAL;
1036: if (val < 0 || val > 255)
1037: return -EINVAL;
1038:
1039: if (val)
1040: static_branch_enable(&ip4_min_ttl);
1041:
1042: WRITE_ONCE(inet->min_ttl, val);
1043: return 0;
1044: case IP_MULTICAST_TTL:
1045: if (sk->sk_type == SOCK_STREAM)
1046: return -EINVAL;
1047: if (optlen < 1)
1048: return -EINVAL;
1049: if (val == -1)
1050: val = 1;
1051: if (val < 0 || val > 255)
1052: return -EINVAL;
1053: WRITE_ONCE(inet->mc_ttl, val);
1054: return 0;
1055: case IP_MTU_DISCOVER:
1056: return ip_sock_set_mtu_discover(sk, val);
1057: case IP_TOS: /* This sets both TOS and Precedence */
1058: ip_sock_set_tos(sk, val);
1059: return 0;
1060: case IP_LOCAL_PORT_RANGE:
1061: {
1062: u16 lo = val;
1063: u16 hi = val >> 16;
1064:
1065: if (optlen != sizeof(u32))
1066: return -EINVAL;
1067: if (lo != 0 && hi != 0 && lo > hi)
1068: return -EINVAL;
1069:
1070: WRITE_ONCE(inet->local_port_range, val);
1071: return 0;
1072: }
1073: }
1074:
1075: err = 0;
1076: if (needs_rtnl)
1077: rtnl_lock();
1078: sockopt_lock_sock(sk);
1079:
1080: switch (optname) {
1081: case IP_OPTIONS:
1082: {
1083: struct ip_options_rcu *old, *opt = NULL;
1084:
1085: if (optlen > 40)
1086: goto e_inval;
1087: err = ip_options_get(sock_net(sk), &opt, optval, optlen);
1088: if (err)
1089: break;
1090: old = rcu_dereference_protected(inet->inet_opt,
1091: lockdep_sock_is_held(sk));
1092: if (inet_test_bit(IS_ICSK, sk)) {
1093: struct inet_connection_sock *icsk = inet_csk(sk);
1094: #if IS_ENABLED(CONFIG_IPV6)
1095: if (sk->sk_family == PF_INET ||
1096: (!((1 << sk->sk_state) &
1097: (TCPF_LISTEN | TCPF_CLOSE)) &&
1098: inet->inet_daddr != LOOPBACK4_IPV6)) {
1099: #endif
1100: if (old)
1101: icsk->icsk_ext_hdr_len -= old->opt.optlen;
1102: if (opt)
1103: icsk->icsk_ext_hdr_len += opt->opt.optlen;
1104: icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
1105: #if IS_ENABLED(CONFIG_IPV6)
1106: }
1107: #endif
1108: }
1109: rcu_assign_pointer(inet->inet_opt, opt);
1110: if (old)
1111: kfree_rcu(old, rcu);
1112: break;
1113: }
1114: case IP_CHECKSUM:
1115: if (val) {
1116: if (!(inet_test_bit(CHECKSUM, sk))) {
1117: inet_inc_convert_csum(sk);
1118: inet_set_bit(CHECKSUM, sk);
1119: }
1120: } else {
1121: if (inet_test_bit(CHECKSUM, sk)) {
1122: inet_dec_convert_csum(sk);
1123: inet_clear_bit(CHECKSUM, sk);
1124: }
1125: }
1126: break;
1127: case IP_UNICAST_IF:
1128: {
1129: struct net_device *dev = NULL;
1130: int ifindex;
1131: int midx;
1132:
1133: if (optlen != sizeof(int))
1134: goto e_inval;
1135:
1136: ifindex = (__force int)ntohl((__force __be32)val);
1137: if (ifindex == 0) {
1138: WRITE_ONCE(inet->uc_index, 0);
1139: err = 0;
1140: break;
1141: }
1142:
1143: dev = dev_get_by_index(sock_net(sk), ifindex);
1144: err = -EADDRNOTAVAIL;
1145: if (!dev)
1146: break;
1147:
1148: midx = l3mdev_master_ifindex(dev);
1149: dev_put(dev);
1150:
1151: err = -EINVAL;
1152: if (sk->sk_bound_dev_if && midx != sk->sk_bound_dev_if)
1153: break;
1154:
1155: WRITE_ONCE(inet->uc_index, ifindex);
1156: err = 0;
1157: break;
1158: }
1159: case IP_MULTICAST_IF:
1160: {
1161: struct ip_mreqn mreq;
1162: struct net_device *dev = NULL;
1163: int midx;
1164:
1165: if (sk->sk_type == SOCK_STREAM)
1166: goto e_inval;
1167: /*
1168: * Check the arguments are allowable
1169: */
1170:
1171: if (optlen < sizeof(struct in_addr))
1172: goto e_inval;
1173:
1174: err = -EFAULT;
1175: if (optlen >= sizeof(struct ip_mreqn)) {
1176: if (copy_from_sockptr(&mreq, optval, sizeof(mreq)))
1177: break;
1178: } else {
1179: memset(&mreq, 0, sizeof(mreq));
1180: if (optlen >= sizeof(struct ip_mreq)) {
1181: if (copy_from_sockptr(&mreq, optval,
1182: sizeof(struct ip_mreq)))
1183: break;
1184: } else if (optlen >= sizeof(struct in_addr)) {
1185: if (copy_from_sockptr(&mreq.imr_address, optval,
1186: sizeof(struct in_addr)))
1187: break;
1188: }
1189: }
1190:
1191: if (!mreq.imr_ifindex) {
1192: if (mreq.imr_address.s_addr == htonl(INADDR_ANY)) {
1193: WRITE_ONCE(inet->mc_index, 0);
1194: WRITE_ONCE(inet->mc_addr, 0);
1195: err = 0;
1196: break;
1197: }
1198: dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr);
1199: if (dev)
1200: mreq.imr_ifindex = dev->ifindex;
1201: } else
1202: dev = dev_get_by_index(sock_net(sk), mreq.imr_ifindex);
1203:
1204:
1205: err = -EADDRNOTAVAIL;
1206: if (!dev)
1207: break;
1208:
1209: midx = l3mdev_master_ifindex(dev);
1210:
1211: dev_put(dev);
1212:
1213: err = -EINVAL;
1214: if (sk->sk_bound_dev_if &&
1215: mreq.imr_ifindex != sk->sk_bound_dev_if &&
1216: midx != sk->sk_bound_dev_if)
1217: break;
1218:
1219: WRITE_ONCE(inet->mc_index, mreq.imr_ifindex);
1220: WRITE_ONCE(inet->mc_addr, mreq.imr_address.s_addr);
1221: err = 0;
1222: break;
1223: }
1224:
1225: case IP_ADD_MEMBERSHIP:
1226: case IP_DROP_MEMBERSHIP:
1227: {
1228: struct ip_mreqn mreq;
1229:
1230: err = -EPROTO;
1231: if (inet_test_bit(IS_ICSK, sk))
1232: break;
1233:
1234: if (optlen < sizeof(struct ip_mreq))
1235: goto e_inval;
1236: err = -EFAULT;
1237: if (optlen >= sizeof(struct ip_mreqn)) {
1238: if (copy_from_sockptr(&mreq, optval, sizeof(mreq)))
1239: break;
1240: } else {
1241: memset(&mreq, 0, sizeof(mreq));
1242: if (copy_from_sockptr(&mreq, optval,
1243: sizeof(struct ip_mreq)))
1244: break;
1245: }
1246:
1247: if (optname == IP_ADD_MEMBERSHIP)
1248: err = ip_mc_join_group(sk, &mreq);
1249: else
1250: err = ip_mc_leave_group(sk, &mreq);
1251: break;
1252: }
1253: case IP_MSFILTER:
1254: {
1255: struct ip_msfilter *msf;
1256:
1257: if (optlen < IP_MSFILTER_SIZE(0))
1258: goto e_inval;
1259: if (optlen > READ_ONCE(net->core.sysctl_optmem_max)) {
1260: err = -ENOBUFS;
1261: break;
1262: }
1263: msf = memdup_sockptr(optval, optlen);
1264: if (IS_ERR(msf)) {
1265: err = PTR_ERR(msf);
1266: break;
1267: }
1268: /* numsrc >= (1G-4) overflow in 32 bits */
1269: if (msf->imsf_numsrc >= 0x3ffffffcU ||
1270: msf->imsf_numsrc > READ_ONCE(net->ipv4.sysctl_igmp_max_msf)) {
1271: kfree(msf);
1272: err = -ENOBUFS;
1273: break;
1274: }
1275: if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) {
1276: kfree(msf);
1277: err = -EINVAL;
1278: break;
1279: }
1280: err = ip_mc_msfilter(sk, msf, 0);
1281: kfree(msf);
1282: break;
1283: }
1284: case IP_BLOCK_SOURCE:
1285: case IP_UNBLOCK_SOURCE:
1286: case IP_ADD_SOURCE_MEMBERSHIP:
1287: case IP_DROP_SOURCE_MEMBERSHIP:
1288: {
1289: struct ip_mreq_source mreqs;
1290: int omode, add;
1291:
1292: if (optlen != sizeof(struct ip_mreq_source))
1293: goto e_inval;
1294: if (copy_from_sockptr(&mreqs, optval, sizeof(mreqs))) {
1295: err = -EFAULT;
1296: break;
1297: }
1298: if (optname == IP_BLOCK_SOURCE) {
1299: omode = MCAST_EXCLUDE;
1300: add = 1;
1301: } else if (optname == IP_UNBLOCK_SOURCE) {
1302: omode = MCAST_EXCLUDE;
1303: add = 0;
1304: } else if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
1305: struct ip_mreqn mreq;
1306:
1307: mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
1308: mreq.imr_address.s_addr = mreqs.imr_interface;
1309: mreq.imr_ifindex = 0;
1310: err = ip_mc_join_group_ssm(sk, &mreq, MCAST_INCLUDE);
1311: if (err && err != -EADDRINUSE)
1312: break;
1313: omode = MCAST_INCLUDE;
1314: add = 1;
1315: } else /* IP_DROP_SOURCE_MEMBERSHIP */ {
1316: omode = MCAST_INCLUDE;
1317: add = 0;
1318: }
1319: err = ip_mc_source(add, omode, sk, &mreqs, 0);
1320: break;
1321: }
1322: case MCAST_JOIN_GROUP:
1323: case MCAST_LEAVE_GROUP:
1324: if (in_compat_syscall())
1325: err = compat_ip_mcast_join_leave(sk, optname, optval,
1326: optlen);
1327: else
1328: err = ip_mcast_join_leave(sk, optname, optval, optlen);
1329: break;
1330: case MCAST_JOIN_SOURCE_GROUP:
1331: case MCAST_LEAVE_SOURCE_GROUP:
1332: case MCAST_BLOCK_SOURCE:
1333: case MCAST_UNBLOCK_SOURCE:
1334: err = do_mcast_group_source(sk, optname, optval, optlen);
1335: break;
1336: case MCAST_MSFILTER:
1337: if (in_compat_syscall())
1338: err = compat_ip_set_mcast_msfilter(sk, optval, optlen);
1339: else
1340: err = ip_set_mcast_msfilter(sk, optval, optlen);
1341: break;
1342: case IP_IPSEC_POLICY:
1343: case IP_XFRM_POLICY:
1344: err = -EPERM;
1345: if (!sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
1346: break;
1347: err = xfrm_user_policy(sk, optname, optval, optlen);
1348: break;
1349:
1350: default:
1351: err = -ENOPROTOOPT;
1352: break;
1353: }
1354: sockopt_release_sock(sk);
1355: if (needs_rtnl)
1356: rtnl_unlock();
1357: return err;
1358:
1359: e_inval:
1360: sockopt_release_sock(sk);
1361: if (needs_rtnl)
1362: rtnl_unlock();
1363: return -EINVAL;
1364: }
SourceFile:net/ipv4/ip_sockglue.c]
|
| 32/2 |
2026/05/31 18:14 |
llm |
expert |
0m
Model:
Tokens:
input: 32344
output: 40
thoughts: 246
Thoughts: **My Assessment of the batman-adv Multicast Vulnerability**
Okay, so `IP_ADD_MEMBERSHIP` doesn't require any special privileges. Interesting. That means a regular, unprivileged user could, in theory, create a bunch of sockets, hook themselves up to multicast groups on a `batman-adv` interface, and potentially cause a softlockup in the `batman-adv` worker thread. Hmmm.
Now, a critical question arises: does the user even *need* to be able to create the `batman-adv` interface in the first place? No, apparently not. The interface just needs to be *there*. So, if a system *already* has a `batman-adv` interface, any user can start joining multicast groups on it.
Aha! So the real question becomes, if a `batman-adv` interface doesn't exist, can the exploit even be triggered? Seems not. The vulnerability is inert unless that interface is active.
Okay, so the system *has* a `batman-adv` interface already, this means any unprivileged user can trigger the vulnerability. But can an unprivileged user just *create* the `batman-adv` interface themselves? No, because that requires `CAP_NET_ADMIN`.
Wait a minute... if the user is in a user namespace, then they *could* get `CAP_NET_ADMIN` and create that `batman-adv` interface, couldn't they? That’s where the vulnerability could be.
So, the critical question becomes: can `batman-adv` even be created in a user namespace? Let's check if it is netns-aware. That's the key.
|
| 33/2 |
2026/05/31 18:14 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:net/batman-adv/main.c Name:batadv_net_init]
Results: map[SourceCode: SourceFile:]
|
| 34/2 |
2026/05/31 18:14 |
llm |
expert |
0m
Model:
Tokens:
input: 33018
output: 31
thoughts: 23
Thoughts: Here's my thought process, summarized as you requested:
**Diving into batman-adv's Core Network Initialization**
Okay, so I need to understand how batman-adv interacts with the network stack at a fundamental level. I'm thinking the best place to start is likely the core initialization code. Where does it hook in, and how does it register itself? Given my experience with kernel modules, and after a quick scan of the module's structure, the place to start poking around is `net/batman-adv/main.c`. Specifically, I'm going to focus on where the code registers itself with the networking subsystem. That usually involves `pernet_operations` or similar structures, so I'll be looking for any usage of that within the `main.c` file. I'm expecting to find how it sets up its network namespaces, and I hope to understand when the basic datastructures are initialized. Let's see what we can find.
|
| 35/2 |
2026/05/31 18:14 |
tool |
grepper |
0m
Error:
Args: map[Expression:pernet_operations PathPrefix:net/batman-adv/]
Results:
|
| 36/2 |
2026/05/31 18:14 |
llm |
expert |
0m
Model:
Tokens:
input: 33291
output: 33
thoughts: 40
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating batman-adv and Network Namespaces**
Hold on a minute... I was under the impression that batman-adv was properly integrated with network namespaces. I need to confirm this. The implications are pretty significant if it's not. I'm going to dig into the source code, specifically `net/batman-adv/soft-interface.c`. That's where the soft interface logic lives, and where I'd expect to find the relevant `rtnl_link_ops` implementation, which is crucial for how the interface interacts with the kernel's networking stack, including namespace support. That's the first place to look to see if this is truly netns-aware. This is fundamental; I need to verify that interaction.
|
| 37/2 |
2026/05/31 18:14 |
tool |
grepper |
0m
Args: map[Expression:rtnl_link_ops PathPrefix:net/batman-adv/]
Results: map[Output:net/batman-adv/hard-interface.c=67=batadv_hardif_get_by_netdev(const struct net_device *net_dev)
--
net/batman-adv/hard-interface.c-89- *
net/batman-adv/hard-interface.c:90: * Return: result of rtnl_link_ops->get_link_net or @fallback_net
net/batman-adv/hard-interface.c-91- */
net/batman-adv/hard-interface.c=92=static struct net *batadv_getlink_net(const struct net_device *netdev,
--
net/batman-adv/hard-interface.c-94-{
net/batman-adv/hard-interface.c:95: if (!netdev->rtnl_link_ops)
net/batman-adv/hard-interface.c-96- return fallback_net;
net/batman-adv/hard-interface.c-97-
net/batman-adv/hard-interface.c:98: if (!netdev->rtnl_link_ops->get_link_net)
net/batman-adv/hard-interface.c-99- return fallback_net;
net/batman-adv/hard-interface.c-100-
net/batman-adv/hard-interface.c:101: return netdev->rtnl_link_ops->get_link_net(netdev);
net/batman-adv/hard-interface.c-102-}
--
net/batman-adv/mesh-interface.c=1120=static const struct nla_policy batadv_ifla_policy[IFLA_BATADV_MAX + 1] = {
--
net/batman-adv/mesh-interface.c-1123-
net/batman-adv/mesh-interface.c:1124:struct rtnl_link_ops batadv_link_ops __read_mostly = {
net/batman-adv/mesh-interface.c-1125- .kind = "batadv",
--
net/batman-adv/mesh-interface.h=21=bool batadv_meshif_is_valid(const struct net_device *net_dev);
net/batman-adv/mesh-interface.h:22:extern struct rtnl_link_ops batadv_link_ops;
net/batman-adv/mesh-interface.h-23-int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid);
]
|
| 38/2 |
2026/05/31 18:14 |
llm |
expert |
0m
Model:
Tokens:
input: 34183
output: 42
thoughts: 27
Thoughts: Here's my take on those thoughts:
**Navigating the Batman-adv Codebase**
Okay, so I'm trying to figure out where this `batadv_link_ops` is defined. Right, a quick scan, and yep, it's pretty clear that it lives in `net/batman-adv/mesh-interface.c`. Shouldn't be too hard to find the actual declaration and implementation then, likely in the same file. Time to dig in and see what's going on with it.
|
| 39/2 |
2026/05/31 18:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/mesh-interface.c Name:batadv_link_ops]
Results: map[SourceCode:1124: struct rtnl_link_ops batadv_link_ops __read_mostly = {
1125: .kind = "batadv",
1126: .priv_size = sizeof(struct batadv_priv),
1127: .setup = batadv_meshif_init_early,
1128: .maxtype = IFLA_BATADV_MAX,
1129: .policy = batadv_ifla_policy,
1130: .validate = batadv_meshif_validate,
1131: .newlink = batadv_meshif_newlink,
1132: .dellink = batadv_meshif_destroy_netlink,
1133: };
SourceFile:net/batman-adv/mesh-interface.c]
|
| 40/2 |
2026/05/31 18:14 |
llm |
expert |
0m
Model:
Tokens:
input: 34594
output: 44
thoughts: 51
Thoughts: **Investigating Network Namespace Creation**
Okay, so I'm trying to figure out if there's a specific netlink command or operation, like a hypothetical `.netns_refund`, that's involved in network namespace creation. My initial thought is about some refund or compensation related to a missing netlink function. Not likely.
Now, wait a minute... if the `rtnl_link_ops` structure is properly registered within the kernel, does that automatically mean a new link type can be created within a user namespace? That's the real question. I need to understand the connection between registration and user namespace capabilities. This is likely the best path to find my answer.
Let me take a look at the `batadv_meshif_newlink` function. That's a solid example of a link type implementation. Examining its structure should illuminate how new link types are handled and if the namespace aspect comes into play. The details of that function will likely confirm or deny if new network types can be created in a user namespace.
|
| 41/2 |
2026/05/31 18:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/mesh-interface.c Name:batadv_meshif_newlink]
Results: map[SourceCode:1051: /**
1052: * batadv_meshif_newlink() - pre-initialize and register new batadv link
1053: * @dev: network device to register
1054: * @params: rtnl newlink parameters
1055: * @extack: extended ACK report struct
1056: *
1057: * Return: 0 if successful or error otherwise.
1058: */
1059: static int batadv_meshif_newlink(struct net_device *dev,
1060: struct rtnl_newlink_params *params,
1061: struct netlink_ext_ack *extack)
1062: {
1063: struct batadv_priv *bat_priv = netdev_priv(dev);
1064: struct nlattr **data = params->data;
1065: const char *algo_name;
1066: int err;
1067:
1068: if (data && data[IFLA_BATADV_ALGO_NAME]) {
1069: algo_name = nla_data(data[IFLA_BATADV_ALGO_NAME]);
1070: err = batadv_algo_select(bat_priv, algo_name);
1071: if (err)
1072: return -EINVAL;
1073: }
1074:
1075: return register_netdevice(dev);
1076: }
SourceFile:net/batman-adv/mesh-interface.c]
|
| 42/2 |
2026/05/31 18:14 |
llm |
expert |
0m
Model:
Tokens:
input: 35335
output: 38
thoughts: 19
Thoughts: Here's my thought process, as if I were considering this code:
**Thinking Through Link Registration in batadv_init**
Okay, so I'm tracing through this initialization, and the `rtnl_link_register` function call in `batadv_init` just popped up. I need to think through this. I know that `rtnl_link_register` is a crucial function for, well, *registering* a network link type with the kernel's netlink subsystem. That's how the kernel knows about this new link type (in this case, I'm assuming it's related to the B.A.T.M.A.N. advanced virtual network interface) and how it can then create and manage it.
So, in the context of `batadv_init`, the B.A.T.M.A.N. advanced module is essentially telling the kernel, "Hey, I'm here! I'm responsible for a new type of network link. Please let me handle creating, destroying, and configuring interfaces of this type." This registration is vital; without it, the kernel wouldn't know how to handle the `bat0` or whatever the interfaces might be. Makes total sense.
This registration will include providing the kernel with associated function pointers for things like link configuration, statistics, and other operational hooks. The details of how this happens will be what I should review next.
|
| 43/2 |
2026/05/31 18:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/main.c Name:batadv_init]
Results: map[SourceCode: 89: static int __init batadv_init(void)
90: {
91: int ret;
92:
93: ret = batadv_tt_cache_init();
94: if (ret < 0)
95: return ret;
96:
97: INIT_LIST_HEAD(&batadv_hardif_list);
98: batadv_algo_init();
99:
100: batadv_recv_handler_init();
101:
102: batadv_v_init();
103: batadv_iv_init();
104: batadv_tp_meter_init();
105:
106: batadv_event_workqueue = create_singlethread_workqueue("bat_events");
107: if (!batadv_event_workqueue)
108: goto err_create_wq;
109:
110: register_netdevice_notifier(&batadv_hard_if_notifier);
111: rtnl_link_register(&batadv_link_ops);
112: batadv_netlink_register();
113:
114: pr_info("B.A.T.M.A.N. advanced %s (compatibility version %i) loaded\n",
115: BATADV_SOURCE_VERSION, BATADV_COMPAT_VERSION);
116:
117: return 0;
118:
119: err_create_wq:
120: batadv_tt_cache_destroy();
121:
122: return -ENOMEM;
123: }
SourceFile:net/batman-adv/main.c]
|
| 44/2 |
2026/05/31 18:14 |
llm |
expert |
0m
Model:
Tokens:
input: 36194
output: 39
thoughts: 122
Thoughts: **Examining the Implications of Global Link Operation Registration**
Okay, so `rtnl_link_register` registers these link operations *globally*. That's the crux of it. My initial thought is, can a link type registered this way even be created within a network namespace that isn't the initial one? This is a critical point when considering namespace isolation.
Specifically, if the `batadv_link_ops` structure *doesn't* have attributes like `netns_refund` or explicitly indicate that it's `netns_aware`, can it still be created within a user namespace? That's the core question, and it has potential security implications.
Wait a moment... I think I'm overthinking the direct registration. The key is really the `CAP_NET_ADMIN` capability. As I understand it, any `rtnl_link_ops` can, in theory, be leveraged to create a link in a network namespace, *provided* the user or process has the `CAP_NET_ADMIN` capability within *that specific* namespace. The only way around this would be if the link operation is explicitly *restricted* in some way.
I'm probably getting ahead of myself, let's go back and examine `rtnl_link_register` and its implications directly.
|
| 45/2 |
2026/05/31 18:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/rtnetlink.c Name:rtnl_link_register]
Results: map[SourceCode: 591: /**
592: * rtnl_link_register - Register rtnl_link_ops with rtnetlink.
593: * @ops: struct rtnl_link_ops * to register
594: *
595: * Returns 0 on success or a negative error code.
596: */
597: int rtnl_link_register(struct rtnl_link_ops *ops)
598: {
599: struct rtnl_link_ops *tmp;
600: int err;
601:
602: /* Sanity-check max sizes to avoid stack buffer overflow. */
603: if (WARN_ON(ops->maxtype > RTNL_MAX_TYPE ||
604: ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE))
605: return -EINVAL;
606:
607: /* The check for alloc/setup is here because if ops
608: * does not have that filled up, it is not possible
609: * to use the ops for creating device. So do not
610: * fill up dellink as well. That disables rtnl_dellink.
611: */
612: if ((ops->alloc || ops->setup) && !ops->dellink)
613: ops->dellink = unregister_netdevice_queue;
614:
615: err = init_srcu_struct(&ops->srcu);
616: if (err)
617: return err;
618:
619: mutex_lock(&link_ops_mutex);
620:
621: list_for_each_entry(tmp, &link_ops, list) {
622: if (!strcmp(ops->kind, tmp->kind)) {
623: err = -EEXIST;
624: goto unlock;
625: }
626: }
627:
628: list_add_tail_rcu(&ops->list, &link_ops);
629: unlock:
630: mutex_unlock(&link_ops_mutex);
631:
632: return err;
633: }
SourceFile:net/core/rtnetlink.c]
|
| 46/2 |
2026/05/31 18:14 |
llm |
expert |
0m
Model:
Tokens:
input: 37292
output: 38
thoughts: 43
Thoughts: Okay, here's my interpretation of those thoughts, summarized from my perspective as a networking expert:
**Investigating Network Namespace Capability Requirements**
Alright, let me see... I'm looking at `rtnl_newlink` and it seems to be checking for the `CAP_NET_ADMIN` capability within the *current* network namespace. That's as expected. The critical question now is whether the operation requires `CAP_NET_ADMIN` in the *initial* netns as well. Is the required permission in the namespace where the command is being *called* or where the network link will be *created*? This distinction is absolutely crucial for understanding the overall security implications, potential privilege escalation vectors, and how best to design and secure these configurations. I need to dig deeper into the code to determine the permission contexts involved, the ownership, and whether the init namespace’s capabilities play any role here.
|
| 47/2 |
2026/05/31 18:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/rtnetlink.c Name:rtnl_newlink]
Results: map[SourceCode:3961: static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
3962: struct netlink_ext_ack *extack)
3963: {
3964: struct net *tgt_net, *link_net = NULL, *peer_net = NULL;
3965: struct nlattr **tb, **linkinfo, **data = NULL;
3966: struct rtnl_link_ops *ops = NULL;
3967: struct rtnl_newlink_tbs *tbs;
3968: struct rtnl_nets rtnl_nets;
3969: int ops_srcu_index;
3970: int ret;
3971:
3972: tbs = kmalloc_obj(*tbs);
3973: if (!tbs)
3974: return -ENOMEM;
3975:
3976: tb = tbs->tb;
3977: ret = nlmsg_parse_deprecated(nlh, sizeof(struct ifinfomsg), tb,
3978: IFLA_MAX, ifla_policy, extack);
3979: if (ret < 0)
3980: goto free;
3981:
3982: ret = rtnl_ensure_unique_netns(tb, extack, false);
3983: if (ret < 0)
3984: goto free;
3985:
3986: linkinfo = tbs->linkinfo;
3987: if (tb[IFLA_LINKINFO]) {
3988: ret = nla_parse_nested_deprecated(linkinfo, IFLA_INFO_MAX,
3989: tb[IFLA_LINKINFO],
3990: ifla_info_policy, NULL);
3991: if (ret < 0)
3992: goto free;
3993: } else {
3994: memset(linkinfo, 0, sizeof(tbs->linkinfo));
3995: }
3996:
3997: if (linkinfo[IFLA_INFO_KIND]) {
3998: char kind[MODULE_NAME_LEN];
3999:
4000: nla_strscpy(kind, linkinfo[IFLA_INFO_KIND], sizeof(kind));
4001: ops = rtnl_link_ops_get(kind, &ops_srcu_index);
4002: #ifdef CONFIG_MODULES
4003: if (!ops) {
4004: request_module("rtnl-link-%s", kind);
4005: ops = rtnl_link_ops_get(kind, &ops_srcu_index);
4006: }
4007: #endif
4008: }
4009:
4010: rtnl_nets_init(&rtnl_nets);
4011:
4012: if (ops) {
4013: if (ops->maxtype > RTNL_MAX_TYPE) {
4014: ret = -EINVAL;
4015: goto put_ops;
4016: }
4017:
4018: if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) {
4019: ret = nla_parse_nested_deprecated(tbs->attr, ops->maxtype,
4020: linkinfo[IFLA_INFO_DATA],
4021: ops->policy, extack);
4022: if (ret < 0)
4023: goto put_ops;
4024:
4025: data = tbs->attr;
4026: }
4027:
4028: if (ops->validate) {
4029: ret = ops->validate(tb, data, extack);
4030: if (ret < 0)
4031: goto put_ops;
4032: }
4033:
4034: if (ops->peer_type) {
4035: peer_net = rtnl_get_peer_net(ops, tb, data, extack);
4036: if (IS_ERR(peer_net)) {
4037: ret = PTR_ERR(peer_net);
4038: goto put_ops;
4039: }
4040: if (peer_net)
4041: rtnl_nets_add(&rtnl_nets, peer_net);
4042: }
4043: }
4044:
4045: tgt_net = rtnl_link_get_net_capable(skb, sock_net(skb->sk), tb, CAP_NET_ADMIN);
4046: if (IS_ERR(tgt_net)) {
4047: ret = PTR_ERR(tgt_net);
4048: goto put_net;
4049: }
4050:
4051: rtnl_nets_add(&rtnl_nets, tgt_net);
4052:
4053: if (tb[IFLA_LINK_NETNSID]) {
4054: int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);
4055:
4056: link_net = get_net_ns_by_id(tgt_net, id);
4057: if (!link_net) {
4058: NL_SET_ERR_MSG(extack, "Unknown network namespace id");
4059: ret = -EINVAL;
4060: goto put_net;
4061: }
4062:
4063: rtnl_nets_add(&rtnl_nets, link_net);
4064:
4065: if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN)) {
4066: ret = -EPERM;
4067: goto put_net;
4068: }
4069: }
4070:
4071: rtnl_nets_lock(&rtnl_nets);
4072: ret = __rtnl_newlink(skb, nlh, ops, tgt_net, link_net, peer_net, tbs, data, extack);
4073: rtnl_nets_unlock(&rtnl_nets);
4074:
4075: put_net:
4076: rtnl_nets_destroy(&rtnl_nets);
4077: put_ops:
4078: if (ops)
4079: rtnl_link_ops_put(ops, ops_srcu_index);
4080: free:
4081: kfree(tbs);
4082: return ret;
4083: }
SourceFile:net/core/rtnetlink.c]
|
| 48/2 |
2026/05/31 18:14 |
llm |
expert |
0m
Model:
Tokens:
input: 39470
output: 39
thoughts: 84
Thoughts: Here's my thought process, as it just happened:
**Diving into the Netlink Code: CAP_NET_ADMIN and Link Operations**
Okay, so I'm looking at this Netlink code, specifically the `tgt_net = rtnl_link_get_net_capable(skb, sock_net(skb->sk), tb, CAP_NET_ADMIN);` line. This immediately flags a potential security boundary. I see it's checking for `CAP_NET_ADMIN` within the *target* network namespace. Makes perfect sense. We want to ensure the user has the necessary privileges in that specific namespace, not just the current one.
But then my mind jumps to a related question, and this is where I need to be *sure*. Does `__rtnl_newlink` (which is likely the calling function) also have a check? Does it ensure that the *specific* link operation being requested is actually *permitted* within that target network namespace? I need to confirm if there's a double check. This is crucial. If the privilege check only happens here, and the underlying link operations lack a similar filter, it could lead to potential vulnerabilities. I need to refresh my memory on that area...
|
| 49/2 |
2026/05/31 18:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/rtnetlink.c Name:__rtnl_newlink]
Results: map[SourceCode:3899: static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
3900: const struct rtnl_link_ops *ops,
3901: struct net *tgt_net, struct net *link_net,
3902: struct net *peer_net,
3903: struct rtnl_newlink_tbs *tbs,
3904: struct nlattr **data,
3905: struct netlink_ext_ack *extack)
3906: {
3907: struct nlattr ** const tb = tbs->tb;
3908: struct net *net = sock_net(skb->sk);
3909: struct net *device_net;
3910: struct net_device *dev;
3911: struct ifinfomsg *ifm;
3912: bool link_specified;
3913:
3914: /* When creating, lookup for existing device in target net namespace */
3915: device_net = (nlh->nlmsg_flags & NLM_F_CREATE) &&
3916: (nlh->nlmsg_flags & NLM_F_EXCL) ?
3917: tgt_net : net;
3918:
3919: ifm = nlmsg_data(nlh);
3920: if (ifm->ifi_index > 0) {
3921: link_specified = true;
3922: dev = __dev_get_by_index(device_net, ifm->ifi_index);
3923: } else if (ifm->ifi_index < 0) {
3924: NL_SET_ERR_MSG(extack, "ifindex can't be negative");
3925: return -EINVAL;
3926: } else if (tb[IFLA_IFNAME] || tb[IFLA_ALT_IFNAME]) {
3927: link_specified = true;
3928: dev = rtnl_dev_get(device_net, tb);
3929: } else {
3930: link_specified = false;
3931: dev = NULL;
3932: }
3933:
3934: if (dev)
3935: return rtnl_changelink(skb, nlh, ops, dev, tgt_net, tbs, data, extack);
3936:
3937: if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
3938: /* No dev found and NLM_F_CREATE not set. Requested dev does not exist,
3939: * or it's for a group
3940: */
3941: if (link_specified || !tb[IFLA_GROUP])
3942: return -ENODEV;
3943:
3944: return rtnl_group_changelink(skb, net, tgt_net,
3945: nla_get_u32(tb[IFLA_GROUP]),
3946: ifm, extack, tb);
3947: }
3948:
3949: if (tb[IFLA_MAP] || tb[IFLA_PROTINFO])
3950: return -EOPNOTSUPP;
3951:
3952: if (!ops) {
3953: NL_SET_ERR_MSG(extack, "Unknown device type");
3954: return -EOPNOTSUPP;
3955: }
3956:
3957: return rtnl_newlink_create(skb, ifm, ops, tgt_net, link_net, peer_net, nlh,
3958: tb, data, extack);
3959: }
SourceFile:net/core/rtnetlink.c]
|
| 50/2 |
2026/05/31 18:14 |
llm |
expert |
0m
Model:
Tokens:
input: 40916
output: 40
thoughts: 24
Thoughts: Here's a summary of my thought process, assuming I'm an expert in networking code:
**Thinking Through Link Operation Permissions**
Okay, I'm digging into this code and trying to figure out the flow. I'm wondering about the permissions when creating a new link. It just hit me - `rtnl_newlink_create` *could* be the place where we check if the link operations are actually permitted within the current network namespace. It makes sense, right? We're initiating the link creation here, so it's a logical point to enforce any netns-specific access restrictions. I need to trace through that function's logic and make sure it's doing the right thing, or at least that it *should* be doing that. Is there a call somewhere down the line, perhaps to a function that enforces the permissions? I should really check the implementation of that.
|
| 51/2 |
2026/05/31 18:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/rtnetlink.c Name:rtnl_newlink_create]
Results: map[SourceCode:3799: static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm,
3800: const struct rtnl_link_ops *ops,
3801: struct net *tgt_net, struct net *link_net,
3802: struct net *peer_net,
3803: const struct nlmsghdr *nlh,
3804: struct nlattr **tb, struct nlattr **data,
3805: struct netlink_ext_ack *extack)
3806: {
3807: unsigned char name_assign_type = NET_NAME_USER;
3808: struct rtnl_newlink_params params = {
3809: .src_net = sock_net(skb->sk),
3810: .link_net = link_net,
3811: .peer_net = peer_net,
3812: .tb = tb,
3813: .data = data,
3814: };
3815: u32 portid = NETLINK_CB(skb).portid;
3816: struct net_device *dev;
3817: char ifname[IFNAMSIZ];
3818: int err;
3819:
3820: if (!ops->alloc && !ops->setup)
3821: return -EOPNOTSUPP;
3822:
3823: if (tb[IFLA_IFNAME]) {
3824: nla_strscpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
3825: } else {
3826: snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind);
3827: name_assign_type = NET_NAME_ENUM;
3828: }
3829:
3830: dev = rtnl_create_link(tgt_net, ifname, name_assign_type, ops, tb,
3831: extack);
3832: if (IS_ERR(dev)) {
3833: err = PTR_ERR(dev);
3834: goto out;
3835: }
3836:
3837: dev->ifindex = ifm->ifi_index;
3838:
3839: if (ops->newlink)
3840: err = ops->newlink(dev, ¶ms, extack);
3841: else
3842: err = register_netdevice(dev);
3843: if (err < 0) {
3844: free_netdev(dev);
3845: goto out;
3846: }
3847:
3848: netdev_lock_ops(dev);
3849:
3850: err = rtnl_configure_link(dev, ifm, portid, nlh);
3851: if (err < 0)
3852: goto out_unregister;
3853: if (tb[IFLA_MASTER]) {
3854: err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]), extack);
3855: if (err)
3856: goto out_unregister;
3857: }
3858:
3859: netdev_unlock_ops(dev);
3860: out:
3861: return err;
3862: out_unregister:
3863: netdev_unlock_ops(dev);
3864: if (ops->newlink) {
3865: LIST_HEAD(list_kill);
3866:
3867: ops->dellink(dev, &list_kill);
3868: unregister_netdevice_many(&list_kill);
3869: } else {
3870: unregister_netdevice(dev);
3871: }
3872: goto out;
3873: }
SourceFile:net/core/rtnetlink.c]
|
| 52/2 |
2026/05/31 18:14 |
llm |
expert |
0m
Model:
Tokens:
input: 42299
output: 44
thoughts: 339
Thoughts: **My Line of Reasoning**
Okay, hold on a second. I need to think through this `rtnl_create_link` function. Could it possibly be checking something related to `netns_refund` or similar? Wait, on second thought, any `rtnl_link_ops` implementation has the potential to let a user with `CAP_NET_ADMIN` in a netns create a device *within* that namespace. Unless the module explicitly blocks it, of course.
Hmm, let me see... I recall `batadv_meshif_newlink` doesn't seem to be explicitly checking `net_eq(dev_net(dev), &init_net)`. This is interesting. So, theoretically, an unprivileged user *could* create a user namespace, acquire `CAP_NET_ADMIN` there, and then instantiate a `batadv` interface?
Now, the next thing that pops into mind is if the `batman-adv` module is loaded automatically under these circumstances. `rtnl_newlink` does this:
```c
#ifdef CONFIG_MODULES
if (!ops) {
request_module("rtnl-link-%s", kind);
ops = rtnl_link_ops_get(kind, &ops_srcu_index);
}
#endif
```
If the user specifically requests `kind="batadv"`, it would attempt to load `rtnl-link-batadv`, which is, as I recall, an alias for `batman-adv`. So *yes*, a non-privileged user can indeed trigger the loading of the `batman-adv` module, create a `batadv` interface, and consequently, trigger the potential bug.
Now, does the `batadv` interface need to be in an "UP" state for the bug to be exposed? Let me check `batadv_mcast_mla_update`, which is a workqueue item. I'll need to figure out the conditions under which that workqueue item is scheduled.
|
| 53/2 |
2026/05/31 18:14 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/batman-adv/multicast.c Name:batadv_mcast_mla_update]
Results: map[References:[map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:batadv_mcast_init SourceFile:net/batman-adv/multicast.c SourceLine:1928]] TruncatedOutput:false]
|
| 54/2 |
2026/05/31 18:14 |
llm |
expert |
0m
Model:
Tokens:
input: 43196
output: 42
thoughts: 13
Thoughts: Okay, let's dive into this. Here's my thought process summarized, assuming I'm already pretty familiar with this area:
**Analyzing the `batadv_mcast_init` Function**
Alright, the first thing I'm doing is homing in on `batadv_mcast_init`. Why? Because based on the name, it's obviously the initialization function for BATMAN Advanced multicast support. That's a critical component, so it makes sense to start here. I'm going to want to understand how this function sets things up, how it integrates with the rest of the BATMAN stack, what data structures it likely touches, and what kind of resources it allocates. This is the starting point for figuring out how BATMAN handles multicast in the first place. This function is likely the bedrock upon which the rest of the multicast mechanisms rely, so it's a smart place to start. I expect it probably does things like registering handlers, initializing some internal state, and potentially allocating some key data structures. Knowing this is the "launch pad" helps me place everything else within the BATMAN Advanced framework. Time to dig in!
|
| 55/2 |
2026/05/31 18:15 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/multicast.c Name:batadv_mcast_init]
Results: map[SourceCode:1914: /**
1915: * batadv_mcast_init() - initialize the multicast optimizations structures
1916: * @bat_priv: the bat priv with all the mesh interface information
1917: */
1918: void batadv_mcast_init(struct batadv_priv *bat_priv)
1919: {
1920: batadv_tvlv_handler_register(bat_priv, batadv_mcast_tvlv_ogm_handler,
1921: NULL, NULL, BATADV_TVLV_MCAST, 2,
1922: BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
1923: batadv_tvlv_handler_register(bat_priv, NULL, NULL,
1924: batadv_mcast_forw_tracker_tvlv_handler,
1925: BATADV_TVLV_MCAST_TRACKER, 1,
1926: BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
1927:
1928: INIT_DELAYED_WORK(&bat_priv->mcast.work, batadv_mcast_mla_update);
1929: batadv_mcast_start_timer(bat_priv);
1930: }
SourceFile:net/batman-adv/multicast.c]
|
| 56/2 |
2026/05/31 18:15 |
llm |
expert |
0m
Model:
Tokens:
input: 43866
output: 44
thoughts: 15
Thoughts: Okay, here's my attempt at summarizing the thought process, as if it were my own, assuming I'm an expert in the field:
**BatAdv Multicast Timer Start: A Deep Dive**
Alright, so `batadv_mcast_start_timer` is the entry point, the trigger. My initial thought is, "Okay, we're dealing with multicast here, and likely something network-related within BATMAN-Adv." This function, as the name suggests, is responsible for initiating something related to a timer. My expertise immediately tells me this implies a delayed action or a periodic event. The code clearly *schedules* the work. What is this scheduled work? Well, that depends on the broader context, but the implication is that something isn't happening immediately and the timer is how we'll trigger the required function at a later time. We're deferring something. I'd need more code context to know exactly what; it could be retransmitting multicast packets, performing some periodic network maintenance, or adjusting multicast forwarding tables. But the core concept is, we need something to happen after a specific delay, and the timer is the instrument. The scheduled work is what's important, and the scheduling mechanism is secondary. My concern is with what we are trying to achieve and how the timer affects this.
|
| 57/2 |
2026/05/31 18:15 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/multicast.c Name:batadv_mcast_start_timer]
Results: map[SourceCode: 64: /**
65: * batadv_mcast_start_timer() - schedule the multicast periodic worker
66: * @bat_priv: the bat priv with all the mesh interface information
67: */
68: static void batadv_mcast_start_timer(struct batadv_priv *bat_priv)
69: {
70: queue_delayed_work(batadv_event_workqueue, &bat_priv->mcast.work,
71: msecs_to_jiffies(BATADV_MCAST_WORK_PERIOD));
72: }
SourceFile:net/batman-adv/multicast.c]
|
| 58/2 |
2026/05/31 18:15 |
llm |
expert |
0m
Model:
Tokens:
input: 44392
output: 42
thoughts: 13
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Investigating the `batadv_mcast_init` Call**
Okay, so I'm digging into the BATMAN advanced code again. The question that's bubbling up is pretty fundamental: exactly *when* does `batadv_mcast_init` get called? It's a key initialization function, so understanding its timing is critical to grasping the multicast operation within the mesh network. I need to nail down the call chain and the specific conditions that trigger it. I'm assuming it has to be triggered during device initialization of some kind. Does it relate to the instantiation of a particular mesh interface? Does it depend on specific kernel modules being loaded or configurations being set? I need to trace back from that function and see where this crucial function gets run. Let's start with a quick search to find all the references to the symbol and then begin to follow the call stack. This should be straightforward, but as we know, the devil is always in the details!
|
| 59/2 |
2026/05/31 18:15 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/batman-adv/multicast.c Name:batadv_mcast_init]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:batadv_mesh_init SourceFile:net/batman-adv/main.c SourceLine:219]] TruncatedOutput:false]
|
| 60/2 |
2026/05/31 18:15 |
llm |
expert |
0m
Model:
Tokens:
input: 44740
output: 40
thoughts: 12
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Diving into `batadv_mesh_init`**
Alright, let's get down to brass tacks. The first order of business is to thoroughly examine `batadv_mesh_init`. It's the starting point for everything related to mesh initialization, so understanding it completely is paramount. This function likely lays the groundwork for the entire B.A.T.M.A.N. Advanced mesh network. We're talking about setting up core data structures, registering crucial network protocols, configuring any necessary hardware interfaces, and potentially initializing some of the dynamic aspects related to mesh discovery and routing. I need to zero in on how the function manages:
* **Core data structure instantiation:** What structures are being set up to represent the mesh itself, nodes, links, and routing information? How is memory allocated and managed, and are there any potential race conditions I need to worry about?
* **Protocol registration:** It has to interact with the networking stack, so what hooks and registrations are happening here? Are they using the standard Netlink interface, or something more custom? What's the protocol family ID it's registering with?
* **Hardware interface setup:** How are network interfaces configured for use with B.A.T.M.A.N.? Does it handle different types of wireless hardware, or is it specific to a particular chipset? Is there anything around setting up virtual interfaces or bridging?
* **Initialization of dynamic mesh aspects:** How are discovery protocols and routing algorithms initialized? Are timers started, worker threads created, and callbacks registered? What are the initial conditions that are set and will they be configurable?
I need to understand what specific parameters are critical, how the function handles errors, and most importantly, how its execution flow integrates into the broader B.A.T.M.A.N. Advanced architecture. This initial examination will inform my next steps in assessing its overall design, its performance characteristics, and its security profile. I'll probably also cross-reference with related initialization routines to see how this function orchestrates the different modules and parts of the overall stack. Let's dig in.
|
| 61/2 |
2026/05/31 18:15 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/main.c Name:batadv_mesh_init]
Results: map[SourceCode: 139: /**
140: * batadv_mesh_init() - Initialize mesh interface
141: * @mesh_iface: netdev struct of the mesh interface
142: *
143: * Return: 0 on success or negative error number in case of failure
144: */
145: int batadv_mesh_init(struct net_device *mesh_iface)
146: {
147: struct batadv_priv *bat_priv = netdev_priv(mesh_iface);
148: int ret;
149:
150: spin_lock_init(&bat_priv->forw_bat_list_lock);
151: spin_lock_init(&bat_priv->forw_bcast_list_lock);
152: spin_lock_init(&bat_priv->tt.changes_list_lock);
153: spin_lock_init(&bat_priv->tt.req_list_lock);
154: spin_lock_init(&bat_priv->tt.roam_list_lock);
155: spin_lock_init(&bat_priv->tt.last_changeset_lock);
156: spin_lock_init(&bat_priv->tt.commit_lock);
157: spin_lock_init(&bat_priv->gw.list_lock);
158: #ifdef CONFIG_BATMAN_ADV_MCAST
159: spin_lock_init(&bat_priv->mcast.mla_lock);
160: spin_lock_init(&bat_priv->mcast.want_lists_lock);
161: #endif
162: spin_lock_init(&bat_priv->tvlv.container_list_lock);
163: spin_lock_init(&bat_priv->tvlv.handler_list_lock);
164: spin_lock_init(&bat_priv->meshif_vlan_list_lock);
165: spin_lock_init(&bat_priv->tp_list_lock);
166:
167: INIT_HLIST_HEAD(&bat_priv->forw_bat_list);
168: INIT_HLIST_HEAD(&bat_priv->forw_bcast_list);
169: INIT_HLIST_HEAD(&bat_priv->gw.gateway_list);
170: #ifdef CONFIG_BATMAN_ADV_MCAST
171: INIT_HLIST_HEAD(&bat_priv->mcast.want_all_unsnoopables_list);
172: INIT_HLIST_HEAD(&bat_priv->mcast.want_all_ipv4_list);
173: INIT_HLIST_HEAD(&bat_priv->mcast.want_all_ipv6_list);
174: #endif
175: INIT_LIST_HEAD(&bat_priv->tt.changes_list);
176: INIT_HLIST_HEAD(&bat_priv->tt.req_list);
177: INIT_LIST_HEAD(&bat_priv->tt.roam_list);
178: #ifdef CONFIG_BATMAN_ADV_MCAST
179: INIT_HLIST_HEAD(&bat_priv->mcast.mla_list);
180: #endif
181: INIT_HLIST_HEAD(&bat_priv->tvlv.container_list);
182: INIT_HLIST_HEAD(&bat_priv->tvlv.handler_list);
183: INIT_HLIST_HEAD(&bat_priv->meshif_vlan_list);
184: INIT_HLIST_HEAD(&bat_priv->tp_list);
185:
186: bat_priv->gw.generation = 0;
187:
188: ret = batadv_originator_init(bat_priv);
189: if (ret < 0) {
190: atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
191: goto err_orig;
192: }
193:
194: ret = batadv_tt_init(bat_priv);
195: if (ret < 0) {
196: atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
197: goto err_tt;
198: }
199:
200: ret = batadv_v_mesh_init(bat_priv);
201: if (ret < 0) {
202: atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
203: goto err_v;
204: }
205:
206: ret = batadv_bla_init(bat_priv);
207: if (ret < 0) {
208: atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
209: goto err_bla;
210: }
211:
212: ret = batadv_dat_init(bat_priv);
213: if (ret < 0) {
214: atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
215: goto err_dat;
216: }
217:
218: batadv_gw_init(bat_priv);
219: batadv_mcast_init(bat_priv);
220:
221: atomic_set(&bat_priv->gw.reselect, 0);
222: atomic_set(&bat_priv->mesh_state, BATADV_MESH_ACTIVE);
223:
224: return 0;
225:
226: err_dat:
227: batadv_bla_free(bat_priv);
228: err_bla:
229: batadv_v_mesh_free(bat_priv);
230: err_v:
231: batadv_tt_free(bat_priv);
232: err_tt:
233: batadv_originator_free(bat_priv);
234: err_orig:
235: batadv_purge_outstanding_packets(bat_priv, NULL);
236: atomic_set(&bat_priv->mesh_state, BATADV_MESH_INACTIVE);
237:
238: return ret;
239: }
SourceFile:net/batman-adv/main.c]
|
| 62/2 |
2026/05/31 18:15 |
llm |
expert |
0m
Model:
Tokens:
input: 47056
output: 40
thoughts: 12
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Tracing the Initialization of batadv_mesh_init**
Alright, so the question that's nagging me is exactly *when* does `batadv_mesh_init` actually get called? It's a fundamental part of the BATMAN-adv setup, and knowing the timing of its execution is crucial for understanding the overall system startup and potential race conditions. I need to pinpoint the exact call path. Is it happening during module loading, network device initialization, or perhaps triggered by some other event? My initial thought is it's likely connected to the network device or module loading process, but I want to verify that. It's time to dig into the kernel source and follow the calls, because I know that a firm grasp of the initialization sequence helps me with debugging and potential customization.
|
| 63/2 |
2026/05/31 18:15 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/batman-adv/main.c Name:batadv_mesh_init]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:batadv_meshif_init_late SourceFile:net/batman-adv/mesh-interface.c SourceLine:811]] TruncatedOutput:false]
|
| 64/2 |
2026/05/31 18:15 |
llm |
expert |
0m
Model:
Tokens:
input: 47375
output: 45
thoughts: 15
Thoughts: Okay, let's dive into this. Here's my thought process on this prompt:
**Analyzing `batadv_meshif_init_late`**
Alright, the directive is to look into `batadv_meshif_init_late`. Given my expertise, I'm already anticipating this is likely related to the Batman-Adv (Better Approach To Mobile Ad-hoc Networking) mesh networking implementation within the kernel. The name "late" suggests this is probably a function that's called during a later stage of the interface initialization process. That hints it could be handling things that depend on other subsystems being ready or configured first, such as interaction with the device, registering specific features. It might deal with setting up some final, more specific mesh interface configurations. I'll need to confirm these guesses by reviewing the source code and documentation.
|
| 65/2 |
2026/05/31 18:15 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/mesh-interface.c Name:batadv_meshif_init_late]
Results: map[SourceCode: 730: /**
731: * batadv_meshif_init_late() - late stage initialization of mesh interface
732: * @dev: registered network device to modify
733: *
734: * Return: error code on failures
735: */
736: static int batadv_meshif_init_late(struct net_device *dev)
737: {
738: struct batadv_priv *bat_priv;
739: u32 random_seqno;
740: int ret;
741: size_t cnt_len = sizeof(u64) * BATADV_CNT_NUM;
742:
743: batadv_set_lockdep_class(dev);
744:
745: bat_priv = netdev_priv(dev);
746: bat_priv->mesh_iface = dev;
747:
748: /* batadv_interface_stats() needs to be available as soon as
749: * register_netdevice() has been called
750: */
751: bat_priv->bat_counters = __alloc_percpu(cnt_len, __alignof__(u64));
752: if (!bat_priv->bat_counters)
753: return -ENOMEM;
754:
755: atomic_set(&bat_priv->aggregated_ogms, 1);
756: atomic_set(&bat_priv->bonding, 0);
757: #ifdef CONFIG_BATMAN_ADV_BLA
758: atomic_set(&bat_priv->bridge_loop_avoidance, 1);
759: #endif
760: #ifdef CONFIG_BATMAN_ADV_DAT
761: atomic_set(&bat_priv->distributed_arp_table, 1);
762: #endif
763: #ifdef CONFIG_BATMAN_ADV_MCAST
764: atomic_set(&bat_priv->multicast_mode, 1);
765: atomic_set(&bat_priv->multicast_fanout, 16);
766: atomic_set(&bat_priv->mcast.num_want_all_unsnoopables, 0);
767: atomic_set(&bat_priv->mcast.num_want_all_ipv4, 0);
768: atomic_set(&bat_priv->mcast.num_want_all_ipv6, 0);
769: atomic_set(&bat_priv->mcast.num_no_mc_ptype_capa, 0);
770: #endif
771: atomic_set(&bat_priv->gw.mode, BATADV_GW_MODE_OFF);
772: atomic_set(&bat_priv->gw.bandwidth_down, 100);
773: atomic_set(&bat_priv->gw.bandwidth_up, 20);
774: atomic_set(&bat_priv->orig_interval, 1000);
775: atomic_set(&bat_priv->hop_penalty, 30);
776: #ifdef CONFIG_BATMAN_ADV_DEBUG
777: atomic_set(&bat_priv->log_level, 0);
778: #endif
779: atomic_set(&bat_priv->fragmentation, 1);
780: atomic_set(&bat_priv->packet_size_max, BATADV_MAX_MTU);
781: atomic_set(&bat_priv->bcast_queue_left, BATADV_BCAST_QUEUE_LEN);
782: atomic_set(&bat_priv->batman_queue_left, BATADV_BATMAN_QUEUE_LEN);
783:
784: atomic_set(&bat_priv->mesh_state, BATADV_MESH_INACTIVE);
785: atomic_set(&bat_priv->bcast_seqno, 1);
786: atomic_set(&bat_priv->tt.vn, 0);
787: atomic_set(&bat_priv->tt.ogm_append_cnt, 0);
788: #ifdef CONFIG_BATMAN_ADV_BLA
789: atomic_set(&bat_priv->bla.num_requests, 0);
790: #endif
791: atomic_set(&bat_priv->tp_num, 0);
792:
793: WRITE_ONCE(bat_priv->tt.local_changes, 0);
794: bat_priv->tt.last_changeset = NULL;
795: bat_priv->tt.last_changeset_len = 0;
796: bat_priv->isolation_mark = 0;
797: bat_priv->isolation_mark_mask = 0;
798:
799: /* randomize initial seqno to avoid collision */
800: get_random_bytes(&random_seqno, sizeof(random_seqno));
801: atomic_set(&bat_priv->frag_seqno, random_seqno);
802:
803: bat_priv->primary_if = NULL;
804:
805: if (!bat_priv->algo_ops) {
806: ret = batadv_algo_select(bat_priv, batadv_routing_algo);
807: if (ret < 0)
808: goto free_bat_counters;
809: }
810:
811: ret = batadv_mesh_init(dev);
812: if (ret < 0)
813: goto free_bat_counters;
814:
815: return 0;
816:
817: free_bat_counters:
818: free_percpu(bat_priv->bat_counters);
819: bat_priv->bat_counters = NULL;
820:
821: return ret;
822: }
SourceFile:net/batman-adv/mesh-interface.c]
|
| 66/2 |
2026/05/31 18:15 |
llm |
expert |
0m
Model:
Tokens:
input: 49284
output: 45
thoughts: 15
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Investigating the `batadv_meshif_init_late` Call**
Okay, so I'm trying to figure out the precise timing of `batadv_meshif_init_late`. It's a critical function, obviously, because it's named with "late" in its name. It's safe to assume that `batadv_meshif_init_late` is a final initialization function that is called after other initialization routines have been completed. I know that the BATMAN advanced (B.A.T.M.A.N.) code is pretty complex. I need to understand when this late stage of initialization is triggered to better understand the overall mesh interface setup. I need to trace this function back through the call stack to understand what prerequisites are in place. Is it triggered during module loading, interface creation, or perhaps some later event? I'll probably start by grepping the kernel sources to find where `batadv_meshif_init_late` is actually called. Then, I need to look into the surrounding code to determine the context of that call, see what events or conditions trigger it. The goal is to fully understand how, why, and when this crucial function is executed in the BATMAN setup process. I need to consider potential race conditions or dependencies on other initialization tasks.
|
| 67/2 |
2026/05/31 18:15 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/batman-adv/mesh-interface.c Name:batadv_meshif_init_late]
Results: map[References:<nil> TruncatedOutput:false]
|
| 68/2 |
2026/05/31 18:15 |
llm |
expert |
0m
Model:
Tokens:
input: 49653
output: 36
thoughts: 25
Thoughts: Here's my thought process, summarized as if I were considering the situation:
**Initial Assessment: Missing Context**
Okay, so I'm looking at something, and the first thing that jumps out is... there's no reference! That's immediately a red flag. In my experience, if something is important enough to be considered, it should be traceable to its origin. This could indicate a typo, an incomplete section, or even a very poorly documented feature. I need to figure out what's going on here. I can't start assessing anything with this absence of context. I could spend a day tracing it, only to find it's a non-issue. I need to find the definition or reference immediately.
The first place to start would be to look for the function declaration. I'll search for `"batadv_meshif_init_late"`. Hopefully, that will lead me to the header file or source file where this is defined and give me some idea of its purpose within the codebase. Hopefully, this isn't a custom function. It could make the rest of the process much more difficult.
|
| 69/2 |
2026/05/31 18:15 |
tool |
grepper |
0m
Args: map[Expression:batadv_meshif_init_late PathPrefix:net/batman-adv/]
Results: map[Output:net/batman-adv/mesh-interface.c=724=static void batadv_set_lockdep_class(struct net_device *dev)
--
net/batman-adv/mesh-interface.c-730-/**
net/batman-adv/mesh-interface.c:731: * batadv_meshif_init_late() - late stage initialization of mesh interface
net/batman-adv/mesh-interface.c-732- * @dev: registered network device to modify
--
net/batman-adv/mesh-interface.c-735- */
net/batman-adv/mesh-interface.c:736:static int batadv_meshif_init_late(struct net_device *dev)
net/batman-adv/mesh-interface.c-737-{
--
net/batman-adv/mesh-interface.c=876=static const struct net_device_ops batadv_netdev_ops = {
net/batman-adv/mesh-interface.c:877: .ndo_init = batadv_meshif_init_late,
net/batman-adv/mesh-interface.c-878- .ndo_get_stats = batadv_interface_stats,
]
|
| 70/2 |
2026/05/31 18:15 |
llm |
expert |
0m
Model:
Tokens:
input: 50262
output: 38
thoughts: 470
Thoughts: **Analyzing a Potential Denial of Service in the Batman-adv Module**
Okay, so I'm looking at this `ndo_init` function. It looks like when `register_netdevice` is called, `ndo_init` gets triggered, which then calls `batadv_meshif_init_late`, which calls `batadv_mesh_init`, and so on, eventually scheduling the work. Interesting! That means just creating the interface through netlink is enough to get that worker thread started. And this worker thread runs periodically.
Here's where it gets interesting... it looks like if an unprivileged user creates a user namespace, gets `CAP_NET_ADMIN` within that namespace, and then creates a `batadv` interface and joins a ton of multicast groups on it, this worker thread will execute and cause a softlockup.
Hold on a second, does this worker thread run within the user namespace or the initial namespace? Ah, it uses the global `batadv_event_workqueue`. So, that means it's running in the context of a kernel thread. And the softlockup happens within that kernel thread. So this is looking like a clear Denial of Service vulnerability, a softlockup specifically.
The question is, can an unprivileged user trigger this? Let's break it down:
1. `unshare(CLONE_NEWUSER | CLONE_NEWNET)` - create a new user and network namespace.
2. Create a `batadv` interface using netlink. This requires `CAP_NET_ADMIN` in the new netns, which the unprivileged user has by default in the new netns, as I understand the situation.
3. Create many sockets and join many multicast groups on the `batadv` interface.
4. Wait for the `batadv_mcast_mla_update` worker thread to execute and cause that softlockup.
Okay, let's step back - will the `batman-adv` module even be loaded automatically when you create a `batadv` interface? It looks like `rtnl_newlink` calls `request_module("rtnl-link-%s", kind)`. So, if the module isn't loaded, it will be loaded. Wait, won't `request_module` be restricted by `CAP_NET_ADMIN` in the init namespace? No, it looks like `rtnl_newlink` calls `request_module` *before* it checks `CAP_NET_ADMIN` in the target namespace. I better double-check `rtnl_newlink`...
|
| 71/2 |
2026/05/31 18:15 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/rtnetlink.c Name:rtnl_newlink]
Results: map[SourceCode:3961: static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
3962: struct netlink_ext_ack *extack)
3963: {
3964: struct net *tgt_net, *link_net = NULL, *peer_net = NULL;
3965: struct nlattr **tb, **linkinfo, **data = NULL;
3966: struct rtnl_link_ops *ops = NULL;
3967: struct rtnl_newlink_tbs *tbs;
3968: struct rtnl_nets rtnl_nets;
3969: int ops_srcu_index;
3970: int ret;
3971:
3972: tbs = kmalloc_obj(*tbs);
3973: if (!tbs)
3974: return -ENOMEM;
3975:
3976: tb = tbs->tb;
3977: ret = nlmsg_parse_deprecated(nlh, sizeof(struct ifinfomsg), tb,
3978: IFLA_MAX, ifla_policy, extack);
3979: if (ret < 0)
3980: goto free;
3981:
3982: ret = rtnl_ensure_unique_netns(tb, extack, false);
3983: if (ret < 0)
3984: goto free;
3985:
3986: linkinfo = tbs->linkinfo;
3987: if (tb[IFLA_LINKINFO]) {
3988: ret = nla_parse_nested_deprecated(linkinfo, IFLA_INFO_MAX,
3989: tb[IFLA_LINKINFO],
3990: ifla_info_policy, NULL);
3991: if (ret < 0)
3992: goto free;
3993: } else {
3994: memset(linkinfo, 0, sizeof(tbs->linkinfo));
3995: }
3996:
3997: if (linkinfo[IFLA_INFO_KIND]) {
3998: char kind[MODULE_NAME_LEN];
3999:
4000: nla_strscpy(kind, linkinfo[IFLA_INFO_KIND], sizeof(kind));
4001: ops = rtnl_link_ops_get(kind, &ops_srcu_index);
4002: #ifdef CONFIG_MODULES
4003: if (!ops) {
4004: request_module("rtnl-link-%s", kind);
4005: ops = rtnl_link_ops_get(kind, &ops_srcu_index);
4006: }
4007: #endif
4008: }
4009:
4010: rtnl_nets_init(&rtnl_nets);
4011:
4012: if (ops) {
4013: if (ops->maxtype > RTNL_MAX_TYPE) {
4014: ret = -EINVAL;
4015: goto put_ops;
4016: }
4017:
4018: if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) {
4019: ret = nla_parse_nested_deprecated(tbs->attr, ops->maxtype,
4020: linkinfo[IFLA_INFO_DATA],
4021: ops->policy, extack);
4022: if (ret < 0)
4023: goto put_ops;
4024:
4025: data = tbs->attr;
4026: }
4027:
4028: if (ops->validate) {
4029: ret = ops->validate(tb, data, extack);
4030: if (ret < 0)
4031: goto put_ops;
4032: }
4033:
4034: if (ops->peer_type) {
4035: peer_net = rtnl_get_peer_net(ops, tb, data, extack);
4036: if (IS_ERR(peer_net)) {
4037: ret = PTR_ERR(peer_net);
4038: goto put_ops;
4039: }
4040: if (peer_net)
4041: rtnl_nets_add(&rtnl_nets, peer_net);
4042: }
4043: }
4044:
4045: tgt_net = rtnl_link_get_net_capable(skb, sock_net(skb->sk), tb, CAP_NET_ADMIN);
4046: if (IS_ERR(tgt_net)) {
4047: ret = PTR_ERR(tgt_net);
4048: goto put_net;
4049: }
4050:
4051: rtnl_nets_add(&rtnl_nets, tgt_net);
4052:
4053: if (tb[IFLA_LINK_NETNSID]) {
4054: int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);
4055:
4056: link_net = get_net_ns_by_id(tgt_net, id);
4057: if (!link_net) {
4058: NL_SET_ERR_MSG(extack, "Unknown network namespace id");
4059: ret = -EINVAL;
4060: goto put_net;
4061: }
4062:
4063: rtnl_nets_add(&rtnl_nets, link_net);
4064:
4065: if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN)) {
4066: ret = -EPERM;
4067: goto put_net;
4068: }
4069: }
4070:
4071: rtnl_nets_lock(&rtnl_nets);
4072: ret = __rtnl_newlink(skb, nlh, ops, tgt_net, link_net, peer_net, tbs, data, extack);
4073: rtnl_nets_unlock(&rtnl_nets);
4074:
4075: put_net:
4076: rtnl_nets_destroy(&rtnl_nets);
4077: put_ops:
4078: if (ops)
4079: rtnl_link_ops_put(ops, ops_srcu_index);
4080: free:
4081: kfree(tbs);
4082: return ret;
4083: }
SourceFile:net/core/rtnetlink.c]
|
| 72/2 |
2026/05/31 18:15 |
llm |
expert |
0m
Model:
Tokens:
input: 53231
output: 39
thoughts: 415
Thoughts: **My Analysis of a Potential Privilege Escalation**
Okay, so I'm tracing this `rtnl_newlink` call and where it originates. It looks like it's called from `rtnetlink_rcv_msg`. The crucial point here is whether there's a check for `CAP_NET_ADMIN` involved. And yes, for `RTM_NEWLINK`, it uses `netlink_net_capable(skb, CAP_NET_ADMIN)`. The issue is, `netlink_net_capable` checks this capability within the *namespace of the socket*. That's a huge clue... which means a regular user, without any root privileges, can create a user namespace and effectively gain `CAP_NET_ADMIN` *within that namespace*. This user namespace is a key vector here, it's how they can escalate privileges.
So, they can call `rtnl_newlink`. This is where it gets interesting... `request_module` gets called next. That's a good place to start the exploit. It gets called with `rtnl-link-batadv` as an argument. Does `request_module` even work from a user namespace? It does, but it's important to realize that `modprobe` is actually run in the init namespace, so the init namespace becomes a critical point of attack.
If the module is already installed, it loads. Great. However, many distributions are now catching this vulnerability by restricting module loading from user namespaces by default, using `sysctl kernel.modules_disabled=1` or other methods. However, by default, if the module alias exists, `request_module` will work even without being root.
Let's assume the module can be loaded, and the attacker is able to get a `batadv` interface created. But does `batman-adv` even support being created in a non-init netns? I've got to check the `batadv_link_ops`. Let's see... looking at the code...no, it doesn't look like `netns_refund` or `netns_aware` or anything like that is set. So, if it doesn't have these, can it *really* be created in a non-init netns? Let me double-check `rtnl_create_link` and make sure it doesn't do any additional checks.
|
| 73/2 |
2026/05/31 18:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/core/rtnetlink.c Name:rtnl_create_link]
Results: map[SourceCode:3611: struct net_device *rtnl_create_link(struct net *net, const char *ifname,
3612: unsigned char name_assign_type,
3613: const struct rtnl_link_ops *ops,
3614: struct nlattr *tb[],
3615: struct netlink_ext_ack *extack)
3616: {
3617: struct net_device *dev;
3618: unsigned int num_tx_queues = 1;
3619: unsigned int num_rx_queues = 1;
3620: int err;
3621:
3622: if (tb[IFLA_NUM_TX_QUEUES])
3623: num_tx_queues = nla_get_u32(tb[IFLA_NUM_TX_QUEUES]);
3624: else if (ops->get_num_tx_queues)
3625: num_tx_queues = ops->get_num_tx_queues();
3626:
3627: if (tb[IFLA_NUM_RX_QUEUES])
3628: num_rx_queues = nla_get_u32(tb[IFLA_NUM_RX_QUEUES]);
3629: else if (ops->get_num_rx_queues)
3630: num_rx_queues = ops->get_num_rx_queues();
3631:
3632: if (num_tx_queues < 1 || num_tx_queues > 4096) {
3633: NL_SET_ERR_MSG(extack, "Invalid number of transmit queues");
3634: return ERR_PTR(-EINVAL);
3635: }
3636:
3637: if (num_rx_queues < 1 || num_rx_queues > 4096) {
3638: NL_SET_ERR_MSG(extack, "Invalid number of receive queues");
3639: return ERR_PTR(-EINVAL);
3640: }
3641:
3642: if (ops->alloc) {
3643: dev = ops->alloc(tb, ifname, name_assign_type,
3644: num_tx_queues, num_rx_queues);
3645: if (IS_ERR(dev))
3646: return dev;
3647: } else {
3648: dev = alloc_netdev_mqs(ops->priv_size, ifname,
3649: name_assign_type, ops->setup,
3650: num_tx_queues, num_rx_queues);
3651: }
3652:
3653: if (!dev)
3654: return ERR_PTR(-ENOMEM);
3655:
3656: err = validate_linkmsg(dev, tb, extack);
3657: if (err < 0) {
3658: free_netdev(dev);
3659: return ERR_PTR(err);
3660: }
3661:
3662: dev_net_set(dev, net);
3663: dev->rtnl_link_ops = ops;
3664: dev->rtnl_link_initializing = true;
3665:
3666: if (tb[IFLA_MTU]) {
3667: u32 mtu = nla_get_u32(tb[IFLA_MTU]);
3668:
3669: err = dev_validate_mtu(dev, mtu, extack);
3670: if (err) {
3671: free_netdev(dev);
3672: return ERR_PTR(err);
3673: }
3674: dev->mtu = mtu;
3675: }
3676: if (tb[IFLA_ADDRESS]) {
3677: __dev_addr_set(dev, nla_data(tb[IFLA_ADDRESS]),
3678: nla_len(tb[IFLA_ADDRESS]));
3679: dev->addr_assign_type = NET_ADDR_SET;
3680: }
3681: if (tb[IFLA_BROADCAST])
3682: memcpy(dev->broadcast, nla_data(tb[IFLA_BROADCAST]),
3683: nla_len(tb[IFLA_BROADCAST]));
3684: if (tb[IFLA_TXQLEN])
3685: dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);
3686: if (tb[IFLA_OPERSTATE])
3687: set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE]));
3688: if (tb[IFLA_LINKMODE])
3689: dev->link_mode = nla_get_u8(tb[IFLA_LINKMODE]);
3690: if (tb[IFLA_GROUP])
3691: netif_set_group(dev, nla_get_u32(tb[IFLA_GROUP]));
3692: if (tb[IFLA_GSO_MAX_SIZE])
3693: netif_set_gso_max_size(dev, nla_get_u32(tb[IFLA_GSO_MAX_SIZE]));
3694: if (tb[IFLA_GSO_MAX_SEGS])
3695: netif_set_gso_max_segs(dev, nla_get_u32(tb[IFLA_GSO_MAX_SEGS]));
3696: if (tb[IFLA_GRO_MAX_SIZE])
3697: netif_set_gro_max_size(dev, nla_get_u32(tb[IFLA_GRO_MAX_SIZE]));
3698: if (tb[IFLA_GSO_IPV4_MAX_SIZE])
3699: netif_set_gso_ipv4_max_size(dev, nla_get_u32(tb[IFLA_GSO_IPV4_MAX_SIZE]));
3700: if (tb[IFLA_GRO_IPV4_MAX_SIZE])
3701: netif_set_gro_ipv4_max_size(dev, nla_get_u32(tb[IFLA_GRO_IPV4_MAX_SIZE]));
3702:
3703: return dev;
3704: }
SourceFile:net/core/rtnetlink.c]
|
| 74/2 |
2026/05/31 18:16 |
llm |
expert |
0m
Model:
Tokens:
input: 55994
output: 43
thoughts: 133
Thoughts: **Examining the Netns and Link Operations**
Okay, so `dev_net_set(dev, net)` is the function that actually assigns a network namespace to a device, right? It's pretty straightforward. The key question is whether there's any safeguard in place, preventing certain `rtnl_link_ops` operations from being performed outside the initial network namespace. It's a critical point for security and isolation.
Hmm, it looks like there isn't a *general* restriction baked into the core. Any `rtnl_link_ops` seem to be fair game for creating a device in *any* network namespace. The onus is entirely on the module implementing the link operation – it needs to proactively verify that `net_eq(net, &init_net)` if it *requires* operation in the initial namespace, within the `validate`, `newlink`, or `setup` functions. This means we must check if that logic is present.
Alright, let's dive into some specific implementations. My focus shifts to the `batadv_meshif_validate` and `batadv_meshif_newlink` functions. These seem like good starting points to determine whether the Batadv mesh interface code is doing this or not.
|
| 75/2 |
2026/05/31 18:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/mesh-interface.c Name:batadv_meshif_validate]
Results: map[SourceCode:1026: /**
1027: * batadv_meshif_validate() - validate configuration of new batadv link
1028: * @tb: IFLA_INFO_DATA netlink attributes
1029: * @data: enum batadv_ifla_attrs attributes
1030: * @extack: extended ACK report struct
1031: *
1032: * Return: 0 if successful or error otherwise.
1033: */
1034: static int batadv_meshif_validate(struct nlattr *tb[], struct nlattr *data[],
1035: struct netlink_ext_ack *extack)
1036: {
1037: struct batadv_algo_ops *algo_ops;
1038:
1039: if (!data)
1040: return 0;
1041:
1042: if (data[IFLA_BATADV_ALGO_NAME]) {
1043: algo_ops = batadv_algo_get(nla_data(data[IFLA_BATADV_ALGO_NAME]));
1044: if (!algo_ops)
1045: return -EINVAL;
1046: }
1047:
1048: return 0;
1049: }
SourceFile:net/batman-adv/mesh-interface.c]
|
| 76/2 |
2026/05/31 18:16 |
llm |
expert |
0m
Model:
Tokens:
input: 56820
output: 41
thoughts: 2529
Thoughts: **Vulnerability Analysis: batadv Multicast DoS**
Okay, let's break this down. It seems a user, even an unprivileged one within a user namespace, can exploit a flaw in the `batadv` mesh networking code. The `batadv_meshif_newlink` function lacks a `init_net` check, which opens the door for user-space interface creation. The core of the problem lies in the `batadv_mcast_mla_update` function, specifically its O(N^2) complexity.
Initially, I thought about the number of multicast groups a user can join. It looks like the `RLIMIT_NOFILE` limit for the number of sockets a process can have is the key. While the number of sockets per process is limited, the number of processes a user can create is not explicitly limited. Each socket can join a number of multicast groups, controlled by `sysctl_igmp_max_memberships`. It appears that many processes each with many sockets can lead to an enormous number of joined multicast groups.
The potential for a softlockup seemed based on the number of groups created. I crunched some numbers: with a large enough `RLIMIT_NOFILE`, a user can join enough multicast groups to cause a quadratic explosion in `batadv_mcast_mla_update`. This is due to the comparisons done in the O(N^2) loop. The number of iterations in `batadv_mcast_mla_is_duplicate` can become huge, enough to cause a substantial delay, but the time to trigger the softlockup seemed questionable, at first.
A further deep dive into `batadv_mcast_mla_meshif_get_ipv4` revealed the problem. The loop within that function, which checks for duplicate MAC addresses, also has O(N^2) complexity and has to iterate through all MAC addresses in the `mcast_list`. The number of unique MAC addresses will grow linearly with the number of groups the attacker can create, but the number of iterations in the loop is a quadratic function of this number. The key is in the attacker's control over which IPv4 multicast addresses are used. Given that the mapping of IPv4 to MAC is controllable, it is feasible to choose addresses that are unique.
The bug report about `__debug_check_no_obj_freed` is interesting but seems to be a red herring caused by `CONFIG_DEBUG_OBJECTS_FREE` and its impact on the `kfree` and object debugging. It appears that the slow debug checks may cause the softlockup with fewer groups than is required in a production kernel. The core issue is the O(N^2) loop.
However, the real clincher is the `rcu_read_lock()` within the O(N^2) loop. The sheer duration of the loop, for a large number of multicast groups, will inevitably lead to RCU stalls, a more direct and severe Denial of Service. With a sufficiently large number of groups, the RCU stall detector is guaranteed to fire. Even if it doesn't trigger a panic, it is still a DoS.
Since `batadv_mcast_mla_update` is a delayed work item, it's not a one-time event; it will repeatedly trigger these stalls and softlockups, rendering the system unusable. So, this is a clear and exploitable DoS vulnerability, reachable by an unprivileged user.
|
| 77/2 |
2026/05/31 18:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/ipv4/igmp.c Name:____ip_mc_inc_group]
Results: map[SourceCode:1501: /*
1502: * A socket has joined a multicast group on device dev.
1503: */
1504: static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
1505: unsigned int mode, gfp_t gfp)
1506: {
1507: struct ip_mc_list __rcu **mc_hash;
1508: struct ip_mc_list *im;
1509:
1510: ASSERT_RTNL();
1511:
1512: mc_hash = rtnl_dereference(in_dev->mc_hash);
1513: if (mc_hash) {
1514: u32 hash = hash_32((__force u32)addr, MC_HASH_SZ_LOG);
1515:
1516: for (im = rtnl_dereference(mc_hash[hash]);
1517: im;
1518: im = rtnl_dereference(im->next_hash)) {
1519: if (im->multiaddr == addr)
1520: break;
1521: }
1522: } else {
1523: for_each_pmc_rtnl(in_dev, im) {
1524: if (im->multiaddr == addr)
1525: break;
1526: }
1527: }
1528:
1529: if (im) {
1530: im->users++;
1531: ip_mc_add_src(in_dev, &addr, mode, 0, NULL, 0);
1532: goto out;
1533: }
1534:
1535: im = kzalloc_obj(*im, gfp);
1536: if (!im)
1537: goto out;
1538:
1539: im->users = 1;
1540: im->interface = in_dev;
1541: in_dev_hold(in_dev);
1542: im->multiaddr = addr;
1543: im->mca_cstamp = jiffies;
1544: im->mca_tstamp = im->mca_cstamp;
1545: /* initial mode is (EX, empty) */
1546: im->sfmode = mode;
1547: im->sfcount[mode] = 1;
1548: refcount_set(&im->refcnt, 1);
1549: spin_lock_init(&im->lock);
1550: #ifdef CONFIG_IP_MULTICAST
1551: timer_setup(&im->timer, igmp_timer_expire, 0);
1552: #endif
1553:
1554: im->next_rcu = in_dev->mc_list;
1555: in_dev->mc_count++;
1556: rcu_assign_pointer(in_dev->mc_list, im);
1557:
1558: ip_mc_hash_add(in_dev, im);
1559:
1560: #ifdef CONFIG_IP_MULTICAST
1561: igmpv3_del_delrec(in_dev, im);
1562: #endif
1563: igmp_group_added(im);
1564: inet_ifmcaddr_notify(in_dev->dev, im, RTM_NEWMULTICAST);
1565: if (!in_dev->dead)
1566: ip_rt_multicast_event(in_dev);
1567: out:
1568: return;
1569: }
SourceFile:net/ipv4/igmp.c]
|
| 78/2 |
2026/05/31 18:16 |
llm |
expert |
0m
Model:
Tokens:
input: 61188
output: 50
thoughts: 842
Thoughts: **Vulnerability Analysis: Multicast Group Denial of Service**
Okay, let's break this down systematically. My initial thought is that the lack of a limit on `in_dev->mc_count` within `____ip_mc_inc_group` is the core issue here. The only constraint seems to be memory limitations via `kzalloc_obj` failures. This means a malicious actor could potentially exhaust system resources by adding an excessive number of multicast groups.
The critical question now is, what's the attack vector? Can this be triggered without resorting to user namespaces? It hits me: if a `batadv` interface already exists on the system, *any* unprivileged user can join multicast groups on it! This is because the `IP_ADD_MEMBERSHIP` socket option doesn't require any special capabilities. This is huge.
The usual scenario is that `batadv` interfaces are not present by default. They're typically configured by an administrator for mesh networking. So, if there isn't one, the attacker would need to create it. To do *that* they'd need `CAP_NET_ADMIN` privileges. This capability would be accessible via user namespaces by an unprivileged user.
But here's the tricky part. The prompt states that user namespaces aren't accessible. If we can't create a `batadv` interface directly, the attack seems blocked at the interface creation step.
However, the vulnerable code is specific to the `batman-adv` module. If that module is *loaded* and a `batadv` interface *exists*, even a non-root user can trigger it.
So, let's clarify my response. If the vulnerable subsystem (batman-adv) is *active* on the system through interface creation, then unprivileged users *can* trigger this vulnerability.
Now, a quick check to make sure `IP_ADD_MEMBERSHIP` can be called on an interface within another network namespace. No, the socket belongs to a specific namespace, and can only interact with interfaces within that namespace. But we are assuming the attacker can create a interface and thus the unprivileged user can use `IP_ADD_MEMBERSHIP` on an interface in the init netns.
Finally, does the `IP_ADD_MEMBERSHIP` socket option have any privilege checks, or any kind of limits for non-root users? Diving back to the code again... No. The `do_ip_setsockopt` path confirms it. There are no capability checks whatsoever for `IP_ADD_MEMBERSHIP`.
The conclusion is, if a `batadv` interface exists, a non-root user can trigger a DoS attack by exhausting memory via excessive multicast group memberships.
|
| 79/2 |
2026/05/31 18:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/multicast.c Name:batadv_mcast_mla_meshif_get_ipv6]
Results: map[SourceCode: 432: static int
433: batadv_mcast_mla_meshif_get_ipv6(struct net_device *dev,
434: struct hlist_head *mcast_list,
435: struct batadv_mcast_mla_flags *flags)
436: {
437: struct batadv_hw_addr *new;
438: struct inet6_dev *in6_dev;
439: u8 mcast_addr[ETH_ALEN];
440: struct ifmcaddr6 *pmc6;
441: int ret = 0;
442:
443: if (flags->tvlv_flags & BATADV_MCAST_WANT_ALL_IPV6)
444: return 0;
445:
446: rcu_read_lock();
447:
448: in6_dev = __in6_dev_get(dev);
449: if (!in6_dev) {
450: rcu_read_unlock();
451: return 0;
452: }
453:
454: for (pmc6 = rcu_dereference(in6_dev->mc_list);
455: pmc6;
456: pmc6 = rcu_dereference(pmc6->next)) {
457: if (IPV6_ADDR_MC_SCOPE(&pmc6->mca_addr) <
458: IPV6_ADDR_SCOPE_LINKLOCAL)
459: continue;
460:
461: if (flags->tvlv_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES &&
462: ipv6_addr_is_ll_all_nodes(&pmc6->mca_addr))
463: continue;
464:
465: if (!(flags->tvlv_flags & BATADV_MCAST_WANT_NO_RTR6) &&
466: IPV6_ADDR_MC_SCOPE(&pmc6->mca_addr) >
467: IPV6_ADDR_SCOPE_LINKLOCAL)
468: continue;
469:
470: ipv6_eth_mc_map(&pmc6->mca_addr, mcast_addr);
471:
472: if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list))
473: continue;
474:
475: new = kmalloc_obj(*new, GFP_ATOMIC);
476: if (!new) {
477: ret = -ENOMEM;
478: break;
479: }
480:
481: ether_addr_copy(new->addr, mcast_addr);
482: hlist_add_head(&new->list, mcast_list);
483: ret++;
484: }
485: rcu_read_unlock();
486:
487: return ret;
488: }
SourceFile:net/batman-adv/multicast.c]
|
| 80/2 |
2026/05/31 18:16 |
llm |
expert |
0m
Model:
Tokens:
input: 63569
output: 46
thoughts: 33
Thoughts: Okay, here's my attempt at summarizing those thoughts as a first-person expert perspective:
**IPv6 Looping Concerns and Potential Solutions**
Right, so I'm thinking about the IPv6 implementation and, just as with IPv4, that pesky O(N^2) loop is still there, isn't it? It hasn't magically disappeared. I need to keep that in mind when I’m assessing performance and scalability. This is a crucial point that can't be overlooked.
That makes me think - what about `batadv_mcast_mla_bridge_get`? Is that part of the solution? Does it handle it properly? I need to look into whether or not its optimized, to what extent, and whether there are any further optimizations available. This warrants further investigation to understand its implications for avoiding performance degradation.
|
| 81/2 |
2026/05/31 18:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/multicast.c Name:batadv_mcast_mla_bridge_get]
Results: map[SourceCode: 569: /**
570: * batadv_mcast_mla_bridge_get() - get bridged-in multicast listeners
571: * @dev: a bridge slave whose bridge to collect multicast addresses from
572: * @mcast_list: a list to put found addresses into
573: * @flags: flags indicating the new multicast state
574: *
575: * Collects multicast addresses of multicast listeners residing
576: * on foreign, non-mesh devices which we gave access to our mesh via
577: * a bridge on top of the given mesh interface, dev, in the given
578: * mcast_list.
579: *
580: * Return: -ENOMEM on memory allocation error or the number of
581: * items added to the mcast_list otherwise.
582: */
583: static int batadv_mcast_mla_bridge_get(struct net_device *dev,
584: struct hlist_head *mcast_list,
585: struct batadv_mcast_mla_flags *flags)
586: {
587: struct list_head bridge_mcast_list = LIST_HEAD_INIT(bridge_mcast_list);
588: struct br_ip_list *br_ip_entry, *tmp;
589: u8 tvlv_flags = flags->tvlv_flags;
590: struct batadv_hw_addr *new;
591: u8 mcast_addr[ETH_ALEN];
592: int ret;
593:
594: /* we don't need to detect these devices/listeners, the IGMP/MLD
595: * snooping code of the Linux bridge already does that for us
596: */
597: ret = br_multicast_list_adjacent(dev, &bridge_mcast_list);
598: if (ret < 0)
599: goto out;
600:
601: list_for_each_entry(br_ip_entry, &bridge_mcast_list, list) {
602: if (br_ip_entry->addr.proto == htons(ETH_P_IP)) {
603: if (tvlv_flags & BATADV_MCAST_WANT_ALL_IPV4)
604: continue;
605:
606: if (tvlv_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES &&
607: ipv4_is_local_multicast(br_ip_entry->addr.dst.ip4))
608: continue;
609:
610: if (!(tvlv_flags & BATADV_MCAST_WANT_NO_RTR4) &&
611: !ipv4_is_local_multicast(br_ip_entry->addr.dst.ip4))
612: continue;
613: }
614:
615: #if IS_ENABLED(CONFIG_IPV6)
616: if (br_ip_entry->addr.proto == htons(ETH_P_IPV6)) {
617: if (tvlv_flags & BATADV_MCAST_WANT_ALL_IPV6)
618: continue;
619:
620: if (tvlv_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES &&
621: ipv6_addr_is_ll_all_nodes(&br_ip_entry->addr.dst.ip6))
622: continue;
623:
624: if (!(tvlv_flags & BATADV_MCAST_WANT_NO_RTR6) &&
625: IPV6_ADDR_MC_SCOPE(&br_ip_entry->addr.dst.ip6) >
626: IPV6_ADDR_SCOPE_LINKLOCAL)
627: continue;
628: }
629: #endif
630:
631: batadv_mcast_mla_br_addr_cpy(mcast_addr, &br_ip_entry->addr);
632: if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list))
633: continue;
634:
635: new = kmalloc_obj(*new, GFP_ATOMIC);
636: if (!new) {
637: ret = -ENOMEM;
638: break;
639: }
640:
641: ether_addr_copy(new->addr, mcast_addr);
642: hlist_add_head(&new->list, mcast_list);
643: }
644:
645: out:
646: list_for_each_entry_safe(br_ip_entry, tmp, &bridge_mcast_list, list) {
647: list_del(&br_ip_entry->list);
648: kfree(br_ip_entry);
649: }
650:
651: return ret;
652: }
SourceFile:net/batman-adv/multicast.c]
|
| 82/2 |
2026/05/31 18:17 |
llm |
expert |
0m
Model:
Tokens:
input: 65287
output: 42
thoughts: 236
Thoughts: **Analyzing the Potential Impact of this O(N^2) Loop**
Okay, so I've pinpointed an O(N^2) complexity issue here, and it's not just a single loop; `batadv_mcast_mla_tt_retract` and `batadv_mcast_mla_tt_add` also share this quadratic complexity. The performance implications are clear: this could lead to significant CPU exhaustion, making it a denial-of-service vulnerability.
The crucial question is whether this could be leveraged for privilege escalation. After a careful review, the answer is "no". This isn't a memory corruption vulnerability; there are no out-of-bounds accesses or use-after-free situations here. It's simply an inefficient algorithm that can be made to consume excessive CPU cycles.
Therefore, while a denial-of-service attack is definitely possible (setting "DenialOfService = True"), the "Exploitable" flag needs to be set to "False". The good news is, from what I can tell, it *is* accessible from unprivileged processes and user namespaces, which makes it easier to trigger. It's not related to a VM guest or host, and it's not directly triggered by network traffic. Wait, let me think. Can it be triggered by network traffic? IGMP/MLD packets *do* have the ability to affect multicast group membership on the bridge... I need to double-check that by looking at `br_multicast_list_adjacent`.
|
| 83/2 |
2026/05/31 18:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bridge/br_multicast.c Name:br_multicast_list_adjacent]
Results: map[SourceCode:4853: /**
4854: * br_multicast_list_adjacent - Returns snooped multicast addresses
4855: * @dev: The bridge port adjacent to which to retrieve addresses
4856: * @br_ip_list: The list to store found, snooped multicast IP addresses in
4857: *
4858: * Creates a list of IP addresses (struct br_ip_list) sensed by the multicast
4859: * snooping feature on all bridge ports of dev's bridge device, excluding
4860: * the addresses from dev itself.
4861: *
4862: * Returns the number of items added to br_ip_list.
4863: *
4864: * Notes:
4865: * - br_ip_list needs to be initialized by caller
4866: * - br_ip_list might contain duplicates in the end
4867: * (needs to be taken care of by caller)
4868: * - br_ip_list needs to be freed by caller
4869: */
4870: int br_multicast_list_adjacent(struct net_device *dev,
4871: struct list_head *br_ip_list)
4872: {
4873: struct net_bridge *br;
4874: struct net_bridge_port *port;
4875: struct net_bridge_port_group *group;
4876: struct br_ip_list *entry;
4877: int count = 0;
4878:
4879: rcu_read_lock();
4880: if (!br_ip_list || !netif_is_bridge_port(dev))
4881: goto unlock;
4882:
4883: port = br_port_get_rcu(dev);
4884: if (!port || !port->br)
4885: goto unlock;
4886:
4887: br = port->br;
4888:
4889: list_for_each_entry_rcu(port, &br->port_list, list) {
4890: if (!port->dev || port->dev == dev)
4891: continue;
4892:
4893: hlist_for_each_entry_rcu(group, &port->mglist, mglist) {
4894: entry = kmalloc_obj(*entry, GFP_ATOMIC);
4895: if (!entry)
4896: goto unlock;
4897:
4898: entry->addr = group->key.addr;
4899: list_add(&entry->list, br_ip_list);
4900: count++;
4901: }
4902: }
4903:
4904: unlock:
4905: rcu_read_unlock();
4906: return count;
4907: }
SourceFile:net/bridge/br_multicast.c]
|
| 84/2 |
2026/05/31 18:17 |
llm |
expert |
0m
Model:
Tokens:
input: 66725
output: 42
thoughts: 137
Thoughts: **Multicast Group Snooping Vulnerability - A Potential Denial-of-Service**
Okay, hold on a second! This `br_multicast_list_adjacent` function... it's returning snooped multicast addresses, right? And those addresses are getting added to the bridge's forwarding table whenever it sees an IGMP or MLD join message from the local network. That's a key observation! An attacker with local network access could exploit this by flooding the bridge with a deluge of IGMP/MLD join messages. Each message adds another multicast group to the bridge's list.
The problem, as I see it, is how `batadv_mcast_mla_bridge_get` then processes all of these groups. If it retrieves all these groups and processes them in that O(N^2) loop, that's a recipe for disaster. The more groups an attacker adds, the slower the bridge becomes as its mla processing slows to a crawl! That spells a denial-of-service vulnerability.
The next thing I need to figure out is if there's any safeguard in place to limit the number of snooped multicast groups. Are there any limits that would prevent a flood? I should check the implementation of `br_multicast_new_group` to see if there are any rate-limiting mechanisms or maximum group limits.
|
| 85/2 |
2026/05/31 18:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bridge/br_multicast.c Name:br_multicast_new_group]
Results: map[SourceCode:1276: struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br,
1277: struct br_ip *group)
1278: {
1279: struct net_bridge_mdb_entry *mp;
1280: int err;
1281:
1282: mp = br_mdb_ip_get(br, group);
1283: if (mp)
1284: return mp;
1285:
1286: if (atomic_read(&br->mdb_hash_tbl.nelems) >= br->hash_max) {
1287: trace_br_mdb_full(br->dev, group);
1288: br_mc_disabled_update(br->dev, false, NULL);
1289: br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false);
1290: return ERR_PTR(-E2BIG);
1291: }
1292:
1293: mp = kzalloc_obj(*mp, GFP_ATOMIC);
1294: if (unlikely(!mp))
1295: return ERR_PTR(-ENOMEM);
1296:
1297: mp->br = br;
1298: mp->addr = *group;
1299: mp->mcast_gc.destroy = br_multicast_destroy_mdb_entry;
1300: timer_setup(&mp->timer, br_multicast_group_expired, 0);
1301: err = rhashtable_lookup_insert_fast(&br->mdb_hash_tbl, &mp->rhnode,
1302: br_mdb_rht_params);
1303: if (err) {
1304: kfree(mp);
1305: mp = ERR_PTR(err);
1306: } else {
1307: hlist_add_head_rcu(&mp->mdb_node, &br->mdb_list);
1308: }
1309:
1310: return mp;
1311: }
SourceFile:net/bridge/br_multicast.c]
|
| 86/2 |
2026/05/31 18:17 |
llm |
expert |
0m
Model:
Tokens:
input: 67793
output: 37
thoughts: 32
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, and delivered as a first-person internal monologue:
**Thinking About MDB Entry Limits**
Right, let me see... I'm looking at this bit of code and the `br->hash_max` variable. Ah, yes, that's the key – it's the constraint on the number of entries allowed within that MDB structure. Interesting. I need to refresh my memory, though. I know it's there to prevent unbounded growth, potential memory exhaustion, and to balance the performance characteristics, but... I'm a bit hazy on the default value of `br->hash_max`. I know I've configured it in the past for specific applications, but what's the baseline, out-of-the-box setting? Need to verify that. I need to understand that default to ensure my configurations are aligned with overall system resources. Okay, I'll have to dig that up. Let's see...
|
| 87/2 |
2026/05/31 18:17 |
tool |
codesearch-find-references |
0m
Error:
Args: map[ContextFile:net/bridge/br_multicast.c Name:hash_max]
Results: map[References:<nil> TruncatedOutput:false]
|
| 88/2 |
2026/05/31 18:17 |
llm |
expert |
0m
Model:
Tokens:
input: 68095
output: 40
thoughts: 12
Thoughts: Okay, let's dive into this. Here's my thought process on this:
**Analyzing the `br_multicast_init` Function**
Alright, so the task at hand is to understand `br_multicast_init`. Immediately, my brain kicks into gear. My field is networking, specifically bridging and multicast, so this function name is pretty descriptive. The "br" prefix likely indicates that this is related to a bridge, and "multicast" clearly points to multicast functionality. The "init" suggests it's an initialization or setup function, a crucial step.
I'm thinking, what's my initial goal here? To understand its purpose within the broader bridging framework. What resources are initialized? Which data structures? How does it integrate with the kernel and the networking stack? Does it register any hooks, allocate memory, or set up any synchronization primitives? What are its dependencies on other functions or modules within the bridging code? I will need to understand the relationship of this function and others to get a comprehensive understanding. My gut says this is a critical starting point for multicast operations within a bridge, the first piece of the puzzle. I need to get into the code and see exactly what it does.
|
| 89/2 |
2026/05/31 18:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/bridge/br_multicast.c Name:br_multicast_init]
Results: map[SourceCode:4139: void br_multicast_init(struct net_bridge *br)
4140: {
4141: br->hash_max = BR_MULTICAST_DEFAULT_HASH_MAX;
4142:
4143: br_multicast_ctx_init(br, NULL, &br->multicast_ctx);
4144:
4145: br_opt_toggle(br, BROPT_MULTICAST_ENABLED, true);
4146: br_opt_toggle(br, BROPT_HAS_IPV6_ADDR, true);
4147:
4148: spin_lock_init(&br->multicast_lock);
4149: INIT_HLIST_HEAD(&br->mdb_list);
4150: INIT_HLIST_HEAD(&br->mcast_gc_list);
4151: INIT_WORK(&br->mcast_gc_work, br_multicast_gc_work);
4152: }
SourceFile:net/bridge/br_multicast.c]
|
| 90/2 |
2026/05/31 18:17 |
llm |
expert |
0m
Model:
Tokens:
input: 68689
output: 44
thoughts: 17
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing `BR_MULTICAST_DEFAULT_HASH_MAX`**
Alright, let's dive into this. My initial thought is to immediately check the definition and context of `BR_MULTICAST_DEFAULT_HASH_MAX`. I want to understand its purpose within the broader multicast hashing framework. I'm assuming it relates to a maximum size or bound for a hash table or similar data structure used to manage multicast group membership or filtering.
Specifically, I need to know how this value impacts performance – is it a trade-off between memory consumption and lookup speed? Does a larger value result in better collision avoidance, and if so, at what memory cost? Or is this parameter perhaps related to a hardware implementation? Does a larger value mean more efficient usage of hardware resources in specific network devices?
I also need to see where this constant is used. Are there any conditional statements that affect its value or usage based on other system parameters or configurations? I'll be looking for relationships with other relevant constants, like those pertaining to network interface limits or the overall memory budget for networking functions. I’ll need to trace its utilization in the relevant code sections and assess the potential implications of any modifications to its defined value. Finally, I'll consider how this parameter relates to multicast flooding and how setting it might improve or worsen performance.
|
| 91/2 |
2026/05/31 18:17 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:net/bridge/br_private.h Name:BR_MULTICAST_DEFAULT_HASH_MAX]
Results: map[SourceCode: SourceFile:]
|
| 92/2 |
2026/05/31 18:17 |
llm |
expert |
0m
Model:
Tokens:
input: 69063
output: 35
thoughts: 18
Thoughts: Here's a summary of my thought process, as requested:
**Digging into Multicast Hashing**
Okay, so I need to find something specific, and my first instinct is to go straight to the code. Specifically, I'm thinking about multicast hashing, probably related to bridging. The name "BR\_MULTICAST\_DEFAULT\_HASH\_MAX" immediately jumped to mind, so I'm going to start there. It's likely a constant or macro defining the maximum size of a hash table used for multicast forwarding in a bridge context. I'll need to figure out where that's defined and used. I'm imagining using `grep` as my primary tool to hunt it down – that's the most efficient way to quickly locate the relevant code sections that utilize this parameter. From there, I can figure out its purpose and usage.
|
| 93/2 |
2026/05/31 18:17 |
tool |
grepper |
0m
Args: map[Expression:BR_MULTICAST_DEFAULT_HASH_MAX PathPrefix:net/bridge/]
Results: map[Output:net/bridge/br_multicast.c=4139=void br_multicast_init(struct net_bridge *br)
net/bridge/br_multicast.c-4140-{
net/bridge/br_multicast.c:4141: br->hash_max = BR_MULTICAST_DEFAULT_HASH_MAX;
net/bridge/br_multicast.c-4142-
--
net/bridge/br_private.h-30-
net/bridge/br_private.h:31:#define BR_MULTICAST_DEFAULT_HASH_MAX 4096
net/bridge/br_private.h-32-#define BR_MULTICAST_QUERY_INTVL_MIN msecs_to_jiffies(1000)
]
|
| 94/2 |
2026/05/31 18:17 |
llm |
expert |
0m
Model:
Tokens:
input: 69497
output: 66
thoughts: 2229
Thoughts: **Analysis of the Multicast Group Vulnerability in Batman-adv**
Okay, let's break this down. My initial assessment focusing on network-level triggers using multicast limits proved to be a dead end. The bridge's multicast group limit is a factor, but not the primary vulnerability. Specifically, while `BR_MULTICAST_DEFAULT_HASH_MAX` limits bridged snooping to 4096 groups, this does not apply to network-triggered IGMP/MLD messages. The kernel doesn't automatically join groups based on network packets; this happens locally with `IP_ADD_MEMBERSHIP`. Neither a malicious filesystem or peripheral are involved. Nor are guest or host VMs at fault.
The heart of this vulnerability lies in an O(N^2) loop within `batadv_mcast_mla_meshif_get_ipv4` and its related IPv6 and bridge variants, specifically within `batadv_mcast_mla_is_duplicate`. This code iterates through a list of multicast addresses to check for duplicates. Each unique multicast group added to the `batadv` interface results in an ever-growing list the code needs to iterate over. The critical issue is the lack of a limit on the number of multicast groups a user can add via sockets.
This code is triggered periodically for *every* `batadv` interface. The attacker's power to trigger the softlockup comes from the ability to join multicast groups on a `batadv` interface. And an unprivileged user can do this with `setsockopt(IP_ADD_MEMBERSHIP)`, specifying the interface index. They just need to know the `ifindex` of the `batadv` interface.
The real key here is that an unprivileged user, by manipulating the `RLIMIT_NOFILE`, can create an enormous number of sockets. Each socket, in turn, can add multiple multicast group memberships. The system will start performing *billions* of iterations in the vulnerability code, and that's the DoS.
This loop runs under `rcu_read_lock()`, meaning no interruptions can occur during the iteration, resulting in softlockups and RCU stalls. If the relevant kernel configuration is enabled (softlockup or RCU stall panic), the system will simply panic. Even without this, the system will experience a severe DoS on the batman-adv subsystem as all events will be blocked on a single threaded workqueue.
To summarize, this is a vulnerability that is:
* Exploitable: False
* DenialOfService: True
* Accessible From Unprivileged Processes: True (if a `batadv` interface exists)
* Accessible From User Namespaces: True (user namespace can create interfaces)
* VM Guest Trigger: False
* VM Host Trigger: False
* Ethernet Network Trigger: False
* Other Remote Trigger: False
* Peripheral Trigger: False
* Malicious Filesystem Trigger: False
The unprivileged user can also create a `batadv` interface inside a user namespace if such an interface is currently absent. This requires `CAP_NET_ADMIN` in the netns. This is a very robust DoS with low barriers to entry.
|
| 95/2 |
2026/05/31 18:17 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 96/2 |
2026/05/31 18:17 |
llm |
expert |
0m
Model:
Tokens:
input: 72537
output: 870
thoughts: 540
Thoughts: **Vulnerability Deep Dive: O(N^2) DoS in batman-adv Multicast Handling**
Okay, the results are in. Time to articulate this vulnerability clearly. I need to explain the root cause and reasoning behind this.
Essentially, this boils down to an algorithmic complexity issue, specifically an O(N^2) vulnerability within the `batman-adv` multicast handling code. My focus is on the functions `batadv_mcast_mla_meshif_get_ipv4()`, `batadv_mcast_mla_meshif_get_ipv6()`, and related routines. It all centers around the `batadv_mcast_mla_update()` periodic worker. This worker meticulously collects information on every multicast group joined on the `batadv` interface. Here's where it gets interesting: for each multicast group, `batadv_mcast_mla_is_duplicate()` is called to check if the group is already present in the `mcast_list`. This check performs a linear search.
Now, here's where an unprivileged user can exploit the situation. By crafting a scenario involving a significant number of sockets—limited by `RLIMIT_NOFILE`, which can be creatively bypassed—and by joining up to `sysctl_igmp_max_memberships` unique multicast groups per socket on the `batadv` interface using `setsockopt(IP_ADD_MEMBERSHIP)`, the user can effectively trigger an avalanche. Imagine hundreds of thousands of unique groups all flooding the `mcast_list`. This forces the O(N^2) loop to iterate through billions of comparisons. Because the loop is executed under `rcu_read_lock()` and doesn’t utilize `cond_resched()`, the CPU gets monopolized for tens of seconds or more. This leads to the softlockup detector being triggered, followed by RCU stall warnings, culminating in a Denial of Service (DoS).
The beauty (or rather, the ugliness) of this is that if a `batadv` interface is already present on the system, any unprivileged user can directly trigger this bug without any special permissions, because `IP_ADD_MEMBERSHIP` doesn't require elevated privileges. Even if no `batadv` interface exists, it's still exploitable. An unprivileged user can create a new user and network namespace, acquire `CAP_NET_ADMIN` within that namespace, and then create a `batadv` interface through netlink (which also triggers automatic loading of the `batman-adv` module if permitted).
A key thing is that this is purely a CPU exhaustion vulnerability. It's not exploitable for privilege escalation or memory corruption. Furthermore, remote exploitation (e.g., via network traffic such as IGMP joins) is prevented. The bridge multicast snooping code limits the number of tracked groups (by default, `BR_MULTICAST_DEFAULT_HASH_MAX` is 4096), so that limit is not enough to cause a noticeable delay in that O(N^2) loop.
|
| 97/1 |
2026/05/31 18:18 |
action |
format-explanation |
0m
Results: map[Explanation:**Analysis of the Bug Report**
The bug report indicates a softlockup (a task blocked for more than 20 seconds)
occurring in the `batman-adv` subsystem, specifically within the
`batadv_mcast_mla_update` worker thread. The stack trace shows the thread is
interrupted while freeing a list of multicast addresses
(`batadv_mcast_mla_list_free` -> `kfree` -> `debug_check_no_obj_freed`).
**Root Cause**
The root cause is an algorithmic complexity vulnerability (O(N^2)) in the
`batman-adv` multicast handling code.
The periodic worker `batadv_mcast_mla_update()` collects all multicast groups
joined on the `batadv` interface (or its bridge) by calling functions like
`batadv_mcast_mla_meshif_get_ipv4()`. For every multicast group found on the
device, it checks if the mapped MAC address is already in the local `mcast_list`
by calling `batadv_mcast_mla_is_duplicate()`.
`batadv_mcast_mla_is_duplicate()` performs a linear search over the
`mcast_list`. If there are `N` unique multicast groups joined on the interface,
building this list requires `O(N^2)` iterations. Furthermore, subsequent
functions like `batadv_mcast_mla_tt_retract()` and `batadv_mcast_mla_tt_add()`
also perform `O(N^2)` list comparisons. These loops run with `rcu_read_lock()`
held and without any `cond_resched()` calls.
**Security Impact**
* **Denial of Service (DoS):** An attacker can force the kernel to perform tens
of billions of iterations in an uninterruptible context. This monopolizes the
CPU core, triggers the softlockup detector, causes RCU stalls, and completely
blocks the `batadv_event_workqueue`. If the system is configured to panic on
softlockups or RCU stalls (`panic_on_rcu_stall`), it will crash the machine.
* **Exploitable:** No. This is purely a CPU exhaustion/algorithmic complexity
issue. There is no memory corruption, out-of-bounds access, or use-after-free
involved.
**Reachability**
* **Accessible From Unprivileged Processes:** Yes. Joining a multicast group via
`setsockopt(IP_ADD_MEMBERSHIP)` does not require any privileges (e.g.,
`CAP_NET_ADMIN`). An unprivileged user can create many sockets (up to
`RLIMIT_NOFILE`, which can be bypassed by forking multiple processes) and join
up to `net.ipv4.igmp_max_memberships` (default 20) unique multicast groups per
socket. By doing this, an attacker can easily register hundreds of thousands of
unique multicast groups on an existing `batadv` interface, triggering the O(N^2)
CPU exhaustion.
* **Accessible From User Namespaces:** Yes. If a `batadv` interface does not
exist by default, an unprivileged user can create a new user and network
namespace, acquire `CAP_NET_ADMIN` within that namespace, and create a `batadv`
interface via netlink (which also auto-loads the `batman-adv` module if
available). Once the interface is created, the worker thread is scheduled, and
the user can proceed to flood it with multicast group joins.
* **Ethernet Network Trigger:** No. While remote IGMP/MLD join packets can add
multicast groups to a bridge (which `batman-adv` also queries via
`batadv_mcast_mla_bridge_get`), the Linux bridge subsystem enforces a strict
limit on the number of snooped multicast groups
(`BR_MULTICAST_DEFAULT_HASH_MAX`, defaulting to 4096). 4096 groups are not
enough to cause a softlockup in the O(N^2) loop (it takes only a few
milliseconds). Therefore, it cannot be triggered remotely.]
|