| 0/0 |
2026/07/05 09:31 |
flow |
assessment-security |
47m
Results: map[DenialOfService:true Explanation:Based on the analysis of the bug report and the kernel source code, here is the
assessment of the vulnerability:
**Root Cause Analysis:**
The bug report shows a softlockup on CPU 0 in `__batadv_dat_purge` (part of the
`batman-adv` subsystem) and a secondary hung task in `hub_event` on CPU 1. The
hung task in `hub_event` is a side effect of CPU 0 being hogged by the
softlockup, which prevents other tasks from running and releasing locks (such as
`hcd->address0_mutex`).
The root cause of the softlockup is an unbounded accumulation of entries in the
Distributed ARP Table (DAT) hash table (`bat_priv->dat.hash`). The `batman-adv`
subsystem snoops incoming and outgoing ARP and DHCP packets to populate this
hash table. Functions like `batadv_dat_snoop_incoming_arp_request`,
`batadv_dat_snoop_incoming_arp_reply`, and `batadv_dat_snoop_outgoing_dhcp_ack`
extract IP and MAC addresses from these packets and add them to the DAT hash
table using `batadv_dat_entry_add`.
Crucially, there is no limit on the number of entries that can be added to the
DAT hash table. An attacker can send a flood of crafted ARP or DHCP packets with
varying source/destination IP and MAC addresses. Each unique packet will cause a
new `batadv_dat_entry` to be allocated and added to the hash table.
When the periodic worker `batadv_dat_purge` runs, it iterates over the 1024
buckets of the hash table. For each bucket, it acquires a spinlock
(`spin_lock_bh`), iterates over all entries to check if they have timed out, and
then releases the lock. If an attacker has added millions of entries, this
iteration takes an excessively long time, causing a softlockup (and potentially
memory exhaustion/OOM due to the unbounded allocations).
**Trigger Vectors:**
1. **Remote/Network Trigger:** An attacker on the same layer 2 mesh network
(Ethernet or WiFi) can trigger this remotely by flooding the network with
crafted ARP requests, ARP replies, or DHCP ACKs. The victim's `batman-adv`
interface will snoop these packets and add the spoofed entries to its DAT hash
table.
2. **User Namespaces:** A local unprivileged user can trigger this by creating a
new user and network namespace. Within the new network namespace, they have
`CAP_NET_ADMIN` and can create a `batadv` interface (`ip link add type batadv`).
They can then use an `AF_PACKET` socket or raw socket to inject millions of
crafted DHCP ACK or ARP packets directly into the `batadv` interface, filling
the DAT hash table and causing a softlockup.
3. **Unprivileged Processes (without namespaces):** A local unprivileged process
cannot easily trigger this because creating a `batadv` interface requires
`CAP_NET_ADMIN`. Furthermore, injecting arbitrary ARP or DHCP packets with
spoofed IPs requires raw sockets (`CAP_NET_RAW`) or binding to privileged ports
(e.g., port 67 for DHCP server).
**Security Impact:**
This is a Denial of Service (DoS) vulnerability. It allows an attacker to cause
memory exhaustion and CPU softlockups, effectively crashing or hanging the
system. It does not involve memory corruption (like use-after-free or
out-of-bounds access) and is therefore not exploitable for privilege escalation
or information leaks. Exploitable:false FilesystemTrigger:false NetworkTrigger:true PeripheralTrigger:false RemoteTrigger:true Unprivileged:false UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/07/05 09:31 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/07/05 09:31 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/3f3d9db1bef082cdfb426b991fb1345e1a6d9cc5]
|
| 3/1 |
2026/07/05 09:33 |
action |
kernel-builder |
22m
Results: map[KernelObj:/app/workdir/cache/build/f57c13100302aa5a260ce48f0aa47e93e51b8a02]
|
| 4/1 |
2026/07/05 09:56 |
action |
codesearch-prepare |
18m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/07/05 10:14 |
agent |
expert |
4m
Model:
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:true PeripheralTrigger:false RemoteTrigger:true Unprivileged:false UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption, elevated privileges, or an information leak.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Information leaks are exploitable on their own and should be classified as such. A bug that copies kernel
memory contents to userspace (e.g. an out-of-bounds read whose result is returned to the caller, or
uninitialized stack/heap bytes written to a user buffer) is exploitable: it can reveal kernel pointer
values and defeat KASLR, expose sensitive data such as cryptographic keys or other processes' memory, and
serves as a necessary building block in most modern kernel privilege-escalation exploit chains. Do not classify
an information leak as non-exploitable solely because it does not directly cause a memory write or control-flow
hijack; the leak itself is the exploit primitive.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
INFO: task kworker/1:0:32 blocked for more than 143 seconds.
Tainted: G L syzkaller #0
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:kworker/1:0 state:D stack:22080 pid:32 tgid:32 ppid:2 task_flags:0x4208060 flags:0x00080000
Workqueue: usb_hub_wq hub_event
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5387 [inline]
__schedule+0x169e/0x54f0 kernel/sched/core.c:7188
__schedule_loop kernel/sched/core.c:7267 [inline]
rt_mutex_schedule+0x76/0xf0 kernel/sched/core.c:7563
rt_mutex_slowlock_block+0x508/0x680 kernel/locking/rtmutex.c:1662
__rt_mutex_slowlock kernel/locking/rtmutex.c:1739 [inline]
__rt_mutex_slowlock_locked kernel/locking/rtmutex.c:1779 [inline]
rt_mutex_slowlock+0x2dc/0x780 kernel/locking/rtmutex.c:1819
__rt_mutex_lock kernel/locking/rtmutex.c:1834 [inline]
__mutex_lock_common kernel/locking/rtmutex_api.c:535 [inline]
mutex_lock_nested+0x168/0x1d0 kernel/locking/rtmutex_api.c:553
hub_even[ 407.202529][ T39] hub_port_connect drivers/usb/core/hub.c:5465 [inline]
hub_even[ 407.202529][ T39] hub_port_connect_change drivers/usb/core/hub.c:5707 [inline]
hub_even[ 407.202529][ T39] port_event drivers/usb/core/hub.c:5871 [inline]
hub_even[ 407.202529][ T39] hub_event+0x21e0/0x4f60 drivers/usb/core/hub.c:5953
process_one_work+0x9a3/0x1710 kernel/workqueue.c:3312
process_scheduled_works kernel/workqueue.c:3403 [inline]
worker_thread+0xba8/0x11e0 kernel/workqueue.c:3489
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
Showing all locks held in the system:
5 locks held by kworker/1:0/32:
#0: ffff88801b379538 ((wq_completion)usb_hub_wq){+.+.}-{0:0}, at: process_one_work+0x890/0x1710 kernel/workqueue.c:3284
#1: ffffc90000a6fc40 ((work_completion)(&hub->events)){+.+.}-{0:0}, at: process_one_work+0x8b7/0x1710 kernel/workqueue.c:3285
#2: ffff88802bfc4210 (&dev->mutex){....}-{4:4}, at: device_lock include/linux/device.h:1040 [inline]
#2: ffff88802bfc4210 (&dev->mutex){....}-{4:4}, at: hub_event+0x17c/0x4f60 drivers/usb/core/hub.c:5899
#3: ffff88802bf84658 (&port_dev->status_lock){+.+.}-{4:4}, at: usb_lock_port drivers/usb/core/hub.c:3252 [inline]
#3: ffff88802bf84658 (&port_dev->status_lock){+.+.}-{4:4}, at: hub_port_connect drivers/usb/core/hub.c:5464 [inline]
#3: ffff88802bf84658 (&port_dev->status_lock){+.+.}-{4:4}, at: hub_port_connect_change drivers/usb/core/hub.c:5707 [inline]
#3: ffff88802bf84658 (&port_dev->status_lock){+.+.}-{4:4}, at: port_event drivers/usb/core/hub.c:5871 [inline]
#3: ffff88802bf84658 (&port_dev->status_lock){+.+.}-{4:4}, at: hub_event+0x21b0/0x4f60 drivers/usb/core/hub.c:5953
#4: ffff88802b0ca058 (hcd->address0_mutex){+.+.}-{4:4}, at: hub_port_connect drivers/usb/core/hub.c:5465 [inline]
#4: ffff88802b0ca058 (hcd->address0_mutex){+.+.}-{4:4}, at: hub_port_connect_change drivers/usb/core/hub.c:5707 [inline]
#4: ffff88802b0ca058 (hcd->address0_mutex){+.+.}-{4:4}, at: port_event drivers/usb/core/hub.c:5871 [inline]
#4: ffff88802b0ca058 (hcd->address0_mutex){+.+.}-{4:4}, at: hub_event+0x21e0/0x4f60 drivers/usb/core/hub.c:5953
1 lock held by khungtaskd/39:
#0: ffffffff8e3c81c0 (rcu_read_lock){....}-{1:3}, at: rcu_lock_acquire include/linux/rcupdate.h:300 [inline]
#0: ffffffff8e3c81c0 (rcu_read_lock){....}-{1:3}, at: rcu_read_lock include/linux/rcupdate.h:838 [inline]
#0: ffffffff8e3c81c0 (rcu_read_lock){....}-{1:3}, at: debug_show_all_locks+0x2e/0x180 kernel/locking/lockdep.c:6777
5 locks held by kworker/0:2/822:
#0: ffff88801b379538 ((wq_completion)usb_hub_wq){+.+.}-{0:0}, at: process_one_work+0x890/0x1710 kernel/workqueue.c:3284
#1: ffffc90005457c40 ((work_completion)(&hub->events)){+.+.}-{0:0}, at: process_one_work+0x8b7/0x1710 kernel/workqueue.c:3285
#2: ffff88802bf20210 (&dev->mutex){....}-{4:4}, at: device_lock include/linux/device.h:1040 [inline]
#2: ffff88802bf20210 (&dev->mutex){....}-{4:4}, at: hub_event+0x17c/0x4f60 drivers/usb/core/hub.c:5899
#3: ffff88802bf61658 (&port_dev->status_lock){+.+.}-{4:4}, at: usb_lock_port drivers/usb/core/hub.c:3252 [inline]
#3: ffff88802bf61658 (&port_dev->status_lock){+.+.}-{4:4}, at: hub_port_connect drivers/usb/core/hub.c:5464 [inline]
#3: ffff88802bf61658 (&port_dev->status_lock){+.+.}-{4:4}, at: hub_port_connect_change drivers/usb/core/hub.c:5707 [inline]
#3: ffff88802bf61658 (&port_dev->status_lock){+.+.}-{4:4}, at: port_event drivers/usb/core/hub.c:5871 [inline]
#3: ffff88802bf61658 (&port_dev->status_lock){+.+.}-{4:4}, at: hub_event+0x21b0/0x4f60 drivers/usb/core/hub.c:5953
#4: ffff88802b0ca058 (hcd->address0_mutex){+.+.}-{4:4}, at: hub_port_connect drivers/usb/core/hub.c:5465 [inline]
#4: ffff88802b0ca058 (hcd->address0_mutex){+.+.}-{4:4}, at: hub_port_connect_change drivers/usb/core/hub.c:5707 [inline]
#4: ffff88802b0ca058 (hcd->address0_mutex){+.+.}-{4:4}, at: port_event drivers/usb/core/hub.c:5871 [inline]
#4: ffff88802b0ca058 (hcd->address0_mutex){+.+.}-{4:4}, at: hub_event+0x21e0/0x4f60 drivers/usb/core/hub.c:5953
5 locks held by kworker/u8:9/1043:
#0: ffff888033eef938 ((wq_completion)bat_events){+.+.}-{0:0}, at: process_one_work+0x890/0x1710 kernel/workqueue.c:3284
#1: ffffc900056b7c40 ((work_completion)(&(&bat_priv->dat.work)->work)){+.+.}-{0:0}, at: process_one_work+0x8b7/0x1710 kernel/workqueue.c:3285
#2: ffffffff8e25f340 (local_bh){.+.+}-{1:3}, at: __local_bh_disable_ip+0x3c/0x420 kernel/softirq.c:163
#3: ffffffff8e3c81c0 (rcu_read_lock){....}-{1:3}, at: __local_bh_disable_ip+0x3c/0x420 kernel/softirq.c:163
#4: ffff88805839f558 (&hash->list_locks[i]){+...}-{3:3}, at: spin_lock_bh include/linux/spinlock_rt.h:90 [inline]
#4: ffff88805839f558 (&hash->list_locks[i]){+...}-{3:3}, at: __batadv_dat_purge+0x131/0x400 net/batman-adv/distributed-arp-table.c:173
4 locks held by kworker/u8:11/1184:
#0: ffff88801be86138 ((wq_completion)netns){+.+.}-{0:0}, at: process_one_work+0x890/0x1710 kernel/workqueue.c:3284
#1: ffffc90005ecfc40 (net_cleanup_work){+.+.}-{0:0}, at: process_one_work+0x8b7/0x1710 kernel/workqueue.c:3285
#2: ffffffff8f795000 (pernet_ops_rwsem){++++}-{4:4}, at: cleanup_net+0xf4/0x800 net/core/net_namespace.c:673
#3: ffffffff8e3ce3b0 (rcu_state.barrier_mutex){+.+.}-{4:4}, at: rcu_barrier+0x4c/0x580 kernel/rcu/tree.c:3828
1 lock held by dhcpcd/5493:
#0: ffffffff8f7a44b8 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_net_lock include/linux/rtnetlink.h:130 [inline]
#0: ffffffff8f7a44b8 (rtnl_mutex){+.+.}-{4:4}, at: devinet_ioctl+0x32b/0x1b30 net/ipv4/devinet.c:1120
2 locks held by getty/5583:
#0: ffff8880380f50a0 (&tty->ldisc_sem){++++}-{0:0}, at: tty_ldisc_ref_wait+0x25/0x70 drivers/tty/tty_ldisc.c:243
#1: ffffc90003cae2e0 (&ldata->atomic_read_lock){+.+.}-{4:4}, at: n_tty_read+0x462/0x13a0 drivers/tty/n_tty.c:2211
4 locks held by syz-executor/10325:
#0: ffffffff8eef59c8 (&ops->srcu#2){.+.+}-{0:0}, at: rcu_lock_acquire include/linux/rcupdate.h:300 [inline]
#0: ffffffff8eef59c8 (&ops->srcu#2){.+.+}-{0:0}, at: rcu_read_lock include/linux/rcupdate.h:838 [inline]
#0: ffffffff8eef59c8 (&ops->srcu#2){.+.+}-{0:0}, at: rtnl_link_ops_get+0x23/0x250 net/core/rtnetlink.c:570
#1: ffffffff8f7a44b8 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_lock net/core/rtnetlink.c:80 [inline]
#1: ffffffff8f7a44b8 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_nets_lock net/core/rtnetlink.c:341 [inline]
#1: ffffffff8f7a44b8 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_newlink+0x883/0x1bb0 net/core/rtnetlink.c:4108
#2: ffff8880b8746510 ((&pcs->lock)){+.+.}-{3:3}, at: alloc_from_pcs mm/slub.c:4743 [inline]
#2: ffff8880b8746510 ((&pcs->lock)){+.+.}-{3:3}, at: slab_alloc_node mm/slub.c:4883 [inline]
#2: ffff8880b8746510 ((&pcs->lock)){+.+.}-{3:3}, at: kmem_cache_alloc_node_noprof+0x304/0x6e0 mm/slub.c:4950
#3: ffffffff8e3c81c0 (rcu_read_lock){....}-{1:3}, at: rcu_lock_acquire include/linux/rcupdate.h:300 [inline]
#3: ffffffff8e3c81c0 (rcu_read_lock){....}-{1:3}, at: rcu_read_lock include/linux/rcupdate.h:838 [inline]
#3: ffffffff8e3c81c0 (rcu_read_lock){....}-{1:3}, at: __rt_spin_trylock kernel/locking/spinlock_rt.c:110 [inline]
#3: ffffffff8e3c81c0 (rcu_read_lock){....}-{1:3}, at: rt_spin_trylock+0x10c/0x2b0 kernel/locking/spinlock_rt.c:118
=============================================
NMI backtrace for cpu 1
CPU: 1 UID: 0 PID: 39 Comm: khungtaskd Tainted: G L syzkaller #0 PREEMPT_{RT,(full)}
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/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+0xfd3/0x1030 kernel/hung_task.c:561
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
Sending NMI from CPU 1 to CPUs 0:
NMI backtrace for cpu 0
CPU: 0 UID: 0 PID: 1043 Comm: kworker/u8:9 Tainted: G L syzkaller #0 PREEMPT_{RT,(full)}
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/2026
Workqueue: bat_events batadv_dat_purge
RIP: 0010:rcu_is_watching_curr_cpu include/linux/context_tracking.h:128 [inline]
RIP: 0010:rcu_is_watching+0x3a/0xb0 kernel/rcu/tree.c:752
Code: e8 0b 10 99 09 89 c3 83 f8 08 73 65 49 bf 00 00 00 00 00 fc ff df 4c 8d 34 dd 90 b7 c4 8d 4c 89 f0 48 c1 e8 03 42 80 3c 38 00 <74> 08 4c 89 f7 e8 7c 7d 84 00 48 c7 c3 98 8e bd 92 49 03 1e 48 89
RSP: 0018:ffffc900056b7a48 EFLAGS: 00000046
RAX: 1ffffffff1b896f2 RBX: 0000000000000000 RCX: 0000000080000001
RDX: 0000000000000000 RSI: ffffffff8bca7040 RDI: ffffffff8bca7000
RBP: 0000000000000400 R08: ffffffff8fcf67f7 R09: 1ffffffff1f9ecfe
R10: dffffc0000000000 R11: fffffbfff1f9ecff R12: 0000000000000282
R13: dffffc0000000000 R14: ffffffff8dc4b790 R15: dffffc0000000000
FS: 0000000000000000(0000) GS:ffff888125a5b000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f337f3e1e9c CR3: 00000000378b6000 CR4: 00000000003526f0
Call Trace:
<TASK>
trace_irq_disable+0x3b/0x140 include/trace/events/preemptirq.h:36
__local_bh_enable_ip+0xaa/0x2b0 kernel/softirq.c:270
local_bh_enable include/linux/bottom_half.h:33 [inline]
spin_unlock_bh include/linux/spinlock_rt.h:116 [inline]
__batadv_dat_purge+0x344/0x400 net/batman-adv/distributed-arp-table.c:185
batadv_dat_purge+0x20/0x70 net/batman-adv/distributed-arp-table.c:204
process_one_work+0x9a3/0x1710 kernel/workqueue.c:3312
process_scheduled_works kernel/workqueue.c:3403 [inline]
worker_thread+0xba8/0x11e0 kernel/workqueue.c:3489
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
Reply: Based on the analysis of the bug report and the kernel source code, here is the assessment of the vulnerability:
**Root Cause Analysis:**
The bug report shows a softlockup on CPU 0 in `__batadv_dat_purge` (part of the `batman-adv` subsystem) and a secondary hung task in `hub_event` on CPU 1. The hung task in `hub_event` is a side effect of CPU 0 being hogged by the softlockup, which prevents other tasks from running and releasing locks (such as `hcd->address0_mutex`).
The root cause of the softlockup is an unbounded accumulation of entries in the Distributed ARP Table (DAT) hash table (`bat_priv->dat.hash`). The `batman-adv` subsystem snoops incoming and outgoing ARP and DHCP packets to populate this hash table. Functions like `batadv_dat_snoop_incoming_arp_request`, `batadv_dat_snoop_incoming_arp_reply`, and `batadv_dat_snoop_outgoing_dhcp_ack` extract IP and MAC addresses from these packets and add them to the DAT hash table using `batadv_dat_entry_add`.
Crucially, there is no limit on the number of entries that can be added to the DAT hash table. An attacker can send a flood of crafted ARP or DHCP packets with varying source/destination IP and MAC addresses. Each unique packet will cause a new `batadv_dat_entry` to be allocated and added to the hash table.
When the periodic worker `batadv_dat_purge` runs, it iterates over the 1024 buckets of the hash table. For each bucket, it acquires a spinlock (`spin_lock_bh`), iterates over all entries to check if they have timed out, and then releases the lock. If an attacker has added millions of entries, this iteration takes an excessively long time, causing a softlockup (and potentially memory exhaustion/OOM due to the unbounded allocations).
**Trigger Vectors:**
1. **Remote/Network Trigger:** An attacker on the same layer 2 mesh network (Ethernet or WiFi) can trigger this remotely by flooding the network with crafted ARP requests, ARP replies, or DHCP ACKs. The victim's `batman-adv` interface will snoop these packets and add the spoofed entries to its DAT hash table.
2. **User Namespaces:** A local unprivileged user can trigger this by creating a new user and network namespace. Within the new network namespace, they have `CAP_NET_ADMIN` and can create a `batadv` interface (`ip link add type batadv`). They can then use an `AF_PACKET` socket or raw socket to inject millions of crafted DHCP ACK or ARP packets directly into the `batadv` interface, filling the DAT hash table and causing a softlockup.
3. **Unprivileged Processes (without namespaces):** A local unprivileged process cannot easily trigger this because creating a `batadv` interface requires `CAP_NET_ADMIN`. Furthermore, injecting arbitrary ARP or DHCP packets with spoofed IPs requires raw sockets (`CAP_NET_RAW`) or binding to privileged ports (e.g., port 67 for DHCP server).
**Security Impact:**
This is a Denial of Service (DoS) vulnerability. It allows an attacker to cause memory exhaustion and CPU softlockups, effectively crashing or hanging the system. It does not involve memory corruption (like use-after-free or out-of-bounds access) and is therefore not exploitable for privilege escalation or information leaks.
|
| 6/2 |
2026/07/05 10:14 |
llm |
expert |
0m
Model:
Tokens:
input: 11546
output: 45
thoughts: 475
Thoughts: **Analyzing a Tricky Kernel Bug**
Okay, so I've got a bug report here, and immediately a few red flags jump out. First, there's a kernel worker thread, `kworker/1:0:32`, that's been blocked for a whopping 143 seconds. The call trace points to something within the USB subsystem, specifically `hub_event`. That's not ideal, but it's not the biggest concern yet.
Then, things get *interesting*. There's a softlockup reported on CPU 0, and the `Tainted: [L]=SOFTLOCKUP` flag is set, which is always a bad sign. Looking at the call trace, I see that the `kworker/u8:9` process is the culprit. It seems to be looping, and the function where it is looping is `__batadv_dat_purge`, which is linked to `batadv_dat_purge`.
My initial thought is to cross-reference the `__batadv_dat_purge` with that hung task timeout, the two have to be linked, and I have to find out if it's an infinite loop in `net/batman-adv/distributed-arp-table.c`. I need to figure out *why* this worker thread is stuck. Is it holding a lock it can't release? Is there a deadlock somewhere? I need to get into the code for `__batadv_dat_purge` and see what it's doing, and understand the relationship between the USB hub event handling and this batman-adv dat purge. It's a confusing combination of subsystems. This requires a deeper dive into the interactions between the USB subsystem, the workqueue mechanism, and the batman-adv networking code. This one has "fun" written all over it.
|
| 7/2 |
2026/07/05 10:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c Name:__batadv_dat_purge]
Results: map[SourceCode: 146: /**
147: * __batadv_dat_purge() - delete entries from the DAT local storage
148: * @bat_priv: the bat priv with all the mesh interface information
149: * @to_purge: function in charge to decide whether an entry has to be purged or
150: * not. This function takes the dat_entry as argument and has to
151: * returns a boolean value: true is the entry has to be deleted,
152: * false otherwise
153: *
154: * Loops over each entry in the DAT local storage and deletes it if and only if
155: * the to_purge function passed as argument returns true.
156: */
157: static void __batadv_dat_purge(struct batadv_priv *bat_priv,
158: bool (*to_purge)(struct batadv_dat_entry *))
159: {
160: spinlock_t *list_lock; /* protects write access to the hash lists */
161: struct batadv_dat_entry *dat_entry;
162: struct hlist_node *node_tmp;
163: struct hlist_head *head;
164: u32 i;
165:
166: if (!bat_priv->dat.hash)
167: return;
168:
169: for (i = 0; i < bat_priv->dat.hash->size; i++) {
170: head = &bat_priv->dat.hash->table[i];
171: list_lock = &bat_priv->dat.hash->list_locks[i];
172:
173: spin_lock_bh(list_lock);
174: hlist_for_each_entry_safe(dat_entry, node_tmp, head,
175: hash_entry) {
176: /* if a helper function has been passed as parameter,
177: * ask it if the entry has to be purged or not
178: */
179: if (to_purge && !to_purge(dat_entry))
180: continue;
181:
182: hlist_del_rcu(&dat_entry->hash_entry);
183: batadv_dat_entry_put(dat_entry);
184: }
185: spin_unlock_bh(list_lock);
186: }
187: }
SourceFile:net/batman-adv/distributed-arp-table.c]
|
| 8/2 |
2026/07/05 10:14 |
llm |
expert |
0m
Model:
Tokens:
input: 13181
output: 59
thoughts: 138
Thoughts: **Analyzing a Softlockup in `__batadv_dat_purge`**
Okay, so I'm looking at this softlockup, and the trace points directly to `__batadv_dat_purge`. Right, this function is looping through `bat_priv->dat.hash->size`. It seems like the core issue lies in the locking strategy within that loop: it locks `list_lock` for each bucket, iterates through the list, and then unlocks. My initial thought is, this softlockup is likely occurring because either `bat_priv->dat.hash->size` is enormous, the lists within each bucket are excessively long, or the operation, in general, is just taking a very long time.
Wait a second... hang on... `bat_priv->dat.hash->size` is *1024*? That seems like a standard, or at least common, hash table size, typically a reasonable default. That throws a bit of a wrench in my initial line of thinking. So, if the hash table size isn't the primary culprit, it points towards the list length or something else entirely in the purging process. Let's delve into the function definition of `batadv_dat_purge` and then circle back to take a look at `batadv_dat_hash_init` to confirm the initialization values and potentially glean insights into why it's becoming slow. This requires a deeper understanding of the interplay between the purge function and how the hash table gets set up in the first place.
|
| 9/2 |
2026/07/05 10:14 |
tool |
codesearch-find-references |
0m
Error:
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c IncludeSnippetLines:0 Name:batadv_dat_hash_init SourceTreePrefix:]
Results: map[References:<nil> TruncatedOutput:false]
|
| 10/2 |
2026/07/05 10:14 |
llm |
expert |
0m
Model:
Tokens:
input: 13720
output: 38
thoughts: 16
Thoughts: Here's my thought process, summarized as you requested:
**Delving into `bat_priv->dat.hash` Initialization**
Okay, so I need to track down exactly where this `bat_priv->dat.hash` structure gets initialized. I know this is likely a critical data structure within this particular module, so understanding its initial state is going to be key to understanding the module's overall behavior and debugging any related issues. Let's start with a focused search. I'll use a precise query, something like "find all instances where `bat_priv->dat.hash` is assigned a value" or, if I'm feeling particularly thorough and the codebase uses a common initialization pattern, I might search for the broader pattern of initialization for similar data structures within the kernel module. I'm assuming, of course, that this is a private structure to this module (as indicated by `bat_priv`), so that simplifies the search. I also need to consider the possibility of implicit or default initializations by the compiler, but it's likely there's some explicit action involved. It's a fundamental part of the module so it will be initialized some where. The goal is pinpointing where this data is actually prepared for use. That's the first step!
|
| 11/2 |
2026/07/05 10:14 |
tool |
grepper |
0m
Args: map[Expression:dat\.hash.*=.*batadv_hash_new PathPrefix:net/batman-adv]
Results: map[Output:net/batman-adv/distributed-arp-table.c=808=int batadv_dat_init(struct batadv_priv *bat_priv)
--
net/batman-adv/distributed-arp-table.c-812-
net/batman-adv/distributed-arp-table.c:813: bat_priv->dat.hash = batadv_hash_new(1024);
net/batman-adv/distributed-arp-table.c-814-
]
|
| 12/2 |
2026/07/05 10:14 |
llm |
expert |
0m
Model:
Tokens:
input: 14173
output: 44
thoughts: 290
Thoughts: **Analyzing a Potential Softlockup**
Okay, so the hash size is 1024. If I'm looping through 1024 buckets and using `spin_lock_bh` for each, that could take some time, but it's probably not enough to trigger a softlockup, which typically takes a much longer duration β like 20 seconds or more. But wait, this is running with PREEMPT_RT enabled! That changes things significantly.
With `PREEMPT_RT` in the mix, and specifically `spin_lock_bh`, it looks like bottom halves are disabled. But does that mean preemption is *also* disabled? I have to double-check. Okay, in the RT context, `spin_lock_bh` actually maps to `local_bh_disable()` *and* `rt_spin_lock()`. And `local_bh_disable()` is what disables softirqs.
Hold on a second... where is `__batadv_dat_purge` being called from? Oh, right, it's called from `batadv_dat_purge`, which is running within a workqueue item. And that's happening within `kworker/u8:9`... it's a workqueue item! Could the list be corrupted?
Hmm, if there's a cycle, something like `hlist_for_each_entry_safe` would loop infinitely. But wait, `hlist_del_rcu` *is* called if `to_purge` returns true. So let's see what that `to_purge` function is doing in `batadv_dat_purge`. That's where the real problem is, I think.
|
| 13/2 |
2026/07/05 10:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c Name:batadv_dat_purge]
Results: map[SourceCode: 189: /**
190: * batadv_dat_purge() - periodic task that deletes old entries from the local
191: * DAT hash table
192: * @work: kernel work struct
193: */
194: static void batadv_dat_purge(struct work_struct *work)
195: {
196: struct delayed_work *delayed_work;
197: struct batadv_priv_dat *priv_dat;
198: struct batadv_priv *bat_priv;
199:
200: delayed_work = to_delayed_work(work);
201: priv_dat = container_of(delayed_work, struct batadv_priv_dat, work);
202: bat_priv = container_of(priv_dat, struct batadv_priv, dat);
203:
204: __batadv_dat_purge(bat_priv, batadv_dat_to_purge);
205: batadv_dat_start_timer(bat_priv);
206: }
SourceFile:net/batman-adv/distributed-arp-table.c]
|
| 14/2 |
2026/07/05 10:14 |
llm |
expert |
0m
Model:
Tokens:
input: 15204
output: 46
thoughts: 14
Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:
**Analyzing `batadv_dat_to_purge`**
Alright, let's take a look at `batadv_dat_to_purge`. Hmm, what's its purpose? Given the naming convention, it's pretty clear that this variable is going to be central to removing data related to the BatAdv protocol. "Purge" is a pretty strong word, so I'd wager it signifies some sort of aggressive cleanup.
I need to understand what specific data this is referring to. Is it stale routing information, cached packet data, or perhaps some sort of temporary bookkeeping information? The context around it will be important. I should probably trace its uses and see when and where this variable gets set, modified, and finally, used. Knowing the data type will be crucial for understanding how it is utilized.
Given my understanding of distributed routing protocols, I should immediately try to work out what possible race conditions this might be protecting against, given that the purpose is to "purge" data. Is it dealing with stale information that could have security implications if not removed? Or could it be simply preventing resource exhaustion? Iβll need to figure out what triggers the purge and how it's integrated with other BatAdv operations to get a complete picture. Let's see how this ties into the overall state machine, or whatever concurrent process is responsible. This sounds like an area that warrants careful examination.
|
| 15/2 |
2026/07/05 10:14 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c Name:batadv_dat_to_purge]
Results: map[SourceCode: 134: /**
135: * batadv_dat_to_purge() - check whether a dat_entry has to be purged or not
136: * @dat_entry: the entry to check
137: *
138: * Return: true if the entry has to be purged now, false otherwise.
139: */
140: static bool batadv_dat_to_purge(struct batadv_dat_entry *dat_entry)
141: {
142: return batadv_has_timed_out(dat_entry->last_update,
143: BATADV_DAT_ENTRY_TIMEOUT);
144: }
SourceFile:net/batman-adv/distributed-arp-table.c]
|
| 16/2 |
2026/07/05 10:15 |
llm |
expert |
0m
Model:
Tokens:
input: 15799
output: 36
thoughts: 93
Thoughts: Okay, here's my breakdown of what's going through my mind as I analyze this code snippet, keeping in mind my expertise in the field:
**Debugging a Potential Infinite Loop in BatAdv Data Purging**
Alright, so the core of the issue seems to revolve around the interaction between `batadv_dat_to_purge` and a loop that purges data. The code's saying that if `batadv_dat_to_purge` returns `false`, indicating an entry *shouldn't* be purged, then we `continue`. Seems straightforward, right? But the danger lies in the interaction of this logic with what appears to be a linked list or some form of cyclical data structure.
The critical piece here is the potential for an infinite loop. If `to_purge` (presumably a local variable or a function being called) consistently returns `false` *because of* a cycle in the data structure, we're stuck. The logic keeps skipping the deletion, and the loop goes on forever. That's the alarm bell going off in my head. I know the intricacies of such structures, the potential for edge cases is always there.
So, how can we have a cycle? This is where I start to hypothesize, I have to assume the worst. I need to understand the data flow, how these entries are added to the list in the first place. My immediate concern is with concurrency issues. Is there a missing lock somewhere? Is it possible that concurrent threads are modifying the list, maybe while `batadv_dat_to_purge` or the deletion process is running? This would make the list's traversal unreliable.
My next step is clear. I need to follow the trail back to where entries are added to `bat_priv->dat.hash`. Understanding the creation and population of this data structure is key. I'll need to examine that code very closely to look for potential race conditions or logic errors that would lead to a cyclical state. I'll be looking for inconsistencies and missing synchronization primitives as a priority. This feels like a classic concurrency bug, and I need to be methodical in its debugging.
|
| 17/2 |
2026/07/05 10:15 |
tool |
grepper |
0m
Args: map[Expression:batadv_hash_add.*dat\.hash PathPrefix:net/batman-adv]
Results: map[Output:net/batman-adv/distributed-arp-table.c=365=static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
--
net/batman-adv/distributed-arp-table.c-394- kref_get(&dat_entry->refcount);
net/batman-adv/distributed-arp-table.c:395: hash_added = batadv_hash_add(bat_priv->dat.hash, batadv_compare_dat,
net/batman-adv/distributed-arp-table.c-396- batadv_hash_dat, dat_entry,
]
|
| 18/2 |
2026/07/05 10:15 |
llm |
expert |
0m
Model:
Tokens:
input: 16547
output: 40
thoughts: 13
Thoughts: Okay, let's dive into this. Here's my take on `batadv_hash_add`:
**Analyzing `batadv_hash_add`**
Alright, so the first thing I'm doing is focusing on the function `batadv_hash_add`. Immediately, I'm thinking about its role within the larger framework. Based on the name, I'm assuming it's responsible for, well, adding things to a hash table. In this context, I'm guessing "things" refers to data relevant to the BATMAN-adv protocol. This probably means either routing information, neighbor information, or something similar. I need to figure out what type of data is hashed.
Now, let's consider the mechanics. Iβm thinking about the hash function used. Is it a custom one? Standard? What are its properties? Collision resolution is paramount in hash tables, so I need to understand how conflicts are handled. Is it chaining, open addressing, or something more sophisticated? What is the hash table implementation itself? Is it dynamically resized? How are entries linked and managed internally? It's key to identify any potential performance bottlenecks such as poor hash key distribution or long collision chains.
My mind is also jumping to concurrency. Is this function thread-safe? Does it use locks, atomic operations, or some lock-free mechanism to protect against race conditions when multiple threads access the hash table concurrently? If there are any synchronization primitives, how is it designed? Is there any contention? This has implications for the overall performance of the BATMAN-adv data path, especially under heavy load.
Finally, I'm looking for edge cases. What happens when the hash table is full? What about situations where the hash key is invalid, or the data being added is corrupted? How does the function handle these error conditions? Are there any security considerations related to the hash key generation, collision resolution, or the addition of data? I need to understand this to ensure that the code is robust and doesn't introduce any vulnerabilities.
|
| 19/2 |
2026/07/05 10:15 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/hash.h Name:batadv_hash_add]
Results: map[SourceCode: 64: /**
65: * batadv_hash_add() - adds data to the hashtable
66: * @hash: storage hash table
67: * @compare: callback to determine if 2 hash elements are identical
68: * @choose: callback calculating the hash index
69: * @data: data passed to the aforementioned callbacks as argument
70: * @data_node: to be added element
71: *
72: * Return: 0 on success, 1 if the element already is in the hash
73: * and -1 on error.
74: */
75: static inline int batadv_hash_add(struct batadv_hashtable *hash,
76: batadv_hashdata_compare_cb compare,
77: batadv_hashdata_choose_cb choose,
78: const void *data,
79: struct hlist_node *data_node)
80: {
81: u32 index;
82: int ret = -1;
83: struct hlist_head *head;
84: struct hlist_node *node;
85: spinlock_t *list_lock; /* spinlock to protect write access */
86:
87: if (!hash)
88: goto out;
89:
90: index = choose(data, hash->size);
91: head = &hash->table[index];
92: list_lock = &hash->list_locks[index];
93:
94: spin_lock_bh(list_lock);
95:
96: hlist_for_each(node, head) {
97: if (!compare(node, data))
98: continue;
99:
100: ret = 1;
101: goto unlock;
102: }
103:
104: /* no duplicate found in list, add new element */
105: hlist_add_head_rcu(data_node, head);
106: atomic_inc(&hash->generation);
107:
108: ret = 0;
109:
110: unlock:
111: spin_unlock_bh(list_lock);
112: out:
113: return ret;
114: }
SourceFile:net/batman-adv/hash.h]
|
| 20/2 |
2026/07/05 10:15 |
llm |
expert |
0m
Model:
Tokens:
input: 17721
output: 46
thoughts: 255
Thoughts: **Analyzing a Potential Deadlock in the batadv Code**
Okay, let's break this down. My initial observation is that `batadv_hash_add` uses `spin_lock_bh` on `list_lock`, and I see that `__batadv_dat_purge` also acquires `list_lock` with `spin_lock_bh`. So, at least in theory, the core list operations *should* be protected. The question then becomes: is there a deadlock possibility here?
The hung task is `kworker/1:0:32` in `hub_event`, and we have a softlockup in `kworker/u8:9` running `batadv_dat_purge`. The hung task timeout is rather substantial at 143 seconds. Thatβs a long time. The softlockup is on CPU 0. I need to understand what's happening. Could `batadv_dat_purge` simply be taking an abnormally long time because of the sheer number of elements it's processing?
I recall that `batadv_dat_purge` iterates through 1024 buckets, locking, iterating, and unlocking for each one. If the list is exceptionally long, this could result in significant processing time. But even with a long list, 20+ seconds still feels excessive. The timeout suggests something more significant.
Perhaps there's a flood of DAT entries contributing to the problem? Let me take a look at `batadv_dat_entry_add` and see if I can get a better handle on the potential for contention.
|
| 21/2 |
2026/07/05 10:15 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c Name:batadv_dat_entry_add]
Results: map[SourceCode: 358: /**
359: * batadv_dat_entry_add() - add a new dat entry or update it if already exists
360: * @bat_priv: the bat priv with all the mesh interface information
361: * @ip: ipv4 to add/edit
362: * @mac_addr: mac address to assign to the given ipv4
363: * @vid: VLAN identifier
364: */
365: static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
366: u8 *mac_addr, unsigned short vid)
367: {
368: struct batadv_dat_entry *dat_entry;
369: int hash_added;
370:
371: dat_entry = batadv_dat_entry_hash_find(bat_priv, ip, vid);
372: /* if this entry is already known, just update it */
373: if (dat_entry) {
374: if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr))
375: ether_addr_copy(dat_entry->mac_addr, mac_addr);
376: dat_entry->last_update = jiffies;
377: batadv_dbg(BATADV_DBG_DAT, bat_priv,
378: "Entry updated: %pI4 %pM (vid: %d)\n",
379: &dat_entry->ip, dat_entry->mac_addr,
380: batadv_print_vid(vid));
381: goto out;
382: }
383:
384: dat_entry = kmalloc_obj(*dat_entry, GFP_ATOMIC);
385: if (!dat_entry)
386: goto out;
387:
388: dat_entry->ip = ip;
389: dat_entry->vid = vid;
390: ether_addr_copy(dat_entry->mac_addr, mac_addr);
391: dat_entry->last_update = jiffies;
392: kref_init(&dat_entry->refcount);
393:
394: kref_get(&dat_entry->refcount);
395: hash_added = batadv_hash_add(bat_priv->dat.hash, batadv_compare_dat,
396: batadv_hash_dat, dat_entry,
397: &dat_entry->hash_entry);
398:
399: if (unlikely(hash_added != 0)) {
400: /* remove the reference for the hash */
401: batadv_dat_entry_put(dat_entry);
402: goto out;
403: }
404:
405: batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM (vid: %d)\n",
406: &dat_entry->ip, dat_entry->mac_addr, batadv_print_vid(vid));
407:
408: out:
409: batadv_dat_entry_put(dat_entry);
410: }
SourceFile:net/batman-adv/distributed-arp-table.c]
|
| 22/2 |
2026/07/05 10:15 |
llm |
expert |
0m
Model:
Tokens:
input: 19313
output: 46
thoughts: 327
Thoughts: **Vulnerability Discovery: BATMAN DAT Table Exhaustion**
Okay, so `batadv_dat_entry_add` is triggered by incoming ARP packets. The potential here is starting to solidify. An attacker flooding the system with ARP packets containing a variety of IP addresses could conceivably saturate the DAT hash table. Now, the core question is, does `bat_priv->dat.hash` have a defined maximum size? I need to re-examine `batadv_dat_entry_add` for confirmation.
Alright, the code allocates `dat_entry` using `kmalloc_obj` with `GFP_ATOMIC`. Critically, there's no inherent size check or rate limiting implemented! If an adversary floods the system with a massive volume of ARP packetsβpotentially millionsβit'll trigger the allocation of millions of `dat_entry` objects. This is a very bad sign.
This is going to be a problem when `batadv_dat_purge` comes into play. It has to iterate over *all* of those allocated entries. The mechanism employs a bucket-level spinlock: it locks the bucket, iterates through all entries within that bucket, and then unlocks. If a single bucket ends up containing an enormous number of entries, that spinlock is going to be held for a very long time, essentially halting the processing of any other traffic that requires access to that bucket.
Hmm, let me see, `batadv_dat_purge` operates on one bucket at a time, but with millions of entries, even a single bucket would likely contain thousands of entries. While iterating through thousands of entries and calling `batadv_has_timed_out` (which is just a `time_is_before_jiffies` check) *does* take time, I'm not entirely convinced that it would be sufficient to cause a 20-second delay on its own.
But wait! I'm forgetting something. When purging, `batadv_dat_purge` calls `batadv_dat_entry_put(dat_entry)`. This is where the true weakness lies. I need to understand what `batadv_dat_entry_put` does to understand the complete picture.
|
| 23/2 |
2026/07/05 10:15 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c Name:batadv_dat_entry_put]
Results: map[SourceCode: 121: /**
122: * batadv_dat_entry_put() - decrement the dat_entry refcounter and possibly
123: * release it
124: * @dat_entry: dat_entry to be free'd
125: */
126: static void batadv_dat_entry_put(struct batadv_dat_entry *dat_entry)
127: {
128: if (!dat_entry)
129: return;
130:
131: kref_put(&dat_entry->refcount, batadv_dat_entry_release);
132: }
SourceFile:net/batman-adv/distributed-arp-table.c]
|
| 24/2 |
2026/07/05 10:15 |
llm |
expert |
0m
Model:
Tokens:
input: 20363
output: 46
thoughts: 14
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Analyzing the `batadv_dat_entry_release` Function**
Alright, let's dive into this. The initial thought, "Let's check `batadv_dat_entry_release`," immediately flags my attention. Given my understanding of the B.A.T.M.A.N. advanced codebase, this function is central to resource management within the data structure. My immediate focus is likely on memory deallocation and potential race conditions associated with releasing a DAT (Data Access Table) entry. I'll be looking for things like:
* **Proper synchronization:** Are there locks or atomic operations involved to prevent double-frees or use-after-free scenarios? The shared nature of the DAT necessitates robust concurrency control.
* **Reference counting (if any):** Does the function handle reference counts correctly, ensuring the entry is only freed when no longer in use by any other part of the system (e.g., forwarding, routing, or the originator).
* **Error handling:** What happens if the `dat_entry` pointer is NULL, or if there's a problem with the underlying memory management? Are there assertions or checks in place?
* **Interaction with other data structures:** How does this function relate to other relevant functions, like `batadv_dat_entry_add` or `batadv_dat_entry_lookup`? Are there consistent access patterns and data integrity guarantees?
* **Potential for memory leaks:** Are all associated resources (e.g., buffers, associated metadata) being freed correctly? Are there any indirect pointers or complex structures that might lead to leaks if not handled meticulously?
* **Performance implications:** Are there any performance bottlenecks? Are memory barriers employed to minimize cache misses and ensure correct memory ordering?
* **Specific DAT considerations:** What kind of data is being stored in the DAT entry? What is the impact of this `release` function if the entry had a specific data that was created with the DAT?
Basically, I want to understand the complete lifecycle of a DAT entry from creation to release, and ensure the `batadv_dat_entry_release` function is a responsible citizen in that process. The goal is to verify robustness, prevent any subtle memory bugs, and maintain optimal performance within the B.A.T.M.A.N. advanced framework.
|
| 25/2 |
2026/07/05 10:15 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c Name:batadv_dat_entry_release]
Results: map[SourceCode: 107: /**
108: * batadv_dat_entry_release() - release dat_entry from lists and queue for free
109: * after rcu grace period
110: * @ref: kref pointer of the dat_entry
111: */
112: static void batadv_dat_entry_release(struct kref *ref)
113: {
114: struct batadv_dat_entry *dat_entry;
115:
116: dat_entry = container_of(ref, struct batadv_dat_entry, refcount);
117:
118: kfree_rcu(dat_entry, rcu);
119: }
SourceFile:net/batman-adv/distributed-arp-table.c]
|
| 26/2 |
2026/07/05 10:15 |
llm |
expert |
0m
Model:
Tokens:
input: 21174
output: 34
thoughts: 942
Thoughts: **Analyzing the Softlockup in `__batadv_dat_purge`**
Okay, so `batadv_dat_entry_release` just frees memory using `kfree_rcu`. The softlockup itself is happening within `__batadv_dat_purge`, which calls `rcu_is_watching` from within `trace_irq_disable`. It's ultimately originating from `spin_unlock_bh`. That's where the problem lies.
Initially, I wondered why we're softlocking in `spin_unlock_bh`. My immediate thought was that a long loop in `__batadv_dat_purge` might be the culprit. Specifically, the loop `for (i = 0; i < bat_priv->dat.hash->size; i++)` might not yield, triggering the softlockup. `spin_unlock_bh` enables bottom halves which handle softirqs. However, `batadv_dat_purge` is a workqueue item, so it runs in process context, meaning it should be preemptable. The issue seems to be that if there are many entries, the loop could execute for a long time without yielding.
Looking closer, `spin_unlock_bh` on PREEMPT_RT could allow softirqs to run. However, the puzzle is why it's a softlockup. It looks like the workqueue thread `kworker/u8:9` isn't yielding. If it runs for, say, 20 seconds without `cond_resched()`, the softlockup is triggered.
Peeking at the code in `__batadv_dat_purge`, I see the loop:
```c
for (i = 0; i < bat_priv->dat.hash->size; i++) {
head = &bat_priv->dat.hash->table[i];
list_lock = &bat_priv->dat.hash->list_locks[i];
spin_lock_bh(list_lock);
hlist_for_each_entry_safe(dat_entry, node_tmp, head,
hash_entry) {
/* if a helper function has been passed as parameter,
* ask it if the entry has to be purged or not
*/
if (to_purge && !to_purge(dat_entry))
continue;
hlist_del_rcu(&dat_entry->hash_entry);
batadv_dat_entry_put(dat_entry);
}
spin_unlock_bh(list_lock);
}
```
There's no `cond_resched()` in this loop! That's the smoking gun. If the hash table has a lot of entries, or if the `to_purge` function takes a while, the loop can cause a softlockup because it blocks the workqueue thread from yielding. Specifically, `bat_priv->dat.hash->size` is 1024, but if the loop iterates over millions of entries, there's a problem.
The interesting thing is that if the hash table is empty, it's fast (just 1024 lock/unlock pairs). So the crux of the issue is whether we can get a *lot* of entries in there. Now I need to check how many entries are added and if there's any limit on the number of DAT entries in `batadv_dat_entry_add`. It looks like there's no limit. That confirms the hypothesis: a flood of entries can lead to softlockups.
|
| 27/2 |
2026/07/05 10:15 |
tool |
grepper |
0m
Args: map[Expression:batadv_dat_entry_add PathPrefix:net/batman-adv]
Results: map[Output:net/batman-adv/distributed-arp-table.c=325=batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
--
net/batman-adv/distributed-arp-table.c-358-/**
net/batman-adv/distributed-arp-table.c:359: * batadv_dat_entry_add() - add a new dat entry or update it if already exists
net/batman-adv/distributed-arp-table.c-360- * @bat_priv: the bat priv with all the mesh interface information
--
net/batman-adv/distributed-arp-table.c-364- */
net/batman-adv/distributed-arp-table.c:365:static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
net/batman-adv/distributed-arp-table.c-366- u8 *mac_addr, unsigned short vid)
--
net/batman-adv/distributed-arp-table.c=1125=bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
--
net/batman-adv/distributed-arp-table.c-1155-
net/batman-adv/distributed-arp-table.c:1156: batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
net/batman-adv/distributed-arp-table.c-1157-
--
net/batman-adv/distributed-arp-table.c=1221=bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
--
net/batman-adv/distributed-arp-table.c-1247-
net/batman-adv/distributed-arp-table.c:1248: batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
net/batman-adv/distributed-arp-table.c-1249-
--
net/batman-adv/distributed-arp-table.c=1286=void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
--
net/batman-adv/distributed-arp-table.c-1310-
net/batman-adv/distributed-arp-table.c:1311: batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
net/batman-adv/distributed-arp-table.c:1312: batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
net/batman-adv/distributed-arp-table.c-1313-
--
net/batman-adv/distributed-arp-table.c=1333=bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
--
net/batman-adv/distributed-arp-table.c-1375- */
net/batman-adv/distributed-arp-table.c:1376: batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
net/batman-adv/distributed-arp-table.c:1377: batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
net/batman-adv/distributed-arp-table.c-1378-
--
net/batman-adv/distributed-arp-table.c=1620=static void batadv_dat_put_dhcp(struct batadv_priv *bat_priv, u8 *chaddr,
--
net/batman-adv/distributed-arp-table.c-1632-
net/batman-adv/distributed-arp-table.c:1633: batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid);
net/batman-adv/distributed-arp-table.c:1634: batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
net/batman-adv/distributed-arp-table.c-1635-
--
net/batman-adv/distributed-arp-table.c=1733=void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv,
--
net/batman-adv/distributed-arp-table.c-1758-
net/batman-adv/distributed-arp-table.c:1759: batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid);
net/batman-adv/distributed-arp-table.c:1760: batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
net/batman-adv/distributed-arp-table.c-1761-
]
|
| 28/2 |
2026/07/05 10:15 |
llm |
expert |
0m
Model:
Tokens:
input: 24192
output: 38
thoughts: 1039
Thoughts: **Analyzing a Potential Softlockup**
Okay, so an attacker can flood the DAT hash table by sending crafted ARP or DHCP packets. Since there's no size limit, this can lead to memory exhaustion or a softlockup in `batadv_dat_purge`. The bug report says `kworker/1:0:32` is blocked for a long time, and the softlockup is on CPU 0 within `batadv_dat_purge`. That's odd. Why is `hub_event` blocked?
It looks like `hub_event` is trying to acquire `dev->mutex` or something similar, and it's causing the issue. Let's examine the locks held by the blocked `kworker/1:0/32`. It seems it's holding five locks: a workqueue lock, a work completion lock, `dev->mutex`, `port_dev->status_lock`, and `hcd->address0_mutex`. It appears to be blocked in `mutex_lock_nested` within `hub_port_connect`, which is trying to acquire `hcd->address0_mutex`. But the dump shows it already holds this mutex! I need to double-check `hub_port_connect` around line 5465. Something's not right.
|
| 29/2 |
2026/07/05 10:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/core/hub.c Name:hub_port_connect]
Results: map[SourceCode:5390: static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
5391: u16 portchange)
5392: {
5393: int status = -ENODEV;
5394: int i;
5395: unsigned unit_load;
5396: struct usb_device *hdev = hub->hdev;
5397: struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
5398: struct usb_port *port_dev = hub->ports[port1 - 1];
5399: struct usb_device *udev = port_dev->child;
5400: static int unreliable_port = -1;
5401: bool retry_locked;
5402:
5403: /* Disconnect any existing devices under this port */
5404: if (udev) {
5405: if (hcd->usb_phy && !hdev->parent)
5406: usb_phy_notify_disconnect(hcd->usb_phy, udev->speed);
5407: usb_disconnect(&port_dev->child);
5408: }
5409:
5410: /* We can forget about a "removed" device when there's a physical
5411: * disconnect or the connect status changes.
5412: */
5413: if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
5414: (portchange & USB_PORT_STAT_C_CONNECTION))
5415: clear_bit(port1, hub->removed_bits);
5416:
5417: if (portchange & (USB_PORT_STAT_C_CONNECTION |
5418: USB_PORT_STAT_C_ENABLE)) {
5419: status = hub_port_debounce_be_stable(hub, port1);
5420: if (status < 0) {
5421: if (status != -ENODEV &&
5422: port1 != unreliable_port &&
5423: printk_ratelimit())
5424: dev_err(&port_dev->dev, "connect-debounce failed\n");
5425: portstatus &= ~USB_PORT_STAT_CONNECTION;
5426: unreliable_port = port1;
5427: } else {
5428: portstatus = status;
5429: }
5430: }
5431:
5432: /* Return now if debouncing failed or nothing is connected or
5433: * the device was "removed".
5434: */
5435: if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
5436: test_bit(port1, hub->removed_bits)) {
5437:
5438: /*
5439: * maybe switch power back on (e.g. root hub was reset)
5440: * but only if the port isn't owned by someone else.
5441: */
5442: if (hub_is_port_power_switchable(hub)
5443: && !usb_port_is_power_on(hub, portstatus)
5444: && !port_dev->port_owner)
5445: set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
5446:
5447: if (portstatus & USB_PORT_STAT_ENABLE)
5448: goto done;
5449: return;
5450: }
5451: if (hub_is_superspeed(hub->hdev))
5452: unit_load = 150;
5453: else
5454: unit_load = 100;
5455:
5456: status = 0;
5457:
5458: for (i = 0; i < PORT_INIT_TRIES; i++) {
5459: if (hub_port_stop_enumerate(hub, port1, i)) {
5460: status = -ENODEV;
5461: break;
5462: }
5463:
5464: usb_lock_port(port_dev);
5465: mutex_lock(hcd->address0_mutex);
5466: retry_locked = true;
5467: /* reallocate for each attempt, since references
5468: * to the previous one can escape in various ways
5469: */
5470: udev = usb_alloc_dev(hdev, hdev->bus, port1);
5471: if (!udev) {
5472: dev_err(&port_dev->dev,
5473: "couldn't allocate usb_device\n");
5474: mutex_unlock(hcd->address0_mutex);
5475: usb_unlock_port(port_dev);
5476: goto done;
5477: }
5478:
5479: usb_set_device_state(udev, USB_STATE_POWERED);
5480: udev->bus_mA = hub->mA_per_port;
5481: udev->level = hdev->level + 1;
5482:
5483: /* Devices connected to SuperSpeed hubs are USB 3.0 or later */
5484: if (hub_is_superspeed(hub->hdev))
5485: udev->speed = USB_SPEED_SUPER;
5486: else
5487: udev->speed = USB_SPEED_UNKNOWN;
5488:
5489: choose_devnum(udev);
5490: if (udev->devnum <= 0) {
5491: status = -ENOTCONN; /* Don't retry */
5492: goto loop;
5493: }
5494:
5495: /* reset (non-USB 3.0 devices) and get descriptor */
5496: status = hub_port_init(hub, udev, port1, i, NULL);
5497: if (status < 0)
5498: goto loop;
5499:
5500: mutex_unlock(hcd->address0_mutex);
5501: usb_unlock_port(port_dev);
5502: retry_locked = false;
5503:
5504: if (udev->quirks & USB_QUIRK_DELAY_INIT)
5505: msleep(2000);
5506:
5507: /* consecutive bus-powered hubs aren't reliable; they can
5508: * violate the voltage drop budget. if the new child has
5509: * a "powered" LED, users should notice we didn't enable it
5510: * (without reading syslog), even without per-port LEDs
5511: * on the parent.
5512: */
5513: if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
5514: && udev->bus_mA <= unit_load) {
5515: u16 devstat;
5516:
5517: status = usb_get_std_status(udev, USB_RECIP_DEVICE, 0,
5518: &devstat);
5519: if (status) {
5520: dev_dbg(&udev->dev, "get status %d ?\n", status);
5521: goto loop_disable;
5522: }
5523: if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
5524: dev_err(&udev->dev,
5525: "can't connect bus-powered hub "
5526: "to this port\n");
5527: if (hub->has_indicators) {
5528: hub->indicator[port1-1] =
5529: INDICATOR_AMBER_BLINK;
5530: queue_delayed_work(
5531: system_power_efficient_wq,
5532: &hub->leds, 0);
5533: }
5534: status = -ENOTCONN; /* Don't retry */
5535: goto loop_disable;
5536: }
5537: }
5538:
5539: /* check for devices running slower than they could */
5540: if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
5541: && udev->speed == USB_SPEED_FULL
5542: && highspeed_hubs != 0)
5543: check_highspeed(hub, udev, port1);
5544:
5545: /* Store the parent's children[] pointer. At this point
5546: * udev becomes globally accessible, although presumably
5547: * no one will look at it until hdev is unlocked.
5548: */
5549: status = 0;
5550:
5551: mutex_lock(&usb_port_peer_mutex);
5552:
5553: /* We mustn't add new devices if the parent hub has
5554: * been disconnected; we would race with the
5555: * recursively_mark_NOTATTACHED() routine.
5556: */
5557: spin_lock_irq(&device_state_lock);
5558: if (hdev->state == USB_STATE_NOTATTACHED)
5559: status = -ENOTCONN;
5560: else
5561: port_dev->child = udev;
5562: spin_unlock_irq(&device_state_lock);
5563: mutex_unlock(&usb_port_peer_mutex);
5564:
5565: /* Run it through the hoops (find a driver, etc) */
5566: if (!status) {
5567: status = usb_new_device(udev);
5568: if (status) {
5569: mutex_lock(&usb_port_peer_mutex);
5570: spin_lock_irq(&device_state_lock);
5571: port_dev->child = NULL;
5572: spin_unlock_irq(&device_state_lock);
5573: mutex_unlock(&usb_port_peer_mutex);
5574: } else {
5575: if (hcd->usb_phy && !hdev->parent)
5576: usb_phy_notify_connect(hcd->usb_phy,
5577: udev->speed);
5578: }
5579: }
5580:
5581: if (status)
5582: goto loop_disable;
5583:
5584: status = hub_power_remaining(hub);
5585: if (status)
5586: dev_dbg(hub->intfdev, "%dmA power budget left\n", status);
5587:
5588: return;
5589:
5590: loop_disable:
5591: hub_port_disable(hub, port1, 1);
5592: loop:
5593: usb_ep0_reinit(udev);
5594: release_devnum(udev);
5595: hub_free_dev(udev);
5596: if (retry_locked) {
5597: mutex_unlock(hcd->address0_mutex);
5598: usb_unlock_port(port_dev);
5599: }
5600: usb_put_dev(udev);
5601: if ((status == -ENOTCONN) || (status == -ENOTSUPP))
5602: break;
5603:
5604: /* When halfway through our retry count, power-cycle the port */
5605: if (i == (PORT_INIT_TRIES - 1) / 2) {
5606: dev_info(&port_dev->dev, "attempt power cycle\n");
5607: usb_hub_set_port_power(hdev, hub, port1, false);
5608: msleep(2 * hub_power_on_good_delay(hub));
5609: usb_hub_set_port_power(hdev, hub, port1, true);
5610: msleep(hub_power_on_good_delay(hub));
5611: }
5612: }
5613: if (hub->hdev->parent ||
5614: !hcd->driver->port_handed_over ||
5615: !(hcd->driver->port_handed_over)(hcd, port1)) {
5616: if (status != -ENOTCONN && status != -ENODEV)
5617: dev_err(&port_dev->dev,
5618: "unable to enumerate USB device\n");
5619: }
5620:
5621: done:
5622: hub_port_disable(hub, port1, 1);
5623: if (hcd->driver->relinquish_port && !hub->hdev->parent) {
5624: if (status != -ENOTCONN && status != -ENODEV)
5625: hcd->driver->relinquish_port(hcd, port1);
5626: }
5627: }
SourceFile:drivers/usb/core/hub.c]
|
| 30/2 |
2026/07/05 10:16 |
llm |
expert |
0m
Model:
Tokens:
input: 29270
output: 64
thoughts: 2338
Thoughts: **Root Cause Analysis: Softlockup in `batadv_dat_purge` leading to USB Hub Hang**
Okay, here's what I'm seeing. `kworker/1:0/32` is getting stuck in `mutex_lock_nested`, specifically trying to acquire `hcd->address0_mutex`. That mutex, it turns out, is being held by `kworker/0:2/822`, which is itself stuck deeper in the USB hub driver. But here's the kicker: `kworker/0:2/822` isn't blocked itself; it's *holding* a lock and just not making progress.
The timing seems off, and the bug report mentioning a 143-second block for `kworker/1:0/32` really catches my attention. Why would a worker thread hold a mutex that long without doing anything? Further investigation is needed.
I see that `kworker/0:2/822` is running on CPU 0 but it's *not* running because CPU 0 is softlocked in `batadv_dat_purge` within a workqueue on CPU 0. So, it appears that `kworker/0:2/822` is being preempted and prevented from running due to an unbound workqueue softlockup on the same CPU. This leads to the deadlock.
The evidence points to a softlockup in `batadv_dat_purge`, which is confirmed by the stack trace. The culprit is likely the `dat_purge` function iterating over a large hash table in the `batadv` module.
The problem lies within the `batadv_dat_purge` loop. Because it holds a spinlock but doesn't include a `cond_resched()`, the function runs without yielding to other processes. If the DAT hash table has too many entries, the loop becomes extremely long, hogging the CPU and causing the softlockup.
This isn't just an inefficiency; it's a DoS vulnerability. An attacker can flood the network with crafted ARP packets, which leads to millions of entries in the DAT hash table. Each entry consumes memory and contributes to the softlockup, preventing `batadv_dat_purge` from returning. It's essentially an OOM/softlockup combination.
The key question is exploitability: Can unprivileged processes send raw packets? This is likely remote since it's an network device.
If so, a local network attacker could send a flood of ARP packets to cause this problem. My understanding is that this is a remotely triggerable DoS. Now, I need to confirm whether `batadv_dat_snoop_incoming_arp_request` is invoked for incoming packets.
|
| 31/2 |
2026/07/05 10:16 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c IncludeSnippetLines:0 Name:batadv_dat_snoop_incoming_arp_request SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:batadv_recv_bcast_packet SourceFile:net/batman-adv/routing.c SourceLine:1243] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:batadv_recv_unicast_packet SourceFile:net/batman-adv/routing.c SourceLine:1004]] TruncatedOutput:false]
|
| 32/2 |
2026/07/05 10:16 |
llm |
expert |
0m
Model:
Tokens:
input: 32399
output: 64
thoughts: 203
Thoughts: **Analyzing a Potential Attack Vector**
Okay, so I'm thinking through this potential vulnerability. It looks like the issue stems from how the Batman-adv module handles incoming packets β specifically through `batadv_recv_bcast_packet` and `batadv_recv_unicast_packet`. These functions are directly processing packets received over the mesh network, which immediately flags it as a potential attack surface. An adversary on the mesh itself could definitely trigger something here.
But wait, what about local processes? Can a local, *unprivileged* process cause this to happen? Hmmm... Well, an unprivileged process can definitely send UDP packets, that much is clear. And if those UDP packets are sent to IP addresses that aren't in the ARP cache, that will, in turn, trigger the kernel to generate ARP requests. The tricky part is, the attacker would have to craft this to work.
If someone were crafty enough to target millions of different IPs, sending UDP packets to them on the local subnet, the kernel would flood the network with ARP requests. And those ARP requests, as they're generated by the kernel and sent out, will be intercepted by the `batadv_dat_snoop_outgoing_arp_request` function. *That's* where it gets interesting! It's *supposed* to add them to the DAT hash table. I should probably go back and double-check exactly how `batadv_dat_snoop_outgoing_arp_request` works, just to be sure.
|
| 33/2 |
2026/07/05 10:16 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c IncludeSnippetLines:0 Name:batadv_dat_snoop_outgoing_arp_request SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:batadv_interface_tx SourceFile:net/batman-adv/mesh-interface.c SourceLine:320] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:batadv_interface_tx SourceFile:net/batman-adv/mesh-interface.c SourceLine:359]] TruncatedOutput:false]
|
| 34/2 |
2026/07/05 10:16 |
llm |
expert |
0m
Model:
Tokens:
input: 33115
output: 51
thoughts: 155
Thoughts: **Analyzing the DAT Snooping of ARP Requests in batman-adv**
Okay, so I'm tracing this transmission process and it's definitely coming from `batadv_interface_tx`. That makes sense, as that's the transmit function for the batman-adv soft interface, `bat0`. Now, I'm thinking about a scenario where a local, unprivileged process is sending packets to a lot of different IPs within the `bat0` subnet. The IPv4 stack in the kernel will generate ARP requests in this situation, right? Those ARP requests would naturally go out through `bat0`, which means `batadv_interface_tx` gets called, and in turn, it'll invoke `batadv_dat_snoop_outgoing_arp_request`.
Wait a second⦠I'm starting to remember what `batadv_dat_snoop_outgoing_arp_request` does. It's *adding* the source IP and MAC address to the DAT hash table! That's interesting. I need to refresh my memory on the specifics of that function. Let's dig into `batadv_dat_snoop_outgoing_arp_request` and make sure my understanding is correct.
|
| 35/2 |
2026/07/05 10:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c Name:batadv_dat_snoop_outgoing_arp_request]
Results: map[SourceCode:1115: /**
1116: * batadv_dat_snoop_outgoing_arp_request() - snoop the ARP request and try to
1117: * answer using DAT
1118: * @bat_priv: the bat priv with all the mesh interface information
1119: * @skb: packet to check
1120: *
1121: * Return: true if the message has been sent to the dht candidates, false
1122: * otherwise. In case of a positive return value the message has to be enqueued
1123: * to permit the fallback.
1124: */
1125: bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
1126: struct sk_buff *skb)
1127: {
1128: u16 type = 0;
1129: __be32 ip_dst, ip_src;
1130: u8 *hw_src;
1131: bool ret = false;
1132: struct batadv_dat_entry *dat_entry = NULL;
1133: struct sk_buff *skb_new;
1134: struct net_device *mesh_iface = bat_priv->mesh_iface;
1135: int hdr_size = 0;
1136: unsigned short vid;
1137:
1138: if (!atomic_read(&bat_priv->distributed_arp_table))
1139: goto out;
1140:
1141: vid = batadv_dat_get_vid(skb, &hdr_size);
1142:
1143: type = batadv_arp_get_type(bat_priv, skb, hdr_size);
1144: /* If the node gets an ARP_REQUEST it has to send a DHT_GET unicast
1145: * message to the selected DHT candidates
1146: */
1147: if (type != ARPOP_REQUEST)
1148: goto out;
1149:
1150: batadv_dbg_arp(bat_priv, skb, hdr_size, "Parsing outgoing ARP REQUEST");
1151:
1152: ip_src = batadv_arp_ip_src(skb, hdr_size);
1153: hw_src = batadv_arp_hw_src(skb, hdr_size);
1154: ip_dst = batadv_arp_ip_dst(skb, hdr_size);
1155:
1156: batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
1157:
1158: dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
1159: if (dat_entry) {
1160: /* If the ARP request is destined for a local client the local
1161: * client will answer itself. DAT would only generate a
1162: * duplicate packet.
1163: *
1164: * Moreover, if the mesh-interface is enslaved into a bridge, an
1165: * additional DAT answer may trigger kernel warnings about
1166: * a packet coming from the wrong port.
1167: */
1168: if (batadv_is_my_client(bat_priv, dat_entry->mac_addr, vid)) {
1169: ret = true;
1170: goto out;
1171: }
1172:
1173: /* If BLA is enabled, only send ARP replies if we have claimed
1174: * the destination for the ARP request or if no one else of
1175: * the backbone gws belonging to our backbone has claimed the
1176: * destination.
1177: */
1178: if (!batadv_bla_check_claim(bat_priv,
1179: dat_entry->mac_addr, vid)) {
1180: batadv_dbg(BATADV_DBG_DAT, bat_priv,
1181: "Device %pM claimed by another backbone gw. Don't send ARP reply!",
1182: dat_entry->mac_addr);
1183: ret = true;
1184: goto out;
1185: }
1186:
1187: skb_new = batadv_dat_arp_create_reply(bat_priv, ip_dst, ip_src,
1188: dat_entry->mac_addr,
1189: hw_src, vid);
1190: if (!skb_new)
1191: goto out;
1192:
1193: skb_new->protocol = eth_type_trans(skb_new, mesh_iface);
1194:
1195: batadv_inc_counter(bat_priv, BATADV_CNT_RX);
1196: batadv_add_counter(bat_priv, BATADV_CNT_RX_BYTES,
1197: skb->len + ETH_HLEN + hdr_size);
1198:
1199: netif_rx(skb_new);
1200: batadv_dbg(BATADV_DBG_DAT, bat_priv, "ARP request replied locally\n");
1201: ret = true;
1202: } else {
1203: /* Send the request to the DHT */
1204: ret = batadv_dat_forward_data(bat_priv, skb, ip_dst, vid,
1205: BATADV_P_DAT_DHT_GET);
1206: }
1207: out:
1208: batadv_dat_entry_put(dat_entry);
1209: return ret;
1210: }
SourceFile:net/batman-adv/distributed-arp-table.c]
|
| 36/2 |
2026/07/05 10:16 |
llm |
expert |
0m
Model:
Tokens:
input: 35194
output: 51
thoughts: 98
Thoughts: **ARP Snooping Optimization - A Local Perspective**
Okay, so I'm looking at `batadv_dat_snoop_outgoing_arp_request`, and I see it's adding `ip_src` and `hw_src` to the snooping table. My initial thought is, "Hmm, that's likely redundant in a lot of cases." The source IP is almost always going to be the local machine's IP, right? So, this function's going to spend its time updating the *same* entry in the snooping table repeatedly. It's not going to generate an explosion of entries, because the source IP doesn't usually change in this scenario. That makes sense, in terms of not flooding memory with repeated local requests.
But then, I need to zoom out a bit here. Now, I have to be asking about the other function in this process. What about `batadv_dat_snoop_incoming_arp_request`? Does that have similar considerations? That's probably the more important aspect, as those can come from many sources and potentially be more of a concern. I should probably be digging into that next to see how that's handled.
|
| 37/2 |
2026/07/05 10:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c Name:batadv_dat_snoop_incoming_arp_request]
Results: map[SourceCode:1212: /**
1213: * batadv_dat_snoop_incoming_arp_request() - snoop the ARP request and try to
1214: * answer using the local DAT storage
1215: * @bat_priv: the bat priv with all the mesh interface information
1216: * @skb: packet to check
1217: * @hdr_size: size of the encapsulation header
1218: *
1219: * Return: true if the request has been answered, false otherwise.
1220: */
1221: bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
1222: struct sk_buff *skb, int hdr_size)
1223: {
1224: u16 type;
1225: __be32 ip_src, ip_dst;
1226: u8 *hw_src;
1227: struct sk_buff *skb_new;
1228: struct batadv_dat_entry *dat_entry = NULL;
1229: bool ret = false;
1230: unsigned short vid;
1231: int err;
1232:
1233: if (!atomic_read(&bat_priv->distributed_arp_table))
1234: goto out;
1235:
1236: vid = batadv_dat_get_vid(skb, &hdr_size);
1237:
1238: type = batadv_arp_get_type(bat_priv, skb, hdr_size);
1239: if (type != ARPOP_REQUEST)
1240: goto out;
1241:
1242: hw_src = batadv_arp_hw_src(skb, hdr_size);
1243: ip_src = batadv_arp_ip_src(skb, hdr_size);
1244: ip_dst = batadv_arp_ip_dst(skb, hdr_size);
1245:
1246: batadv_dbg_arp(bat_priv, skb, hdr_size, "Parsing incoming ARP REQUEST");
1247:
1248: batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
1249:
1250: dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
1251: if (!dat_entry)
1252: goto out;
1253:
1254: skb_new = batadv_dat_arp_create_reply(bat_priv, ip_dst, ip_src,
1255: dat_entry->mac_addr, hw_src, vid);
1256: if (!skb_new)
1257: goto out;
1258:
1259: /* To preserve backwards compatibility, the node has choose the outgoing
1260: * format based on the incoming request packet type. The assumption is
1261: * that a node not using the 4addr packet format doesn't support it.
1262: */
1263: if (hdr_size == sizeof(struct batadv_unicast_4addr_packet))
1264: err = batadv_send_skb_via_tt_4addr(bat_priv, skb_new,
1265: BATADV_P_DAT_CACHE_REPLY,
1266: NULL, vid);
1267: else
1268: err = batadv_send_skb_via_tt(bat_priv, skb_new, NULL, vid);
1269:
1270: if (err != NET_XMIT_DROP) {
1271: batadv_inc_counter(bat_priv, BATADV_CNT_DAT_CACHED_REPLY_TX);
1272: ret = true;
1273: }
1274: out:
1275: batadv_dat_entry_put(dat_entry);
1276: if (ret)
1277: kfree_skb(skb);
1278: return ret;
1279: }
SourceFile:net/batman-adv/distributed-arp-table.c]
|
| 38/2 |
2026/07/05 10:16 |
llm |
expert |
0m
Model:
Tokens:
input: 36768
output: 51
thoughts: 184
Thoughts: **My Analysis of the ARP Request Vulnerability**
Okay, so I'm looking at this code, and it's grabbing the `ip_src` and `hw_src` from incoming ARP requests. My initial thought is, "Well, that's interesting... potentially a security flaw." Anyone on the mesh network could easily spoof those fields in an ARP request. And each of those forged packets would create a new entry in the DAT hash table. That's immediately ringing alarm bells.
The next question, a critical one, is whether a local, non-privileged process can even *send* packets to the mesh network. I have to consider all the angles. If the attacker can open a raw socket on the mesh interface, like `wlan0`, then they could absolutely craft these packets. But raw sockets require `CAP_NET_RAW` capabilities, and those aren't typically given to just any process. Still, itβs a possible attack vector.
What if they're sending via `bat0`? Would that work? I need to think this through. Sending UDP packets with spoofed source IPs is unlikely to trigger the ARP requests unless it's a raw socket, and that loops back to the raw socket capability. It's becoming less likely that the UDP route will work as expected, but the other one is looking interesting.
Wait, hold on... What about `batadv_dat_snoop_outgoing_arp_reply`? That function is important, and I need to review that code closely to understand its role. I need to know how it plays into this potential exploit. That's the key.
|
| 39/2 |
2026/07/05 10:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c Name:batadv_dat_snoop_outgoing_arp_reply]
Results: map[SourceCode:1281: /**
1282: * batadv_dat_snoop_outgoing_arp_reply() - snoop the ARP reply and fill the DHT
1283: * @bat_priv: the bat priv with all the mesh interface information
1284: * @skb: packet to check
1285: */
1286: void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
1287: struct sk_buff *skb)
1288: {
1289: u16 type;
1290: __be32 ip_src, ip_dst;
1291: u8 *hw_src, *hw_dst;
1292: int hdr_size = 0;
1293: unsigned short vid;
1294:
1295: if (!atomic_read(&bat_priv->distributed_arp_table))
1296: return;
1297:
1298: vid = batadv_dat_get_vid(skb, &hdr_size);
1299:
1300: type = batadv_arp_get_type(bat_priv, skb, hdr_size);
1301: if (type != ARPOP_REPLY)
1302: return;
1303:
1304: batadv_dbg_arp(bat_priv, skb, hdr_size, "Parsing outgoing ARP REPLY");
1305:
1306: hw_src = batadv_arp_hw_src(skb, hdr_size);
1307: ip_src = batadv_arp_ip_src(skb, hdr_size);
1308: hw_dst = batadv_arp_hw_dst(skb, hdr_size);
1309: ip_dst = batadv_arp_ip_dst(skb, hdr_size);
1310:
1311: batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
1312: batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
1313:
1314: /* Send the ARP reply to the candidates for both the IP addresses that
1315: * the node obtained from the ARP reply
1316: */
1317: batadv_dat_forward_data(bat_priv, skb, ip_src, vid,
1318: BATADV_P_DAT_DHT_PUT);
1319: batadv_dat_forward_data(bat_priv, skb, ip_dst, vid,
1320: BATADV_P_DAT_DHT_PUT);
1321: }
SourceFile:net/batman-adv/distributed-arp-table.c]
|
| 40/2 |
2026/07/05 10:16 |
llm |
expert |
0m
Model:
Tokens:
input: 38107
output: 47
thoughts: 223
Thoughts: **My Analysis of ARP Reply Poisoning in BATMAN-Adv**
Okay, so I'm looking at how ARP replies are handled in this BATMAN-Adv context. My initial thought is that it's adding *both* `ip_src` and `ip_dst` to something, presumably a data structure. But then I immediately ask myself, can a *local* process, without special privileges, somehow trick the kernel into sending ARP replies with an IP destination of its choosing?
The answer, I quickly realize, hinges on how ARP requests are processed. And yes, the kernel *will* send an ARP reply in response to an ARP request. Crucially, the destination IP in that reply is simply the source IP from the request itself. This is the crucial point.
So, if someone on the network, an attacker, can craft ARP requests with a forged `ip_src` address β a spoofed source β the kernel will dutifully send back an ARP reply that includes *that* spoofed IP as the destination. And that's where `batadv_dat_snoop_outgoing_arp_reply` gets involved, adding that (potentially malicious) IP to the DAT hash table.
But then I stop and think... surely, the attacker could simply skip this entirely, as they can directly craft ARP requests. They could use `batadv_dat_snoop_incoming_arp_request` to directly manipulate the hash table. This means an attacker *on the mesh network* can definitely trigger this behavior.
Now, going back to the local, unprivileged process β can *it* trigger this? The key here is whether it can spoof IPs. If it *can't* directly send ARP requests with arbitrary source IPs, it shouldn't be able to inject these arbitrary entries.
Wait a second... *DHCP*... that's potentially an avenue for exploitation. I need to investigate how DHCP interacts with this entire process.
|
| 41/2 |
2026/07/05 10:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c Name:batadv_dat_put_dhcp]
Results: map[SourceCode:1604: /**
1605: * batadv_dat_put_dhcp() - puts addresses from a DHCP packet into the DHT and
1606: * DAT cache
1607: * @bat_priv: the bat priv with all the mesh interface information
1608: * @chaddr: the DHCP client MAC address
1609: * @yiaddr: the DHCP client IP address
1610: * @hw_dst: the DHCP server MAC address
1611: * @ip_dst: the DHCP server IP address
1612: * @vid: VLAN identifier
1613: *
1614: * Adds given MAC/IP pairs to the local DAT cache and propagates them further
1615: * into the DHT.
1616: *
1617: * For the DHT propagation, client MAC + IP will appear as the ARP Reply
1618: * transmitter (and hw_dst/ip_dst as the target).
1619: */
1620: static void batadv_dat_put_dhcp(struct batadv_priv *bat_priv, u8 *chaddr,
1621: __be32 yiaddr, u8 *hw_dst, __be32 ip_dst,
1622: unsigned short vid)
1623: {
1624: struct sk_buff *skb;
1625:
1626: skb = batadv_dat_arp_create_reply(bat_priv, yiaddr, ip_dst, chaddr,
1627: hw_dst, vid);
1628: if (!skb)
1629: return;
1630:
1631: skb_set_network_header(skb, ETH_HLEN);
1632:
1633: batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid);
1634: batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
1635:
1636: batadv_dat_forward_data(bat_priv, skb, yiaddr, vid,
1637: BATADV_P_DAT_DHT_PUT);
1638: batadv_dat_forward_data(bat_priv, skb, ip_dst, vid,
1639: BATADV_P_DAT_DHT_PUT);
1640:
1641: consume_skb(skb);
1642:
1643: batadv_dbg(BATADV_DBG_DAT, bat_priv,
1644: "Snooped from outgoing DHCPACK (server address): %pI4, %pM (vid: %i)\n",
1645: &ip_dst, hw_dst, batadv_print_vid(vid));
1646: batadv_dbg(BATADV_DBG_DAT, bat_priv,
1647: "Snooped from outgoing DHCPACK (client address): %pI4, %pM (vid: %i)\n",
1648: &yiaddr, chaddr, batadv_print_vid(vid));
1649: }
SourceFile:net/batman-adv/distributed-arp-table.c]
|
| 42/2 |
2026/07/05 10:17 |
llm |
expert |
0m
Model:
Tokens:
input: 39656
output: 52
thoughts: 67
Thoughts: Okay, here's my take on those thoughts, framed as my own:
**Investigating DHCP Packet Manipulation Vulnerabilities**
Alright, this is interesting. It's dawning on me that, fundamentally, unprivileged processes can forge DHCP packets using UDP sockets. That opens up a potentially nasty vector. We know they can send a crafted DHCP packet β any old UDP packet on ports 67 or 68 β and just set the `yiaddr` and `chaddr` fields to whatever they want. Now, where could this be relevant... What's the impact of an unprivileged process manipulating those fields?
My immediate thought is to check `batadv_dat_snoop_outgoing_dhcp_ack` within the context of the BATMAN-adv protocol. Could a forged DHCP ACK, or any DHCP packet for that matter, with carefully crafted source and destination addresses, be used to disrupt, bypass, or manipulate the BATMAN-adv mesh network's DHCP snooping functionality, causing some denial of service, data injection, or even some form of man-in-the-middle attack? I'll need to dig into that specific function to see what data is being used, where the source of the packet is checked and how. The details are important.
|
| 43/2 |
2026/07/05 10:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c Name:batadv_dat_snoop_outgoing_dhcp_ack]
Results: map[SourceCode:1690: /**
1691: * batadv_dat_snoop_outgoing_dhcp_ack() - snoop DHCPACK and fill DAT with it
1692: * @bat_priv: the bat priv with all the mesh interface information
1693: * @skb: the packet to snoop
1694: * @proto: ethernet protocol hint (behind a potential vlan)
1695: * @vid: VLAN identifier
1696: *
1697: * This function first checks whether the given skb is a valid DHCPACK. If
1698: * so then its source MAC and IP as well as its DHCP Client Hardware Address
1699: * field and DHCP Your IP Address field are added to the local DAT cache and
1700: * propagated into the DHT.
1701: *
1702: * Caller needs to ensure that the skb mac and network headers are set
1703: * correctly.
1704: */
1705: void batadv_dat_snoop_outgoing_dhcp_ack(struct batadv_priv *bat_priv,
1706: struct sk_buff *skb,
1707: __be16 proto,
1708: unsigned short vid)
1709: {
1710: u8 chaddr[BATADV_DHCP_CHADDR_LEN];
1711: __be32 ip_src, yiaddr;
1712:
1713: if (!atomic_read(&bat_priv->distributed_arp_table))
1714: return;
1715:
1716: if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr))
1717: return;
1718:
1719: batadv_dat_put_dhcp(bat_priv, chaddr, yiaddr, eth_hdr(skb)->h_source,
1720: ip_src, vid);
1721: }
SourceFile:net/batman-adv/distributed-arp-table.c]
|
| 44/2 |
2026/07/05 10:17 |
llm |
expert |
0m
Model:
Tokens:
input: 40611
output: 49
thoughts: 25
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing the DHCP ACK Validation**
Okay, so I'm digging into this `batadv_dat_check_dhcp_ack` function. The name itself is pretty clear β it's designed to validate a DHCP ACK message. That's the core function, the one that tells the system if the offer has been received, and it's what determines the success of this part of the process. I need to understand what constitutes "valid" in this context. What specific checks are being done? Are we looking for particular flags, option presence, or specific address assignments? Is there any specific interaction with other data? The efficiency of these validation checks is important, as it directly impacts DHCP throughput and network responsiveness. Let's see how they are structured.
|
| 45/2 |
2026/07/05 10:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c Name:batadv_dat_check_dhcp_ack]
Results: map[SourceCode:1651: /**
1652: * batadv_dat_check_dhcp_ack() - examine packet for valid DHCP message
1653: * @skb: the packet to check
1654: * @proto: ethernet protocol hint (behind a potential vlan)
1655: * @ip_src: a buffer to store the IPv4 source address in
1656: * @chaddr: a buffer to store the DHCP Client Hardware Address in
1657: * @yiaddr: a buffer to store the DHCP Your IP Address in
1658: *
1659: * Checks whether the given skb is a valid DHCPACK. And if so, stores the
1660: * IPv4 server source address (ip_src), client MAC address (chaddr) and client
1661: * IPv4 address (yiaddr) in the provided buffers.
1662: *
1663: * Caller needs to ensure that the skb network header is set correctly.
1664: *
1665: * Return: True if the skb is a valid DHCPACK. False otherwise.
1666: */
1667: static bool
1668: batadv_dat_check_dhcp_ack(struct sk_buff *skb, __be16 proto, __be32 *ip_src,
1669: u8 *chaddr, __be32 *yiaddr)
1670: {
1671: int type;
1672:
1673: type = batadv_dat_check_dhcp(skb, proto, ip_src);
1674: if (type != BATADV_BOOTREPLY)
1675: return false;
1676:
1677: type = batadv_dat_get_dhcp_message_type(skb);
1678: if (type != BATADV_DHCPACK)
1679: return false;
1680:
1681: if (!batadv_dat_dhcp_get_yiaddr(skb, yiaddr))
1682: return false;
1683:
1684: if (!batadv_dat_get_dhcp_chaddr(skb, chaddr))
1685: return false;
1686:
1687: return true;
1688: }
SourceFile:net/batman-adv/distributed-arp-table.c]
|
| 46/2 |
2026/07/05 10:17 |
llm |
expert |
0m
Model:
Tokens:
input: 41518
output: 47
thoughts: 78
Thoughts: Okay, here's my take on those thoughts, framed as my own internal analysis:
**Analyzing a Potential DHCP Vulnerability**
Alright, let's break this down. A local, unprivileged process somehow sends a UDP packet to port 68, the DHCP client port, from port 67, which is the DHCP server port. My initial reaction is that's... suspicious. But hold on, the act of sending from port 67 immediately triggers a flag. We're talking about a privileged port here β anything below 1024 typically requires root privileges. That's a huge red flag because an unprivileged process shouldn't be able to do that.
Now, my mind shifts to the core of this: does `batadv_dat_check_dhcp` actually vet the source port? This is the critical question. If the function *doesn't* validate the source port of the DHCP traffic, then this becomes a much more serious issue. The ability to spoof the source port in this context opens the door for potential exploitation. I need to dig deeper into the code to determine the specifics of that check, because this feels like a rabbit hole.
|
| 47/2 |
2026/07/05 10:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c Name:batadv_dat_check_dhcp]
Results: map[SourceCode:1450: /**
1451: * batadv_dat_check_dhcp() - examine packet for valid DHCP message
1452: * @skb: the packet to check
1453: * @proto: ethernet protocol hint (behind a potential vlan)
1454: * @ip_src: a buffer to store the IPv4 source address in
1455: *
1456: * Checks whether the given skb is a valid DHCP packet. And if so, stores the
1457: * IPv4 source address in the provided buffer.
1458: *
1459: * Caller needs to ensure that the skb network header is set correctly.
1460: *
1461: * Return: If skb is a valid DHCP packet, then returns its op code
1462: * (e.g. BOOTREPLY vs. BOOTREQUEST). Otherwise returns -EINVAL.
1463: */
1464: static int
1465: batadv_dat_check_dhcp(struct sk_buff *skb, __be16 proto, __be32 *ip_src)
1466: {
1467: __be32 *magic, _magic;
1468: unsigned int offset;
1469: struct {
1470: __u8 op;
1471: __u8 htype;
1472: __u8 hlen;
1473: __u8 hops;
1474: } *dhcp_h, _dhcp_h;
1475:
1476: if (proto != htons(ETH_P_IP))
1477: return -EINVAL;
1478:
1479: if (!batadv_dat_check_dhcp_ipudp(skb, ip_src))
1480: return -EINVAL;
1481:
1482: offset = skb_transport_offset(skb) + sizeof(struct udphdr);
1483: if (skb->len < offset + sizeof(struct batadv_dhcp_packet))
1484: return -EINVAL;
1485:
1486: dhcp_h = skb_header_pointer(skb, offset, sizeof(_dhcp_h), &_dhcp_h);
1487: if (!dhcp_h || dhcp_h->htype != BATADV_HTYPE_ETHERNET ||
1488: dhcp_h->hlen != ETH_ALEN)
1489: return -EINVAL;
1490:
1491: offset += offsetof(struct batadv_dhcp_packet, magic);
1492:
1493: magic = skb_header_pointer(skb, offset, sizeof(_magic), &_magic);
1494: if (!magic || get_unaligned(magic) != htonl(BATADV_DHCP_MAGIC))
1495: return -EINVAL;
1496:
1497: return dhcp_h->op;
1498: }
SourceFile:net/batman-adv/distributed-arp-table.c]
|
| 48/2 |
2026/07/05 10:17 |
llm |
expert |
0m
Model:
Tokens:
input: 42735
output: 50
thoughts: 18
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Diving into `batadv_dat_check_dhcp_ipudp`**
Alright, time to dig in. Let's see what's happening with `batadv_dat_check_dhcp_ipudp`. I need to understand its role within the broader data flow. Specifically, it seems to be involved with DHCP and IPUDP. Considering the name, I'm already anticipating we're dealing with checking some aspect of DHCP-assigned addresses or perhaps IPUDP-related configurations. I'll need to refresh my understanding of how this function fits into the overall BATMAN-adv architecture. Is it validation, filtering, or possibly a detection mechanism for IP conflicts or misconfigurations within the mesh? This'll be interesting.
|
| 49/2 |
2026/07/05 10:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/distributed-arp-table.c Name:batadv_dat_check_dhcp_ipudp]
Results: map[SourceCode:1413: /**
1414: * batadv_dat_check_dhcp_ipudp() - check skb for IP+UDP headers valid for DHCP
1415: * @skb: the packet to check
1416: * @ip_src: a buffer to store the IPv4 source address in
1417: *
1418: * Checks whether the given skb has an IP and UDP header valid for a DHCP
1419: * message from a DHCP server. And if so, stores the IPv4 source address in
1420: * the provided buffer.
1421: *
1422: * Return: True if valid, false otherwise.
1423: */
1424: static bool
1425: batadv_dat_check_dhcp_ipudp(struct sk_buff *skb, __be32 *ip_src)
1426: {
1427: unsigned int offset = skb_network_offset(skb);
1428: struct udphdr *udphdr, _udphdr;
1429: struct iphdr *iphdr, _iphdr;
1430:
1431: iphdr = skb_header_pointer(skb, offset, sizeof(_iphdr), &_iphdr);
1432: if (!iphdr || iphdr->version != 4 || iphdr->ihl * 4 < sizeof(_iphdr))
1433: return false;
1434:
1435: if (iphdr->protocol != IPPROTO_UDP)
1436: return false;
1437:
1438: offset += iphdr->ihl * 4;
1439: skb_set_transport_header(skb, offset);
1440:
1441: udphdr = skb_header_pointer(skb, offset, sizeof(_udphdr), &_udphdr);
1442: if (!udphdr || udphdr->source != htons(67))
1443: return false;
1444:
1445: *ip_src = get_unaligned(&iphdr->saddr);
1446:
1447: return true;
1448: }
SourceFile:net/batman-adv/distributed-arp-table.c]
|
| 50/2 |
2026/07/05 10:17 |
llm |
expert |
0m
Model:
Tokens:
input: 43597
output: 33
thoughts: 905
Thoughts: **Analyzing the UDP and ARP Interaction with batman-adv**
Okay, so I'm thinking through this UDP and ARP interaction. First, the code checks if the UDP source port is not 67. If it *is* 67, then it's a BOOTP/DHCP client or server, which I'm ignoring for now. Since 67 is a privileged port, a standard unprivileged process can't just bind to it or send directly from it. But what about ARP? An unprivileged process *can't* just send raw ARP requests because that needs `CAP_NET_RAW` capabilities.
Then it hits me: if an unprivileged process sends a UDP packet to an arbitrary IP *on the local subnet*, the kernel will generate an ARP request to resolve that IP! Specifically, the IPv4 stack in the kernel will generate the ARP request. That's the key. The source IP will be the interface's IP, and the destination IP will be that arbitrary IP I specified.
Now I need to understand how `batadv_dat_snoop_outgoing_arp_request` handles this. I can see the code fetches IP and hardware addresses from the ARP packet, and then uses these to update the DAT hash table. However, since the source IP is always the interface's IP, that's just a repeated update, not adding new entries. The code specifically *doesn't* add the destination IP to the DAT table during outgoing requests. So, an unprivileged process can't directly populate the DAT table with arbitrary entries simply by sending UDP packets.
Next up, what about `batadv_dat_snoop_outgoing_arp_reply`? Ah, if the kernel *receives* an ARP request and then *sends* an ARP reply, it does add both the source and destination IP/MAC address pairs. But the kernel only sends a reply if it *receives* a request. So, could an attacker on the network spoof the source IP of an ARP request? If so, the kernel sends a reply with the spoofed address as the destination, which then gets added to the DAT table via this handler. But, that's almost pointless because an attacker can just directly send ARP requests, which will be snooped by the `batadv_dat_snoop_incoming_arp_request` handler, which adds both source IP/MAC address pairs anyway. So an attacker on the network can easily trigger this situation and populate the DAT table.
The critical question now is, can a *local* unprivileged process trigger it? No, a local process can't spoof IPs and can't send raw ARP packets. But, what if it uses user namespaces? If I create a new network namespace, can I somehow work around these restrictions? Ah, if I use user namespaces, I can create a veth pair, send ARP packets via it.
The veth pair isn't a batman-adv interface, but wait, batman-adv *can* operate on veth interfaces! If the unprivileged user creates a user namespace, and within that network namespace they can then create a veth pair. Crucially, they have `CAP_NET_ADMIN` *within that new network namespace*, so they can then create a batman-adv interface (`bat0`) on top of the veth interface. Then, this local user *could* possibly trigger the ARP-based DAT table population.
Now, I need to check whether batman-adv supports creation of interfaces inside non-initial network namespaces. This could be a significant vulnerability.
|
| 51/2 |
2026/07/05 10:17 |
tool |
grepper |
0m
Args: map[Expression:batadv_netlink_family PathPrefix:net/batman-adv]
Results: map[Output:net/batman-adv/bat_algo.c=160=static int batadv_algo_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
--
net/batman-adv/bat_algo.c-164-
net/batman-adv/bat_algo.c:165: hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
net/batman-adv/bat_algo.c-166- NLM_F_MULTI, BATADV_CMD_GET_ROUTING_ALGOS);
--
net/batman-adv/bat_iv_ogm.c=1812=batadv_iv_ogm_orig_dump_subentry(struct sk_buff *msg, u32 portid, u32 seq,
--
net/batman-adv/bat_iv_ogm.c-1831-
net/batman-adv/bat_iv_ogm.c:1832: hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
net/batman-adv/bat_iv_ogm.c-1833- NLM_F_MULTI, BATADV_CMD_GET_ORIGINATORS);
--
net/batman-adv/bat_iv_ogm.c=2054=batadv_iv_ogm_neigh_dump_neigh(struct sk_buff *msg, u32 portid, u32 seq,
--
net/batman-adv/bat_iv_ogm.c-2061-
net/batman-adv/bat_iv_ogm.c:2062: hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
net/batman-adv/bat_iv_ogm.c-2063- NLM_F_MULTI, BATADV_CMD_GET_NEIGHBORS);
--
net/batman-adv/bat_iv_ogm.c=2396=static int batadv_iv_gw_dump_entry(struct sk_buff *msg, u32 portid,
--
net/batman-adv/bat_iv_ogm.c-2417- hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
net/batman-adv/bat_iv_ogm.c:2418: &batadv_netlink_family, NLM_F_MULTI,
net/batman-adv/bat_iv_ogm.c-2419- BATADV_CMD_GET_GATEWAYS);
--
net/batman-adv/bat_v.c=128=batadv_v_neigh_dump_neigh(struct sk_buff *msg, u32 portid, u32 seq,
--
net/batman-adv/bat_v.c-138-
net/batman-adv/bat_v.c:139: hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, NLM_F_MULTI,
net/batman-adv/bat_v.c-140- BATADV_CMD_GET_NEIGHBORS);
--
net/batman-adv/bat_v.c=264=batadv_v_orig_dump_subentry(struct sk_buff *msg, u32 portid, u32 seq,
--
net/batman-adv/bat_v.c-289-
net/batman-adv/bat_v.c:290: hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, NLM_F_MULTI,
net/batman-adv/bat_v.c-291- BATADV_CMD_GET_ORIGINATORS);
--
net/batman-adv/bat_v.c=653=static int batadv_v_gw_dump_entry(struct sk_buff *msg, u32 portid,
--
net/batman-adv/bat_v.c-674- hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
net/batman-adv/bat_v.c:675: &batadv_netlink_family, NLM_F_MULTI,
net/batman-adv/bat_v.c-676- BATADV_CMD_GET_GATEWAYS);
--
net/batman-adv/bridge_loop_avoidance.c=2127=batadv_bla_claim_dump_entry(struct sk_buff *msg, u32 portid,
--
net/batman-adv/bridge_loop_avoidance.c-2139- hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
net/batman-adv/bridge_loop_avoidance.c:2140: &batadv_netlink_family, NLM_F_MULTI,
net/batman-adv/bridge_loop_avoidance.c-2141- BATADV_CMD_GET_BLA_CLAIM);
--
net/batman-adv/bridge_loop_avoidance.c=2290=batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid,
--
net/batman-adv/bridge_loop_avoidance.c-2302- hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
net/batman-adv/bridge_loop_avoidance.c:2303: &batadv_netlink_family, NLM_F_MULTI,
net/batman-adv/bridge_loop_avoidance.c-2304- BATADV_CMD_GET_BLA_BACKBONE);
--
net/batman-adv/distributed-arp-table.c=853=batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid,
--
net/batman-adv/distributed-arp-table.c-860- hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
net/batman-adv/distributed-arp-table.c:861: &batadv_netlink_family, NLM_F_MULTI,
net/batman-adv/distributed-arp-table.c-862- BATADV_CMD_GET_DAT_CACHE);
--
net/batman-adv/multicast.c=1976=batadv_mcast_flags_dump_entry(struct sk_buff *msg, u32 portid,
--
net/batman-adv/multicast.c-1982- hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
net/batman-adv/multicast.c:1983: &batadv_netlink_family, NLM_F_MULTI,
net/batman-adv/multicast.c-1984- BATADV_CMD_GET_MCAST_FLAGS);
--
net/batman-adv/netlink.c-50-
net/batman-adv/netlink.c:51:struct genl_family batadv_netlink_family;
net/batman-adv/netlink.c-52-
--
net/batman-adv/netlink.c=222=static int batadv_netlink_mesh_fill(struct sk_buff *msg,
--
net/batman-adv/netlink.c-231-
net/batman-adv/netlink.c:232: hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, flags, cmd);
net/batman-adv/netlink.c-233- if (!hdr)
--
net/batman-adv/netlink.c=368=static int batadv_netlink_notify_mesh(struct batadv_priv *bat_priv)
--
net/batman-adv/netlink.c-383-
net/batman-adv/netlink.c:384: genlmsg_multicast_netns(&batadv_netlink_family,
net/batman-adv/netlink.c-385- dev_net(bat_priv->mesh_iface), msg, 0,
--
net/batman-adv/netlink.c=627=int batadv_netlink_tpmeter_notify(struct batadv_priv *bat_priv, const u8 *dst,
--
net/batman-adv/netlink.c-638-
net/batman-adv/netlink.c:639: hdr = genlmsg_put(msg, 0, 0, &batadv_netlink_family, 0,
net/batman-adv/netlink.c-640- BATADV_CMD_TP_METER);
--
net/batman-adv/netlink.c-663-
net/batman-adv/netlink.c:664: genlmsg_multicast_netns(&batadv_netlink_family,
net/batman-adv/netlink.c-665- dev_net(bat_priv->mesh_iface), msg, 0,
--
net/batman-adv/netlink.c=687=batadv_netlink_tp_meter_start(struct sk_buff *skb, struct genl_info *info)
--
net/batman-adv/netlink.c-713- msg_head = genlmsg_put(msg, info->snd_portid, info->snd_seq,
net/batman-adv/netlink.c:714: &batadv_netlink_family, 0,
net/batman-adv/netlink.c-715- BATADV_CMD_TP_METER);
--
net/batman-adv/netlink.c=773=static int batadv_netlink_hardif_fill(struct sk_buff *msg,
--
net/batman-adv/netlink.c-782-
net/batman-adv/netlink.c:783: hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, flags, cmd);
net/batman-adv/netlink.c-784- if (!hdr)
--
net/batman-adv/netlink.c=840=static int batadv_netlink_notify_hardif(struct batadv_priv *bat_priv,
--
net/batman-adv/netlink.c-856-
net/batman-adv/netlink.c:857: genlmsg_multicast_netns(&batadv_netlink_family,
net/batman-adv/netlink.c-858- dev_net(bat_priv->mesh_iface), msg, 0,
--
net/batman-adv/netlink.c=999=static int batadv_netlink_vlan_fill(struct sk_buff *msg,
--
net/batman-adv/netlink.c-1006-
net/batman-adv/netlink.c:1007: hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, flags, cmd);
net/batman-adv/netlink.c-1008- if (!hdr)
--
net/batman-adv/netlink.c=1041=static int batadv_netlink_notify_vlan(struct batadv_priv *bat_priv,
--
net/batman-adv/netlink.c-1057-
net/batman-adv/netlink.c:1058: genlmsg_multicast_netns(&batadv_netlink_family,
net/batman-adv/netlink.c-1059- dev_net(bat_priv->mesh_iface), msg, 0,
--
net/batman-adv/netlink.c=1400=static const struct genl_small_ops batadv_netlink_ops[] = {
--
net/batman-adv/netlink.c-1523-
net/batman-adv/netlink.c:1524:struct genl_family batadv_netlink_family __ro_after_init = {
net/batman-adv/netlink.c-1525- .hdrsize = 0,
--
net/batman-adv/netlink.c=1544=void __init batadv_netlink_register(void)
--
net/batman-adv/netlink.c-1547-
net/batman-adv/netlink.c:1548: ret = genl_register_family(&batadv_netlink_family);
net/batman-adv/netlink.c-1549- if (ret)
--
net/batman-adv/netlink.c=1556=void batadv_netlink_unregister(void)
net/batman-adv/netlink.c-1557-{
net/batman-adv/netlink.c:1558: genl_unregister_family(&batadv_netlink_family);
net/batman-adv/netlink.c-1559-}
--
net/batman-adv/netlink.h=22=int batadv_netlink_tpmeter_notify(struct batadv_priv *bat_priv, const u8 *dst,
--
net/batman-adv/netlink.h-25-
net/batman-adv/netlink.h:26:extern struct genl_family batadv_netlink_family;
net/batman-adv/netlink.h-27-
--
net/batman-adv/translation-table.c=1039=batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid,
--
net/batman-adv/translation-table.c-1061- hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
net/batman-adv/translation-table.c:1062: &batadv_netlink_family, NLM_F_MULTI,
net/batman-adv/translation-table.c-1063- BATADV_CMD_GET_TRANSTABLE_LOCAL);
--
net/batman-adv/translation-table.c=1763=batadv_tt_global_dump_subentry(struct sk_buff *msg, u32 portid, u32 seq,
--
net/batman-adv/translation-table.c-1782-
net/batman-adv/translation-table.c:1783: hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
net/batman-adv/translation-table.c-1784- NLM_F_MULTI,
]
|
| 52/2 |
2026/07/05 10:17 |
llm |
expert |
0m
Model:
Tokens:
input: 49067
output: 32
thoughts: 116
Thoughts: **Investigating Batman-Adv Interface Creation**
Okay, so I'm thinking about the `batadv_netlink_family` registration β that's global, right? It's making me wonder about the implications for unprivileged users and the ability to spin up batman-adv interfaces. My intuition is screaming "no way," but I need to be certain.
Typically, crafting a netdevice of a particular type demands `CAP_NET_ADMIN` privileges, especially within a specific network namespace. Now, a user can establish a fresh network namespace, effectively granting themselves `CAP_NET_ADMIN` privileges *within that namespace*. This raises the critical question: can they leverage this newfound power to conjure a `batadv` interface?
To nail this down, I'm going to dig into the `batadv_link_ops` structure. Specifically, I need to check if it implements a `setup` function and then determine if that setup function is permitted to run within a non-initial network namespace. That's the key to understanding the security boundary here. This is where the kernel code is going to be my best friend.
|
| 53/2 |
2026/07/05 10:17 |
tool |
grepper |
0m
Args: map[Expression:batadv_link_ops PathPrefix:net/batman-adv]
Results: map[Output:net/batman-adv/main.c=89=static int __init batadv_init(void)
--
net/batman-adv/main.c-110- register_netdevice_notifier(&batadv_hard_if_notifier);
net/batman-adv/main.c:111: rtnl_link_register(&batadv_link_ops);
net/batman-adv/main.c-112- batadv_netlink_register();
--
net/batman-adv/main.c=125=static void __exit batadv_exit(void)
--
net/batman-adv/main.c-127- batadv_netlink_unregister();
net/batman-adv/main.c:128: rtnl_link_unregister(&batadv_link_ops);
net/batman-adv/main.c-129- unregister_netdevice_notifier(&batadv_hard_if_notifier);
--
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);
]
|
| 54/2 |
2026/07/05 10:17 |
llm |
expert |
0m
Model:
Tokens:
input: 49951
output: 42
thoughts: 14
Thoughts: Here's my take on those thoughts, tailored for an expert audience:
**Diving into `batadv_link_ops`**
Okay, time to get my hands dirty. Let's see what's actually going on under the hood with that `batadv_link_ops` definition. I need to refresh my memory on the specifics of the Bat-Adv link operations. I'm assuming there's some interesting stuff in there, potentially related to mesh link management, neighbor discovery, or packet forwarding logic. I'm especially keen to see how they handle link quality estimation and adaptation. I'm going to pull up the definition and quickly scan for the key function pointers and data structures. It's time to understand how this is used within the overall Bat-Adv architecture. Let's see if there are any gotchas in there.
|
| 55/2 |
2026/07/05 10:17 |
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]
|
| 56/2 |
2026/07/05 10:17 |
llm |
expert |
0m
Model:
Tokens:
input: 50415
output: 34
thoughts: 119
Thoughts: **Investigating Batadv Interface Creation**
Okay, so `batadv_link_ops` is registered, which means `ip link add type batadv` should work because it's hooked into `rtnl_link_register`. That's good. And if someone has `CAP_NET_ADMIN` within a network namespace, they should be able to instantiate a `batadv` interface. But hang on... `rtnl_link_register` registers globally, doesn't it? So, the implication is that this registration should be usable in *any* netns. Unless, of course, there's some kind of `netns_refund` mechanism or something similar that blocks it. I need to be sure. Let me go check if `batadv` is, in fact, restricted to the `init_net` in any way. I have to be absolutely certain of this.
|
| 57/2 |
2026/07/05 10:17 |
tool |
grepper |
0m
Args: map[Expression:batadv_meshif_newlink PathPrefix:net/batman-adv]
Results: map[Output:net/batman-adv/mesh-interface.c=1034=static int batadv_meshif_validate(struct nlattr *tb[], struct nlattr *data[],
--
net/batman-adv/mesh-interface.c-1051-/**
net/batman-adv/mesh-interface.c:1052: * batadv_meshif_newlink() - pre-initialize and register new batadv link
net/batman-adv/mesh-interface.c-1053- * @dev: network device to register
--
net/batman-adv/mesh-interface.c-1058- */
net/batman-adv/mesh-interface.c:1059:static int batadv_meshif_newlink(struct net_device *dev,
net/batman-adv/mesh-interface.c-1060- struct rtnl_newlink_params *params,
--
net/batman-adv/mesh-interface.c=1124=struct rtnl_link_ops batadv_link_ops __read_mostly = {
--
net/batman-adv/mesh-interface.c-1130- .validate = batadv_meshif_validate,
net/batman-adv/mesh-interface.c:1131: .newlink = batadv_meshif_newlink,
net/batman-adv/mesh-interface.c-1132- .dellink = batadv_meshif_destroy_netlink,
]
|
| 58/2 |
2026/07/05 10:17 |
llm |
expert |
0m
Model:
Tokens:
input: 51131
output: 44
thoughts: 15
Thoughts: Okay, let's dive into this `batadv_meshif_newlink` function. I need to understand what's happening here. The name itself suggests we're dealing with the creation of a new mesh interface within BATMAN Advanced. I immediately need to consider the context: how does this fit into the overall architecture, what are the upstream and downstream implications? What's the purpose of this interface, and what data structures are involved? I'm anticipating that this function will initialize the interface, likely allocate some memory for its representation, configure some basic parameters, and then perhaps register it within the relevant network stack subsystems. I will look for calls to functions dealing with netlink, the kernel's mechanism for communicating with user space, as that is what I expect will be used here. Also, I'll be looking for any synchronization primitives to ensure thread safety as BATMAN is a distributed protocol and must deal with concurrent access. I expect there is some management of interface lifecycles that I need to be aware of. I need to see what happens on error conditions and how it cleans up resources. This is where I start. Let's dig in.
|
| 59/2 |
2026/07/05 10:17 |
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]
|
| 60/2 |
2026/07/05 10:17 |
llm |
expert |
0m
Model:
Tokens:
input: 51861
output: 42
thoughts: 141
Thoughts: **My Initial Assessment of the `batadv` Vulnerability**
Okay, so there's no safeguard for `init_net` in this setup, meaning I can spin it up within any network namespace. Interesting. But then my mind races: can a non-privileged user even attach a physical interface to `batadv`? I need to quickly refresh my memory on the attachment methods; it's either through the `batadv` netlink interface or sysfs, right? I should probably dig into `batadv_netlink_hardif_fill` to understand how that hard interface attachment actually works.
Actually, scratch that β I'm overthinking it. Hold on, if all I need to do is *create* a `batadv` interface, wouldn't that suffice for me to push packets through it? No need for the messy attachment. But wait, a crucial question arises: if no physical interfaces are actually tethered to the `batadv` interface, will `batadv_interface_tx` even process the packets? That's the key point to nail down.
|
| 61/2 |
2026/07/05 10:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/mesh-interface.c Name:batadv_interface_tx]
Results: map[SourceCode: 167: static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
168: struct net_device *mesh_iface)
169: {
170: struct ethhdr *ethhdr;
171: struct batadv_priv *bat_priv = netdev_priv(mesh_iface);
172: struct batadv_hard_iface *primary_if = NULL;
173: struct batadv_bcast_packet *bcast_packet;
174: static const u8 stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00,
175: 0x00, 0x00};
176: static const u8 ectp_addr[ETH_ALEN] = {0xCF, 0x00, 0x00, 0x00,
177: 0x00, 0x00};
178: enum batadv_dhcp_recipient dhcp_rcp = BATADV_DHCP_NO;
179: u8 *dst_hint = NULL, chaddr[ETH_ALEN];
180: struct vlan_ethhdr *vhdr;
181: unsigned int header_len = 0;
182: int data_len = skb->len, ret;
183: unsigned long brd_delay = 0;
184: bool do_bcast = false, client_added;
185: unsigned short vid;
186: u32 seqno;
187: int gw_mode;
188: enum batadv_forw_mode forw_mode = BATADV_FORW_BCAST;
189: int mcast_is_routable = 0;
190: int network_offset = ETH_HLEN;
191: __be16 proto;
192:
193: if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
194: goto dropped;
195:
196: /* reset control block to avoid left overs from previous users */
197: memset(skb->cb, 0, sizeof(struct batadv_skb_cb));
198:
199: netif_trans_update(mesh_iface);
200: vid = batadv_get_vid(skb, 0);
201:
202: skb_reset_mac_header(skb);
203: ethhdr = eth_hdr(skb);
204:
205: proto = ethhdr->h_proto;
206:
207: switch (ntohs(proto)) {
208: case ETH_P_8021Q:
209: if (!pskb_may_pull(skb, sizeof(*vhdr)))
210: goto dropped;
211: vhdr = vlan_eth_hdr(skb);
212: proto = vhdr->h_vlan_encapsulated_proto;
213:
214: /* drop batman-in-batman packets to prevent loops */
215: if (proto != htons(ETH_P_BATMAN)) {
216: network_offset += VLAN_HLEN;
217: break;
218: }
219:
220: fallthrough;
221: case ETH_P_BATMAN:
222: goto dropped;
223: }
224:
225: skb_set_network_header(skb, network_offset);
226:
227: if (batadv_bla_tx(bat_priv, skb, vid))
228: goto dropped;
229:
230: /* skb->data might have been reallocated by batadv_bla_tx() */
231: ethhdr = eth_hdr(skb);
232:
233: /* Register the client MAC in the transtable */
234: if (!is_multicast_ether_addr(ethhdr->h_source) &&
235: !batadv_bla_is_loopdetect_mac(ethhdr->h_source)) {
236: client_added = batadv_tt_local_add(mesh_iface, ethhdr->h_source,
237: vid, skb->skb_iif,
238: skb->mark);
239: if (!client_added)
240: goto dropped;
241: }
242:
243: /* Snoop address candidates from DHCPACKs for early DAT filling */
244: batadv_dat_snoop_outgoing_dhcp_ack(bat_priv, skb, proto, vid);
245:
246: /* don't accept stp packets. STP does not help in meshes.
247: * better use the bridge loop avoidance ...
248: *
249: * The same goes for ECTP sent at least by some Cisco Switches,
250: * it might confuse the mesh when used with bridge loop avoidance.
251: */
252: if (batadv_compare_eth(ethhdr->h_dest, stp_addr))
253: goto dropped;
254:
255: if (batadv_compare_eth(ethhdr->h_dest, ectp_addr))
256: goto dropped;
257:
258: gw_mode = atomic_read(&bat_priv->gw.mode);
259: if (is_multicast_ether_addr(ethhdr->h_dest)) {
260: /* if gw mode is off, broadcast every packet */
261: if (gw_mode == BATADV_GW_MODE_OFF) {
262: do_bcast = true;
263: goto send;
264: }
265:
266: dhcp_rcp = batadv_gw_dhcp_recipient_get(skb, &header_len,
267: chaddr);
268: /* skb->data may have been modified by
269: * batadv_gw_dhcp_recipient_get()
270: */
271: ethhdr = eth_hdr(skb);
272: /* if gw_mode is on, broadcast any non-DHCP message.
273: * All the DHCP packets are going to be sent as unicast
274: */
275: if (dhcp_rcp == BATADV_DHCP_NO) {
276: do_bcast = true;
277: goto send;
278: }
279:
280: if (dhcp_rcp == BATADV_DHCP_TO_CLIENT)
281: dst_hint = chaddr;
282: else if ((gw_mode == BATADV_GW_MODE_SERVER) &&
283: (dhcp_rcp == BATADV_DHCP_TO_SERVER))
284: /* gateways should not forward any DHCP message if
285: * directed to a DHCP server
286: */
287: goto dropped;
288:
289: send:
290: if (do_bcast && !is_broadcast_ether_addr(ethhdr->h_dest)) {
291: forw_mode = batadv_mcast_forw_mode(bat_priv, skb, vid,
292: &mcast_is_routable);
293: switch (forw_mode) {
294: case BATADV_FORW_BCAST:
295: break;
296: case BATADV_FORW_UCASTS:
297: case BATADV_FORW_MCAST:
298: do_bcast = false;
299: break;
300: case BATADV_FORW_NONE:
301: fallthrough;
302: default:
303: goto dropped;
304: }
305: }
306: }
307:
308: batadv_skb_set_priority(skb, 0);
309:
310: /* ethernet packet should be broadcasted */
311: if (do_bcast) {
312: primary_if = batadv_primary_if_get_selected(bat_priv);
313: if (!primary_if)
314: goto dropped;
315:
316: /* in case of ARP request, we do not immediately broadcasti the
317: * packet, instead we first wait for DAT to try to retrieve the
318: * correct ARP entry
319: */
320: if (batadv_dat_snoop_outgoing_arp_request(bat_priv, skb))
321: brd_delay = msecs_to_jiffies(ARP_REQ_DELAY);
322:
323: if (batadv_skb_head_push(skb, sizeof(*bcast_packet)) < 0)
324: goto dropped;
325:
326: bcast_packet = (struct batadv_bcast_packet *)skb->data;
327: bcast_packet->version = BATADV_COMPAT_VERSION;
328: bcast_packet->ttl = BATADV_TTL - 1;
329:
330: /* batman packet type: broadcast */
331: bcast_packet->packet_type = BATADV_BCAST;
332: bcast_packet->reserved = 0;
333:
334: /* hw address of first interface is the orig mac because only
335: * this mac is known throughout the mesh
336: */
337: ether_addr_copy(bcast_packet->orig,
338: primary_if->net_dev->dev_addr);
339:
340: /* set broadcast sequence number */
341: seqno = atomic_inc_return(&bat_priv->bcast_seqno);
342: bcast_packet->seqno = htonl(seqno);
343:
344: batadv_send_bcast_packet(bat_priv, skb, brd_delay, true);
345: /* unicast packet */
346: } else {
347: /* DHCP packets going to a server will use the GW feature */
348: if (dhcp_rcp == BATADV_DHCP_TO_SERVER) {
349: ret = batadv_gw_out_of_range(bat_priv, skb);
350: if (ret)
351: goto dropped;
352: ret = batadv_send_skb_via_gw(bat_priv, skb, vid);
353: } else if (forw_mode == BATADV_FORW_UCASTS) {
354: ret = batadv_mcast_forw_send(bat_priv, skb, vid,
355: mcast_is_routable);
356: } else if (forw_mode == BATADV_FORW_MCAST) {
357: ret = batadv_mcast_forw_mcsend(bat_priv, skb);
358: } else {
359: if (batadv_dat_snoop_outgoing_arp_request(bat_priv,
360: skb))
361: goto dropped;
362:
363: batadv_dat_snoop_outgoing_arp_reply(bat_priv, skb);
364:
365: ret = batadv_send_skb_via_tt(bat_priv, skb, dst_hint,
366: vid);
367: }
368: if (ret != NET_XMIT_SUCCESS)
369: goto dropped_freed;
370: }
371:
372: batadv_inc_counter(bat_priv, BATADV_CNT_TX);
373: batadv_add_counter(bat_priv, BATADV_CNT_TX_BYTES, data_len);
374: goto end;
375:
376: dropped:
377: kfree_skb(skb);
378: dropped_freed:
379: batadv_inc_counter(bat_priv, BATADV_CNT_TX_DROPPED);
380: end:
381: batadv_hardif_put(primary_if);
382: return NETDEV_TX_OK;
383: }
SourceFile:net/batman-adv/mesh-interface.c]
|
| 62/2 |
2026/07/05 10:17 |
llm |
expert |
0m
Model:
Tokens:
input: 55866
output: 32
thoughts: 77
Thoughts: Here's my summary, as I delve into this code:
**Analyzing the `batadv_interface_tx` Check**
Okay, so I'm looking at `batadv_interface_tx` and I see this check: `atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE`. My immediate thought is, if there are no hard interfaces connected, the `mesh_state` is *probably* not going to be `BATADV_MESH_ACTIVE`. It would make sense for it to be in some other state until a mesh is actually...active. Now, I need to figure out *when* that `mesh_state` variable transitions to `BATADV_MESH_ACTIVE`. I need to trace back how and where this state is set, so I can understand the logic around it and whether this check is truly as intended. Gotta understand the lifecycle here.
|
| 63/2 |
2026/07/05 10:17 |
tool |
grepper |
0m
Args: map[Expression:BATADV_MESH_ACTIVE PathPrefix:net/batman-adv]
Results: map[Output:net/batman-adv/main.c=145=int batadv_mesh_init(struct net_device *mesh_iface)
--
net/batman-adv/main.c-221- atomic_set(&bat_priv->gw.reselect, 0);
net/batman-adv/main.c:222: atomic_set(&bat_priv->mesh_state, BATADV_MESH_ACTIVE);
net/batman-adv/main.c-223-
--
net/batman-adv/main.c=405=int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
--
net/batman-adv/main.c-442-
net/batman-adv/main.c:443: if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
net/batman-adv/main.c-444- goto err_free;
--
net/batman-adv/main.h=132=enum batadv_mesh_state {
--
net/batman-adv/main.h-135-
net/batman-adv/main.h:136: /** @BATADV_MESH_ACTIVE: interface is up and running */
net/batman-adv/main.h:137: BATADV_MESH_ACTIVE,
net/batman-adv/main.h-138-
--
net/batman-adv/mesh-interface.c=112=static int batadv_interface_set_mac_addr(struct net_device *dev, void *p)
--
net/batman-adv/mesh-interface.c-125- /* only modify transtable if it has been initialized before */
net/batman-adv/mesh-interface.c:126: if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
net/batman-adv/mesh-interface.c-127- return 0;
--
net/batman-adv/mesh-interface.c=167=static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
--
net/batman-adv/mesh-interface.c-192-
net/batman-adv/mesh-interface.c:193: if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
net/batman-adv/mesh-interface.c-194- goto dropped;
]
|
| 64/2 |
2026/07/05 10:17 |
llm |
expert |
0m
Model:
Tokens:
input: 56804
output: 53
thoughts: 12
Thoughts: Okay, here's my summary, presented as a stream of thought:
**Diving into batadv_mesh_init**
Alright, time to get started. Let's dig into the `batadv_mesh_init` function. I need to understand how the BatMan Advanced mesh initialization is handled. What's the core setup here? What data structures are getting created or initialized? Is this the function that registers the mesh interface with the kernel? Are we dealing with hardware setup here, like configuring wireless interfaces, or is this more about internal BatMan structures? I want to pay particularly close attention to how it handles potential errors. What are the common failure points during initialization and how does the function deal with them? And what about the sequence of operations? What needs to happen first? The overall architecture of the init function will be the main point. Let's get to work.
|
| 65/2 |
2026/07/05 10:17 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/batman-adv/main.c IncludeSnippetLines:0 Name:batadv_mesh_init SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:batadv_meshif_init_late SourceFile:net/batman-adv/mesh-interface.c SourceLine:811]] TruncatedOutput:false]
|
| 66/2 |
2026/07/05 10:17 |
llm |
expert |
0m
Model:
Tokens:
input: 57131
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.
|
| 67/2 |
2026/07/05 10:18 |
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]
|
| 68/2 |
2026/07/05 10:18 |
llm |
expert |
0m
Model:
Tokens:
input: 59040
output: 58
thoughts: 54
Thoughts: Here's my take on the thought process:
**Diving into the Batadv Initialization Sequence**
Okay, so I'm tracing the initialization of a BATMAN Advanced (B.A.T.M.A.N.) mesh interface, specifically looking at `batadv_meshif_init_late`. Let's see... the code flow shows `batadv_meshif_init_late` is calling `batadv_mesh_init(dev)`. That makes sense; the late initialization likely handles device-specific setup after the core mesh structure is initialized. Now, where does `batadv_meshif_init_late` get called from? I *thought* it was `batadv_meshif_newlink`. Hmm, hold on... let me double-check. I need to refresh my memory on the entire device creation process and make sure I'm not missing a step. My initial assessment is the `init_late` function is being called from the link setup routine, but I can't be certain. Gotta be precise here.
|
| 69/2 |
2026/07/05 10:18 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/batman-adv/mesh-interface.c IncludeSnippetLines:0 Name:batadv_meshif_init_late SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:batadv_netdev_ops SourceFile:net/batman-adv/mesh-interface.c SourceLine:877]] TruncatedOutput:false]
|
| 70/2 |
2026/07/05 10:18 |
llm |
expert |
0m
Model:
Tokens:
input: 59457
output: 43
thoughts: 1271
Thoughts: **Vulnerability Deep Dive: batman-adv's DHCP ACK Processing & Denial of Service**
Okay, so I've been tracing the `batadv` initialization and I see something concerning in `batadv_netdev_ops.ndo_init`. Specifically, when a `batadv` interface is registered, `batadv_meshif_init_late` kicks off, followed by `batadv_mesh_init`. And the critical part is that `batadv_mesh_init` immediately sets the `mesh_state` to `BATADV_MESH_ACTIVE`. This means a `batadv` interface is *active* right from the get-go after creation.
My immediate thought is, can an unprivileged user exploit this? And, yes, if an unprivileged user can create a network namespace, they can create a `batadv` interface. That means they could send packets to it!
I quickly verify this with `unshare -Urn`, `ip link add name bat0 type batadv`, and `ip link set bat0 up`. Bingo. They can inject packets.
Now, if a user sends packets to the `bat0` interface, `batadv_interface_tx` will get involved. If they send a crafted UDP packet to port 68 from port 67, this will trigger `batadv_dat_snoop_outgoing_dhcp_ack`. Diving into `batadv_dat_snoop_outgoing_dhcp_ack`, I see the relevant code:
```c
if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr))
return;
batadv_dat_put_dhcp(bat_priv, chaddr, yiaddr, eth_hdr(skb)->h_source,
ip_src, vid);
```
Essentially, `batadv_dat_put_dhcp` adds `yiaddr` and `ip_src` to the DAT hash table. Crucially, the user controls these fields through the DHCP payload. So, I realize a malicious user can set `yiaddr` and `chaddr` to arbitrary values!
Now, even if `batadv_dat_check_dhcp_ack` validates the packet as a DHCP ACK, that's not a strong barrier because a valid-looking DHCP ACK can be easily crafted and injected.
And here's where it gets interesting: the user can either use a raw socket on `bat0` (with `CAP_NET_RAW` in their network namespace) or even a simple UDP socket if the routing is configured to send the packet to `bat0`. Even better, they can use AF_PACKET sockets. Since they have `CAP_NET_RAW` in their user namespace, they can open an AF_PACKET socket and inject arbitrary Ethernet frames directly into `bat0`!
So, the user injects their crafted DHCP ACK packets using AF_PACKET. These packets go through `dev_queue_xmit` and then `batadv_interface_tx`. Each injected packet triggers an addition of two entries to the `bat_priv->dat.hash` table, like this:
```c
batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid);
batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
```
Since `yiaddr` and `chaddr` are derived from the DHCP payload, the attacker has complete control and can make these completely arbitrary and different for each packet. This creates a massive number of entries in `bat_priv->dat.hash`.
Then comes the kill shot: `batadv_dat_purge` runs periodically. Every 10 seconds, `batadv_dat_purge` iterates over this massive number of entries. Since it locks each bucket individually and iterates over all the entries within each bucket, this will take an extraordinarily long time. This is a clear softlockup DoS. Plus, because each entry requires memory allocation (`kmalloc_obj(*dat_entry, GFP_ATOMIC)`), this also leads to substantial memory consumption and potentially OOM.
I confirm that this is accessible from unprivileged processes via user namespaces. An unprivileged user can create a user namespace and a network namespace, create a `batadv` interface, and inject packets. I'm satisfied that this is exploitable from user namespaces.
Now, could this be triggered by a local network attacker? Absolutely. If the machine is part of a batman-adv mesh, it has a physical interface (e.g., `wlan0`) attached to `bat0`. An attacker on the mesh network sends packets to the victim, using ARP requests or DHCP ACKs. These packets
|
| 71/2 |
2026/07/05 10:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/batman-adv/routing.c Name:batadv_recv_unicast_packet]
Results: map[SourceCode: 930: /**
931: * batadv_recv_unicast_packet() - Process incoming unicast packet
932: * @skb: incoming packet buffer
933: * @recv_if: incoming hard interface
934: *
935: * Return: NET_RX_SUCCESS on success or NET_RX_DROP in case of failure
936: */
937: int batadv_recv_unicast_packet(struct sk_buff *skb,
938: struct batadv_hard_iface *recv_if)
939: {
940: struct batadv_priv *bat_priv = netdev_priv(recv_if->mesh_iface);
941: struct batadv_unicast_packet *unicast_packet;
942: struct batadv_unicast_4addr_packet *unicast_4addr_packet;
943: u8 *orig_addr, *orig_addr_gw;
944: struct batadv_orig_node *orig_node = NULL, *orig_node_gw = NULL;
945: int check, hdr_size = sizeof(*unicast_packet);
946: enum batadv_subtype subtype;
947: int ret = NET_RX_DROP;
948: bool is4addr, is_gw;
949:
950: unicast_packet = (struct batadv_unicast_packet *)skb->data;
951: is4addr = unicast_packet->packet_type == BATADV_UNICAST_4ADDR;
952: /* the caller function should have already pulled 2 bytes */
953: if (is4addr)
954: hdr_size = sizeof(*unicast_4addr_packet);
955:
956: /* function returns -EREMOTE for promiscuous packets */
957: check = batadv_check_unicast_packet(bat_priv, skb, hdr_size);
958: if (check < 0)
959: goto free_skb;
960:
961: if (!batadv_check_unicast_ttvn(bat_priv, skb, hdr_size))
962: goto free_skb;
963:
964: unicast_packet = (struct batadv_unicast_packet *)skb->data;
965:
966: /* packet for me */
967: if (batadv_is_my_mac(bat_priv, unicast_packet->dest)) {
968: /* If this is a unicast packet from another backgone gw,
969: * drop it.
970: */
971: orig_addr_gw = eth_hdr(skb)->h_source;
972: orig_node_gw = batadv_orig_hash_find(bat_priv, orig_addr_gw);
973: if (orig_node_gw) {
974: is_gw = batadv_bla_is_backbone_gw(skb, orig_node_gw,
975: hdr_size);
976: batadv_orig_node_put(orig_node_gw);
977: if (is_gw) {
978: batadv_dbg(BATADV_DBG_BLA, bat_priv,
979: "%s(): Dropped unicast pkt received from another backbone gw %pM.\n",
980: __func__, orig_addr_gw);
981: goto free_skb;
982: }
983: }
984:
985: if (is4addr) {
986: unicast_4addr_packet =
987: (struct batadv_unicast_4addr_packet *)skb->data;
988: subtype = unicast_4addr_packet->subtype;
989: batadv_dat_inc_counter(bat_priv, subtype);
990:
991: /* Only payload data should be considered for speedy
992: * join. For example, DAT also uses unicast 4addr
993: * types, but those packets should not be considered
994: * for speedy join, since the clients do not actually
995: * reside at the sending originator.
996: */
997: if (subtype == BATADV_P_DATA) {
998: orig_addr = unicast_4addr_packet->src;
999: orig_node = batadv_orig_hash_find(bat_priv,
1000: orig_addr);
1001: }
1002: }
1003:
1004: if (batadv_dat_snoop_incoming_arp_request(bat_priv, skb,
1005: hdr_size))
1006: goto rx_success;
1007: if (batadv_dat_snoop_incoming_arp_reply(bat_priv, skb,
1008: hdr_size))
1009: goto rx_success;
1010:
1011: batadv_dat_snoop_incoming_dhcp_ack(bat_priv, skb, hdr_size);
1012:
1013: batadv_interface_rx(recv_if->mesh_iface, skb, hdr_size,
1014: orig_node);
1015:
1016: rx_success:
1017: batadv_orig_node_put(orig_node);
1018:
1019: return NET_RX_SUCCESS;
1020: }
1021:
1022: ret = batadv_route_unicast_packet(skb, recv_if);
1023: /* skb was consumed */
1024: skb = NULL;
1025:
1026: free_skb:
1027: kfree_skb(skb);
1028:
1029: return ret;
1030: }
SourceFile:net/batman-adv/routing.c]
|
| 72/2 |
2026/07/05 10:18 |
llm |
expert |
0m
Model:
Tokens:
input: 63492
output: 66
thoughts: 2380
Thoughts: **Analysis of the batman-adv DAT Table Denial of Service Vulnerability**
Okay, so here's the breakdown of this batman-adv issue. `batadv_recv_unicast_packet` is the entry point, and it calls these critical functions: `batadv_dat_snoop_incoming_arp_request`, `batadv_dat_snoop_incoming_arp_reply`, and `batadv_dat_snoop_incoming_dhcp_ack`. The heart of the problem is that an attacker on the mesh network can craft unicast packets, stuffing them with malicious ARP requests, replies, or DHCP ACKs. Each of these packets triggers `batadv_dat_entry_add`, which subsequently floods the DAT hash table.
Because the attacker can control the inner payload (ARP or DHCP), they get to specify arbitrary IP and MAC addresses. This means they can remotely inject *millions* of entries into the DAT hash table! That screams Remote Denial of Service (DoS) to me. It's exploitable over the mesh network, typically Ethernet or WiFi.
Now, accessibility is key. Can an unprivileged local user trigger this? Well, they can't *directly* create a `batadv` interface without `CAP_NET_ADMIN`. However, if one already exists, they *could* send UDP packets, which in turn might trigger ARP requests. But, those requests would originate from the interface's IP, so while it *might* add entries to the table, itβs not the DoS vector. DHCP ACKs from an unprivileged process? Nope, that would require root (or `CAP_NET_BIND_SERVICE`).
But user namespaces? Bingo! In user namespaces, a local unprivileged user *can* create and manipulate `batadv` interfaces and inject arbitrary packets. That's a huge problem.
From a remote perspective, the vulnerability is ripe for exploitation. An attacker on the mesh network can send crafted packets directly, making it an Ethernet (or WiFi) Network Trigger.
So, here's my summarized findings:
* Exploitable: **No** (it's DoS, not memory corruption).
* Denial Of Service: **Yes**.
* Accessible From Unprivileged Processes: **No** (requires user namespaces or remote access).
* Accessible From User Namespaces: **Yes**.
* VM Guest Trigger: **No** (unless the VM is *part* of the mesh network).
* VM Host Trigger: **No**.
* Ethernet Network Trigger: **Yes** (remotely).
* Other Remote Trigger: **Yes** (WiFi mesh network).
* Peripheral Trigger: **No**.
* Malicious Filesystem Trigger: **No**.
The root cause is the unbounded addition of entries to the DAT hash table. batman-adv's DAT implementation has no limit on the DAT entries. An attacker can flood the network with ARP or DHCP packets, causing the DAT hash table to grow indefinitely. This leads to memory exhaustion (OOM) and softlockups during `batadv_dat_purge`, which is what's being seen in the provided bug report. The size of the hash table is fixed at 1024 buckets, so it's all about how many entries land in each one. Because there's no limit on the entries, it can take an incredibly long time, hence the softlockup.
I'm confident in this assessment.
|
| 73/2 |
2026/07/05 10:18 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:true PeripheralTrigger:false RemoteTrigger:true Unprivileged:false UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:true PeripheralTrigger:false RemoteTrigger:true Unprivileged:false UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 74/2 |
2026/07/05 10:18 |
llm |
expert |
0m
Model:
Tokens:
input: 66736
output: 761
thoughts: 245
Thoughts: **My Analysis of the Batadv Softlockup**
Okay, the data's in, time to break down the logic. Essentially, we're looking at a softlockup situation within `__batadv_dat_purge`. The root cause is that unbounded growth of the Distributed ARP Table (DAT) hash table. We're talking about an attacker being able to trigger this β and it's all down to flooding the network with maliciously crafted ARP or DHCP packets. Clever.
My immediate focus is the unbounded entries. When `batadv_dat_purge` kicks in, it meticulously walks through the entire DAT hash table, bucket by bucket. Hereβs where it gets problematic: it holds a spinlock for *each* bucket during the iteration. With potentially millions of entries, this becomes a massive, resource-intensive operation, dragging on and on, hence the softlockup.
The `hub_event` task hanging is just a side effect, a symptom of the CPU 0 softlockup, which causes the entire system to grind to a halt because it can't release locks and run other tasks.
The vulnerability's remote nature, exploitable over the mesh network (ethernet or WiFi), is a serious concern. It also presents a local threat - even a non-privileged user can exploit it by setting up a network namespace and injecting the crafted packets.
Ultimately, weβre looking at a classic Denial of Service (DoS) vulnerability β softlockup and memory exhaustion. The good news (in a very perverse way) is that it doesnβt seem to be a vector for privilege escalation or information leakage. Thatβs at least something. Now, letβs consider mitigation strategies.
|
| 75/1 |
2026/07/05 10:18 |
action |
format-explanation |
0m
Results: map[Explanation:Based on the analysis of the bug report and the kernel source code, here is the
assessment of the vulnerability:
**Root Cause Analysis:**
The bug report shows a softlockup on CPU 0 in `__batadv_dat_purge` (part of the
`batman-adv` subsystem) and a secondary hung task in `hub_event` on CPU 1. The
hung task in `hub_event` is a side effect of CPU 0 being hogged by the
softlockup, which prevents other tasks from running and releasing locks (such as
`hcd->address0_mutex`).
The root cause of the softlockup is an unbounded accumulation of entries in the
Distributed ARP Table (DAT) hash table (`bat_priv->dat.hash`). The `batman-adv`
subsystem snoops incoming and outgoing ARP and DHCP packets to populate this
hash table. Functions like `batadv_dat_snoop_incoming_arp_request`,
`batadv_dat_snoop_incoming_arp_reply`, and `batadv_dat_snoop_outgoing_dhcp_ack`
extract IP and MAC addresses from these packets and add them to the DAT hash
table using `batadv_dat_entry_add`.
Crucially, there is no limit on the number of entries that can be added to the
DAT hash table. An attacker can send a flood of crafted ARP or DHCP packets with
varying source/destination IP and MAC addresses. Each unique packet will cause a
new `batadv_dat_entry` to be allocated and added to the hash table.
When the periodic worker `batadv_dat_purge` runs, it iterates over the 1024
buckets of the hash table. For each bucket, it acquires a spinlock
(`spin_lock_bh`), iterates over all entries to check if they have timed out, and
then releases the lock. If an attacker has added millions of entries, this
iteration takes an excessively long time, causing a softlockup (and potentially
memory exhaustion/OOM due to the unbounded allocations).
**Trigger Vectors:**
1. **Remote/Network Trigger:** An attacker on the same layer 2 mesh network
(Ethernet or WiFi) can trigger this remotely by flooding the network with
crafted ARP requests, ARP replies, or DHCP ACKs. The victim's `batman-adv`
interface will snoop these packets and add the spoofed entries to its DAT hash
table.
2. **User Namespaces:** A local unprivileged user can trigger this by creating a
new user and network namespace. Within the new network namespace, they have
`CAP_NET_ADMIN` and can create a `batadv` interface (`ip link add type batadv`).
They can then use an `AF_PACKET` socket or raw socket to inject millions of
crafted DHCP ACK or ARP packets directly into the `batadv` interface, filling
the DAT hash table and causing a softlockup.
3. **Unprivileged Processes (without namespaces):** A local unprivileged process
cannot easily trigger this because creating a `batadv` interface requires
`CAP_NET_ADMIN`. Furthermore, injecting arbitrary ARP or DHCP packets with
spoofed IPs requires raw sockets (`CAP_NET_RAW`) or binding to privileged ports
(e.g., port 67 for DHCP server).
**Security Impact:**
This is a Denial of Service (DoS) vulnerability. It allows an attacker to cause
memory exhaustion and CPU softlockups, effectively crashing or hanging the
system. It does not involve memory corruption (like use-after-free or
out-of-bounds access) and is therefore not exploitable for privilege escalation
or information leaks.]
|