| 0/0 |
2026/05/31 18:26 |
flow |
assessment-security |
57m
Results: map[DenialOfService:true Explanation:**Bug Analysis:**
The crash is a NULL pointer dereference in `atm_force_charge()`, which is called
from `send_to_lecd()` in the ATM LAN Emulation (LANE) driver (`net/atm/lec.c`).
The function `send_to_lecd()` checks if the control socket `priv->lecd` is NULL
at the beginning of the function. However, it accesses `priv->lecd` later
without holding any locks. If the ATM control socket is closed concurrently by
the user-space daemon, the `lec_atm_close()` function is invoked, which sets
`priv->lecd = NULL`. When `send_to_lecd()` subsequently calls
`atm_force_charge(priv->lecd, ...)`, it dereferences the now-NULL pointer.
Specifically, `atm_force_charge()` attempts to execute an `atomic_add()` on the
`sk_rmem_alloc` field of the socket structure. The `sk_rmem_alloc` field is
located at offset `0x140` within the `struct sock`.
**Exploitability:**
The bug results in a 4-byte write (the `truesize` of the `skb`) to the virtual
address `0x140`. Because this address is within the unmapped zero page (which is
protected by `mmap_min_addr` in modern kernels), the access will
deterministically cause a page fault and a kernel panic. It cannot be exploited
for privilege escalation or memory corruption of valid kernel objects.
**Denial of Service:**
Triggering the bug causes a kernel panic, leading to a Denial of Service (DoS).
**Privilege Requirements:**
To trigger the race condition, an attacker must be able to create a LANE device
and attach an ATM control socket to it. This setup is performed via the
`ATMLEC_CTRL` ioctl on an ATM socket. The `lane_ioctl()` function explicitly
checks for `capable(CAP_NET_ADMIN)`. Because it uses `capable()` rather than
`ns_capable()`, it strictly requires the `CAP_NET_ADMIN` capability in the
**initial user namespace**. Therefore, unprivileged users and users within
unprivileged user namespaces cannot reach the vulnerable code or trigger the
teardown process.
**Network/Remote Trigger:**
The race condition occurs between the local transmission of a packet (e.g., via
`lec_start_xmit()`) and the local closure of the ATM control socket. The control
socket is a local communication channel between the kernel and the `lecd`
daemon, and it cannot be closed remotely. Thus, the bug cannot be triggered by
remote network traffic or remote ATM hosts. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/05/31 18:26 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/05/31 18:26 |
action |
kernel-checkouter |
2m
Results: map[KernelSrc:/app/workdir/cache/src/e872b4e689ded71f7cce363dd509dd64f4bd57c5]
|
| 3/1 |
2026/05/31 18:29 |
action |
kernel-builder |
25m
Results: map[KernelObj:/app/workdir/cache/build/56637a50777e2ea73df22f7d23980363a60991f3]
|
| 4/1 |
2026/05/31 18:55 |
action |
codesearch-prepare |
22m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/05/31 19:17 |
agent |
expert |
6m
Model:
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption or elevated privileges.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
==================================================================
BUG: KASAN: null-ptr-deref in instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
BUG: KASAN: null-ptr-deref in atomic_add include/linux/atomic/atomic-instrumented.h:104 [inline]
BUG: KASAN: null-ptr-deref in atm_force_charge include/linux/atmdev.h:261 [inline]
BUG: KASAN: null-ptr-deref in send_to_lecd+0x2ef/0x5e0 net/atm/lec.c:535
Write of size 4 at addr 0000000000000140 by task dhcpcd/28797
CPU: 1 UID: 101 PID: 28797 Comm: dhcpcd Tainted: G L syzkaller #0 PREEMPT(full)
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 02/12/2026
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
kasan_report+0xdf/0x1e0 mm/kasan/report.c:595
check_region_inline mm/kasan/generic.c:186 [inline]
kasan_check_range+0x10f/0x1e0 mm/kasan/generic.c:200
instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
atomic_add include/linux/atomic/atomic-instrumented.h:104 [inline]
atm_force_charge include/linux/atmdev.h:261 [inline]
send_to_lecd+0x2ef/0x5e0 net/atm/lec.c:535
lec_arp_resolve net/atm/lec.c:1791 [inline]
lec_start_xmit+0x1252/0x2070 net/atm/lec.c:285
__netdev_start_xmit include/linux/netdevice.h:5292 [inline]
netdev_start_xmit include/linux/netdevice.h:5301 [inline]
xmit_one net/core/dev.c:3871 [inline]
dev_hard_start_xmit+0x121/0x7d0 net/core/dev.c:3887
sch_direct_xmit+0x1b2/0xc60 net/sched/sch_generic.c:347
qdisc_restart net/sched/sch_generic.c:412 [inline]
__qdisc_run+0x52d/0x1b10 net/sched/sch_generic.c:420
qdisc_run include/net/pkt_sched.h:120 [inline]
qdisc_run include/net/pkt_sched.h:117 [inline]
__dev_xmit_skb net/core/dev.c:4196 [inline]
__dev_queue_xmit+0x196d/0x4800 net/core/dev.c:4802
dev_queue_xmit include/linux/netdevice.h:3384 [inline]
packet_xmit+0x243/0x310 net/packet/af_packet.c:275
packet_snd net/packet/af_packet.c:3076 [inline]
packet_sendmsg+0x32f4/0x53c0 net/packet/af_packet.c:3108
sock_sendmsg_nosec net/socket.c:727 [inline]
__sock_sendmsg net/socket.c:742 [inline]
sock_write_iter+0x524/0x5a0 net/socket.c:1195
do_iter_readv_writev+0x6ee/0x920 fs/read_write.c:829
vfs_writev+0x360/0xe10 fs/read_write.c:1059
do_writev+0x28a/0x340 fs/read_write.c:1105
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x106/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f53f6e3c407
Code: 48 89 fa 4c 89 df e8 38 aa 00 00 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 1a 5b c3 0f 1f 84 00 00 00 00 00 48 8b 44 24 10 0f 05 <5b> c3 0f 1f 80 00 00 00 00 83 e2 39 83 fa 08 75 de e8 23 ff ff ff
RSP: 002b:00007fff9cf80170 EFLAGS: 00000202 ORIG_RAX: 0000000000000014
RAX: ffffffffffffffda RBX: 00007f53f6db2780 RCX: 00007f53f6e3c407
RDX: 0000000000000002 RSI: 00007fff9cf801d0 RDI: 0000000000000003
RBP: 0000000000000188 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000202 R12: 00007fff9cf80210
R13: 0000562512791790 R14: 00007fff9cf80260 R15: 00007fff9cf90430
</TASK>
==================================================================
Reply: **Bug Analysis:**
The crash is a NULL pointer dereference in `atm_force_charge()`, which is called from `send_to_lecd()` in the ATM LAN Emulation (LANE) driver (`net/atm/lec.c`).
The function `send_to_lecd()` checks if the control socket `priv->lecd` is NULL at the beginning of the function. However, it accesses `priv->lecd` later without holding any locks. If the ATM control socket is closed concurrently by the user-space daemon, the `lec_atm_close()` function is invoked, which sets `priv->lecd = NULL`. When `send_to_lecd()` subsequently calls `atm_force_charge(priv->lecd, ...)`, it dereferences the now-NULL pointer.
Specifically, `atm_force_charge()` attempts to execute an `atomic_add()` on the `sk_rmem_alloc` field of the socket structure. The `sk_rmem_alloc` field is located at offset `0x140` within the `struct sock`.
**Exploitability:**
The bug results in a 4-byte write (the `truesize` of the `skb`) to the virtual address `0x140`. Because this address is within the unmapped zero page (which is protected by `mmap_min_addr` in modern kernels), the access will deterministically cause a page fault and a kernel panic. It cannot be exploited for privilege escalation or memory corruption of valid kernel objects.
**Denial of Service:**
Triggering the bug causes a kernel panic, leading to a Denial of Service (DoS).
**Privilege Requirements:**
To trigger the race condition, an attacker must be able to create a LANE device and attach an ATM control socket to it. This setup is performed via the `ATMLEC_CTRL` ioctl on an ATM socket. The `lane_ioctl()` function explicitly checks for `capable(CAP_NET_ADMIN)`. Because it uses `capable()` rather than `ns_capable()`, it strictly requires the `CAP_NET_ADMIN` capability in the **initial user namespace**. Therefore, unprivileged users and users within unprivileged user namespaces cannot reach the vulnerable code or trigger the teardown process.
**Network/Remote Trigger:**
The race condition occurs between the local transmission of a packet (e.g., via `lec_start_xmit()`) and the local closure of the ATM control socket. The control socket is a local communication channel between the kernel and the `lecd` daemon, and it cannot be closed remotely. Thus, the bug cannot be triggered by remote network traffic or remote ATM hosts.
|
| 6/2 |
2026/05/31 19:17 |
llm |
expert |
0m
Model:
Tokens:
input: 7184
output: 146
thoughts: 140
Thoughts: Okay, here's my analysis of this KASAN null-ptr-deref, delivered from the expert's perspective:
**Investigating a KASAN Null-Pointer Dereference in `atm_force_charge`**
Right, so we've got a KASAN null-pointer dereference kicking off in `atm_force_charge`, which, based on the information, is being triggered by a call to `send_to_lecd` within `net/atm/lec.c`. My initial focus is definitely going to be on that `lec.c` file and, specifically, on understanding the control flow and potential null-pointer exposure within `send_to_lecd`.
I'll start by pulling up the definition and source code for `send_to_lecd` using `codesearch-definition-source`. I'll need to immediately follow the path that function takes. I'll need to know which values are passed to it, and which, if any, could possibly be NULL.
Then, I'll examine the definition of `atm_force_charge`. Knowing where the fault occurs, and its function parameters will be invaluable. `codesearch-definition-source` for that, too. That should give me the entry point and, more importantly, the conditions that lead to its invocation.
To broaden the investigation, I'm going to quickly check the calls of other functions related to the LEC, to understand the flow within the code. Let's see how `lec_start_xmit` and `lec_arp_resolve` fit into this picture. Grabbing the definition and source for those functions with `codesearch-definition-source` will provide some context. I'll look for shared data structures, potential race conditions, or similar points of vulnerability.
|
| 7/2 |
2026/05/31 19:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/lec.c Name:send_to_lecd]
Results: map[SourceCode: 504: /*
505: * LANE2: new argument struct sk_buff *data contains
506: * the LE_ARP based TLVs introduced in the LANE2 spec
507: */
508: static int
509: send_to_lecd(struct lec_priv *priv, atmlec_msg_type type,
510: const unsigned char *mac_addr, const unsigned char *atm_addr,
511: struct sk_buff *data)
512: {
513: struct sock *sk;
514: struct sk_buff *skb;
515: struct atmlec_msg *mesg;
516:
517: if (!priv || !priv->lecd)
518: return -1;
519: skb = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC);
520: if (!skb)
521: return -1;
522: skb->len = sizeof(struct atmlec_msg);
523: mesg = (struct atmlec_msg *)skb->data;
524: memset(mesg, 0, sizeof(struct atmlec_msg));
525: mesg->type = type;
526: if (data != NULL)
527: mesg->sizeoftlvs = data->len;
528: if (mac_addr)
529: ether_addr_copy(mesg->content.normal.mac_addr, mac_addr);
530: else
531: mesg->content.normal.targetless_le_arp = 1;
532: if (atm_addr)
533: memcpy(&mesg->content.normal.atm_addr, atm_addr, ATM_ESA_LEN);
534:
535: atm_force_charge(priv->lecd, skb->truesize);
536: sk = sk_atm(priv->lecd);
537: skb_queue_tail(&sk->sk_receive_queue, skb);
538: sk->sk_data_ready(sk);
539:
540: if (data != NULL) {
541: pr_debug("about to send %d bytes of data\n", data->len);
542: atm_force_charge(priv->lecd, data->truesize);
543: skb_queue_tail(&sk->sk_receive_queue, data);
544: sk->sk_data_ready(sk);
545: }
546:
547: return 0;
548: }
SourceFile:net/atm/lec.c]
|
| 8/2 |
2026/05/31 19:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/lec.c Name:atm_force_charge]
Results: map[SourceCode: 259: static inline void atm_force_charge(struct atm_vcc *vcc,int truesize)
260: {
261: atomic_add(truesize, &sk_atm(vcc)->sk_rmem_alloc);
262: }
SourceFile:include/linux/atmdev.h]
|
| 9/2 |
2026/05/31 19:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/lec.c Name:lec_start_xmit]
Results: map[SourceCode: 206: static netdev_tx_t lec_start_xmit(struct sk_buff *skb,
207: struct net_device *dev)
208: {
209: struct sk_buff *skb2;
210: struct lec_priv *priv = netdev_priv(dev);
211: struct lecdatahdr_8023 *lec_h;
212: struct atm_vcc *vcc;
213: struct lec_arp_table *entry;
214: unsigned char *dst;
215: int min_frame_size;
216: int is_rdesc;
217:
218: pr_debug("called\n");
219: if (!priv->lecd) {
220: pr_info("%s:No lecd attached\n", dev->name);
221: dev->stats.tx_errors++;
222: netif_stop_queue(dev);
223: kfree_skb(skb);
224: return NETDEV_TX_OK;
225: }
226:
227: pr_debug("skbuff head:%lx data:%lx tail:%lx end:%lx\n",
228: (long)skb->head, (long)skb->data, (long)skb_tail_pointer(skb),
229: (long)skb_end_pointer(skb));
230: #if IS_ENABLED(CONFIG_BRIDGE)
231: if (memcmp(skb->data, bridge_ula_lec, sizeof(bridge_ula_lec)) == 0)
232: lec_handle_bridge(skb, dev);
233: #endif
234:
235: /* Make sure we have room for lec_id */
236: if (skb_headroom(skb) < 2) {
237: pr_debug("reallocating skb\n");
238: skb2 = skb_realloc_headroom(skb, LEC_HEADER_LEN);
239: if (unlikely(!skb2)) {
240: kfree_skb(skb);
241: return NETDEV_TX_OK;
242: }
243: consume_skb(skb);
244: skb = skb2;
245: }
246: skb_push(skb, 2);
247:
248: /* Put le header to place */
249: lec_h = (struct lecdatahdr_8023 *)skb->data;
250: lec_h->le_header = htons(priv->lecid);
251:
252: #if DUMP_PACKETS >= 2
253: #define MAX_DUMP_SKB 99
254: #elif DUMP_PACKETS >= 1
255: #define MAX_DUMP_SKB 30
256: #endif
257: #if DUMP_PACKETS >= 1
258: printk(KERN_DEBUG "%s: send datalen:%ld lecid:%4.4x\n",
259: dev->name, skb->len, priv->lecid);
260: print_hex_dump(KERN_DEBUG, "", DUMP_OFFSET, 16, 1,
261: skb->data, min(skb->len, MAX_DUMP_SKB), true);
262: #endif /* DUMP_PACKETS >= 1 */
263:
264: /* Minimum ethernet-frame size */
265: min_frame_size = LEC_MINIMUM_8023_SIZE;
266: if (skb->len < min_frame_size) {
267: if ((skb->len + skb_tailroom(skb)) < min_frame_size) {
268: skb2 = skb_copy_expand(skb, 0,
269: min_frame_size - skb->truesize,
270: GFP_ATOMIC);
271: dev_kfree_skb(skb);
272: if (skb2 == NULL) {
273: dev->stats.tx_dropped++;
274: return NETDEV_TX_OK;
275: }
276: skb = skb2;
277: }
278: skb_put(skb, min_frame_size - skb->len);
279: }
280:
281: /* Send to right vcc */
282: is_rdesc = 0;
283: dst = lec_h->h_dest;
284: entry = NULL;
285: vcc = lec_arp_resolve(priv, dst, is_rdesc, &entry);
286: pr_debug("%s:vcc:%p vcc_flags:%lx, entry:%p\n",
287: dev->name, vcc, vcc ? vcc->flags : 0, entry);
288: if (!vcc || !test_bit(ATM_VF_READY, &vcc->flags)) {
289: if (entry && (entry->tx_wait.qlen < LEC_UNRES_QUE_LEN)) {
290: pr_debug("%s:queuing packet, MAC address %pM\n",
291: dev->name, lec_h->h_dest);
292: skb_queue_tail(&entry->tx_wait, skb);
293: } else {
294: pr_debug("%s:tx queue full or no arp entry, dropping, MAC address: %pM\n",
295: dev->name, lec_h->h_dest);
296: dev->stats.tx_dropped++;
297: dev_kfree_skb(skb);
298: }
299: goto out;
300: }
301: #if DUMP_PACKETS > 0
302: printk(KERN_DEBUG "%s:sending to vpi:%d vci:%d\n",
303: dev->name, vcc->vpi, vcc->vci);
304: #endif /* DUMP_PACKETS > 0 */
305:
306: while (entry && (skb2 = skb_dequeue(&entry->tx_wait))) {
307: pr_debug("emptying tx queue, MAC address %pM\n", lec_h->h_dest);
308: lec_send(vcc, skb2);
309: }
310:
311: lec_send(vcc, skb);
312:
313: if (!atm_may_send(vcc, 0)) {
314: struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc);
315:
316: vpriv->xoff = 1;
317: netif_stop_queue(dev);
318:
319: /*
320: * vcc->pop() might have occurred in between, making
321: * the vcc usuable again. Since xmit is serialized,
322: * this is the only situation we have to re-test.
323: */
324:
325: if (atm_may_send(vcc, 0))
326: netif_wake_queue(dev);
327: }
328:
329: out:
330: if (entry)
331: lec_arp_put(entry);
332: netif_trans_update(dev);
333: return NETDEV_TX_OK;
334: }
SourceFile:net/atm/lec.c]
|
| 10/2 |
2026/05/31 19:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/lec.c Name:lec_arp_resolve]
Results: map[SourceCode:1704: /*
1705: * Try to find vcc where mac_address is attached.
1706: *
1707: */
1708: static struct atm_vcc *lec_arp_resolve(struct lec_priv *priv,
1709: const unsigned char *mac_to_find,
1710: int is_rdesc,
1711: struct lec_arp_table **ret_entry)
1712: {
1713: unsigned long flags;
1714: struct lec_arp_table *entry;
1715: struct atm_vcc *found;
1716:
1717: if (mac_to_find[0] & 0x01) {
1718: switch (priv->lane_version) {
1719: case 1:
1720: return priv->mcast_vcc;
1721: case 2: /* LANE2 wants arp for multicast addresses */
1722: if (ether_addr_equal(mac_to_find, bus_mac))
1723: return priv->mcast_vcc;
1724: break;
1725: default:
1726: break;
1727: }
1728: }
1729:
1730: spin_lock_irqsave(&priv->lec_arp_lock, flags);
1731: entry = lec_arp_find(priv, mac_to_find);
1732:
1733: if (entry) {
1734: if (entry->status == ESI_FORWARD_DIRECT) {
1735: /* Connection Ok */
1736: entry->last_used = jiffies;
1737: lec_arp_hold(entry);
1738: *ret_entry = entry;
1739: found = entry->vcc;
1740: goto out;
1741: }
1742: /*
1743: * If the LE_ARP cache entry is still pending, reset count to 0
1744: * so another LE_ARP request can be made for this frame.
1745: */
1746: if (entry->status == ESI_ARP_PENDING)
1747: entry->no_tries = 0;
1748: /*
1749: * Data direct VC not yet set up, check to see if the unknown
1750: * frame count is greater than the limit. If the limit has
1751: * not been reached, allow the caller to send packet to
1752: * BUS.
1753: */
1754: if (entry->status != ESI_FLUSH_PENDING &&
1755: entry->packets_flooded <
1756: priv->maximum_unknown_frame_count) {
1757: entry->packets_flooded++;
1758: pr_debug("Flooding..\n");
1759: found = priv->mcast_vcc;
1760: goto out;
1761: }
1762: /*
1763: * We got here because entry->status == ESI_FLUSH_PENDING
1764: * or BUS flood limit was reached for an entry which is
1765: * in ESI_ARP_PENDING or ESI_VC_PENDING state.
1766: */
1767: lec_arp_hold(entry);
1768: *ret_entry = entry;
1769: pr_debug("entry->status %d entry->vcc %p\n", entry->status,
1770: entry->vcc);
1771: found = NULL;
1772: } else {
1773: /* No matching entry was found */
1774: entry = make_entry(priv, mac_to_find);
1775: pr_debug("Making entry\n");
1776: if (!entry) {
1777: found = priv->mcast_vcc;
1778: goto out;
1779: }
1780: lec_arp_add(priv, entry);
1781: /* We want arp-request(s) to be sent */
1782: entry->packets_flooded = 1;
1783: entry->status = ESI_ARP_PENDING;
1784: entry->no_tries = 1;
1785: entry->last_used = entry->timestamp = jiffies;
1786: entry->is_rdesc = is_rdesc;
1787: if (entry->is_rdesc)
1788: send_to_lecd(priv, l_rdesc_arp_xmt, mac_to_find, NULL,
1789: NULL);
1790: else
1791: send_to_lecd(priv, l_arp_xmt, mac_to_find, NULL, NULL);
1792: entry->timer.expires = jiffies + (1 * HZ);
1793: entry->timer.function = lec_arp_expire_arp;
1794: add_timer(&entry->timer);
1795: found = priv->mcast_vcc;
1796: }
1797:
1798: out:
1799: spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
1800: return found;
1801: }
SourceFile:net/atm/lec.c]
|
| 11/2 |
2026/05/31 19:17 |
llm |
expert |
0m
Model:
Tokens:
input: 12461
output: 35
thoughts: 105
Thoughts: **Debugging a Suspected Memory Allocation Issue in ATM Driver**
Okay, the kernel's just crashed, and from the stack trace, it looks like the problem originates within `atm_force_charge` β specifically, at the line `atm_force_charge(priv->lecd, skb->truesize);`. I need to figure out what's going wrong. My first thought is to check what's being passed as arguments. `priv->lecd` is a `struct atm_vcc *`, which is the ATM virtual channel context, and I'm guessing `skb->truesize` contains the size of the packet buffer.
Looking at the `atm_force_charge` function itself, I see it's calling `atomic_add(truesize, &sk_atm(vcc)->sk_rmem_alloc);`. So, it's adding the `truesize` to `sk_rmem_alloc`, which is likely a per-socket receive memory allocation counter. The crash likely involves an issue with this memory allocation. Now, I need to understand what `sk_atm(vcc)` is actually doing. I need to refresh myself, I believe that is simply a cast of the ATM virtual channel to get the socket structure for it. Let me just confirm that quickly.
|
| 12/2 |
2026/05/31 19:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/atmdev.h Name:sk_atm]
Results: map[SourceCode: 135: static inline struct sock *sk_atm(struct atm_vcc *vcc)
136: {
137: return (struct sock *)vcc;
138: }
SourceFile:include/linux/atmdev.h]
|
| 13/2 |
2026/05/31 19:17 |
llm |
expert |
0m
Model:
Tokens:
input: 12968
output: 36
thoughts: 33
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Hmm, That `sk_atm` Casting...**
Wait a second, so `sk_atm` is literally just casting `vcc` to `struct sock *`? That seems... interesting, or perhaps more accurately, *simplistic*. I need to understand the underlying rationale behind this. Is it just a type punning trick to achieve something? Perhaps optimization? Or is this part of a larger abstraction strategy that I am missing? The code here *looks* simple, but appearances can be deceiving, especially in this domain.
Alright, let's back up and refresh my memory. Time to revisit `struct atm_vcc`. I need to understand its structure, its relationship to `struct sock`, and how the two interact in this specific context. Understanding the members of `atm_vcc` and what they represent is crucial to understanding the implications of this apparent type conversion. I need to see what information is being accessed through the generic `sock` pointer that actually originates in the `atm_vcc` structure. I'm hoping I can piece together what this is doing with that data structure and then understand why it is designed this way. Let's see...
|
| 14/2 |
2026/05/31 19:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/atmdev.h Name:atm_vcc]
Results: map[SourceCode: 92: struct atm_vcc {
93: /* struct sock has to be the first member of atm_vcc */
94: struct sock sk;
95: unsigned long flags; /* VCC flags (ATM_VF_*) */
96: short vpi; /* VPI and VCI (types must be equal */
97: /* with sockaddr) */
98: int vci;
99: unsigned long aal_options; /* AAL layer options */
100: unsigned long atm_options; /* ATM layer options */
101: struct atm_dev *dev; /* device back pointer */
102: struct atm_qos qos; /* QOS */
103: struct atm_sap sap; /* SAP */
104: void (*release_cb)(struct atm_vcc *vcc); /* release_sock callback */
105: void (*push)(struct atm_vcc *vcc,struct sk_buff *skb);
106: void (*pop)(struct atm_vcc *vcc,struct sk_buff *skb); /* optional */
107: int (*push_oam)(struct atm_vcc *vcc,void *cell);
108: int (*send)(struct atm_vcc *vcc,struct sk_buff *skb);
109: void *dev_data; /* per-device data */
110: void *proto_data; /* per-protocol data */
111: struct k_atm_aal_stats *stats; /* pointer to AAL stats group */
112: struct module *owner; /* owner of ->push function */
113: /* SVC part --- may move later ------------------------------------- */
114: short itf; /* interface number */
115: struct sockaddr_atmsvc local;
116: struct sockaddr_atmsvc remote;
117: /* Multipoint part ------------------------------------------------- */
118: struct atm_vcc *session; /* session VCC descriptor */
119: /* Other stuff ----------------------------------------------------- */
120: void *user_back; /* user backlink - not touched by */
121: /* native ATM stack. Currently used */
122: /* by CLIP and sch_atm. */
123: };
SourceFile:include/linux/atmdev.h]
|
| 15/2 |
2026/05/31 19:17 |
llm |
expert |
0m
Model:
Tokens:
input: 13961
output: 39
thoughts: 64
Thoughts: **Analyzing a Null Pointer Dereference**
Okay, so I've got a crash, a null pointer dereference, right at address `0000000000000140`. Now, that's not exactly helpful on its own. However, if I am reading this correctly, it seems this offset might point to `sk_rmem_alloc` and that's interesting. I bet it is located at offset `0x140` within the `struct sock`. I need to confirm this quickly. Let me double-check the `struct sock` layout to verify the placement of that particular member and figure out if that's the real problem.
|
| 16/2 |
2026/05/31 19:17 |
tool |
codesearch-struct-layout |
0m
Args: map[ContextFile:include/net/sock.h FieldOffset:320 Name:sock]
Results: map[Fields:[map[Name:sk_receive_queue OffsetBits:1856 SizeBits:704] map[Name:sk_backlog OffsetBits:2560 SizeBits:192]]]
|
| 17/2 |
2026/05/31 19:17 |
llm |
expert |
0m
Model:
Tokens:
input: 14273
output: 32
thoughts: 104
Thoughts: **Dissecting the Socket Receive Queue**
Okay, so I'm looking at offset 320, or 0x140. The symbol I'm seeing associated with it is `sk_receive_queue`. That makes sense. But wait... is `sk_rmem_alloc` actually *inside* `sk_receive_queue`? That doesn't seem right. Let me check... Ah, `sk_receive_queue` is a `struct sk_buff_head`, which would include members like `qlen` and a `lock`. Now I remember: `sk_rmem_alloc` isn't *within* the `sk_buff_head`. It's actually a separate field, an `atomic_t`. Let's confirm that by checking the definition of the `struct sock` itself. It's time to refresh my memory on the layout and find where `sk_rmem_alloc` truly resides within the overall `struct sock` structure.
|
| 18/2 |
2026/05/31 19:17 |
tool |
codesearch-struct-layout |
0m
Args: map[ContextFile:include/net/sock.h Name:sock]
Results: map[Fields:[map[Name:__sk_common OffsetBits:0 SizeBits:1088] map[Name:__cacheline_group_begin__sock_write_rx OffsetBits:1088 SizeBits:0] map[Name:sk_drops OffsetBits:1088 SizeBits:32] map[Name:sk_peek_off OffsetBits:1120 SizeBits:32] map[Name:sk_error_queue OffsetBits:1152 SizeBits:704] map[Name:sk_receive_queue OffsetBits:1856 SizeBits:704] map[Name:sk_backlog OffsetBits:2560 SizeBits:192] map[Name:__cacheline_group_end__sock_write_rx OffsetBits:2752 SizeBits:0] map[Name:__cacheline_group_begin__sock_read_rx OffsetBits:2752 SizeBits:0] map[Name:sk_rx_dst OffsetBits:2752 SizeBits:64] map[Name:sk_rx_dst_ifindex OffsetBits:2816 SizeBits:32] map[Name:sk_rx_dst_cookie OffsetBits:2848 SizeBits:32] map[Name:sk_ll_usec OffsetBits:2880 SizeBits:32] map[Name:sk_napi_id OffsetBits:2912 SizeBits:32] map[Name:sk_busy_poll_budget OffsetBits:2944 SizeBits:16] map[Name:sk_prefer_busy_poll OffsetBits:2960 SizeBits:8] map[Name:sk_userlocks OffsetBits:2968 SizeBits:8] map[Name:sk_rcvbuf OffsetBits:2976 SizeBits:32] map[Name:sk_filter OffsetBits:3008 SizeBits:64] map[Name: OffsetBits:3072 SizeBits:64] map[Name:sk_data_ready OffsetBits:3136 SizeBits:64] map[Name:sk_rcvtimeo OffsetBits:3200 SizeBits:64] map[Name:sk_rcvlowat OffsetBits:3264 SizeBits:32] map[Name:__cacheline_group_end__sock_read_rx OffsetBits:3296 SizeBits:0] map[Name:__cacheline_group_begin__sock_read_rxtx OffsetBits:3296 SizeBits:0] map[Name:sk_err OffsetBits:3296 SizeBits:32] map[Name:sk_socket OffsetBits:3328 SizeBits:64] map[Name:sk_memcg OffsetBits:3392 SizeBits:64] map[Name:sk_policy OffsetBits:3456 SizeBits:128] map[Name:psp_assoc OffsetBits:3584 SizeBits:64] map[Name:__cacheline_group_end__sock_read_rxtx OffsetBits:3648 SizeBits:0] map[Name:__cacheline_group_begin__sock_write_rxtx OffsetBits:3648 SizeBits:0] map[Name:sk_lock OffsetBits:3648 SizeBits:1536] map[Name:sk_reserved_mem OffsetBits:5184 SizeBits:32] map[Name:sk_forward_alloc OffsetBits:5216 SizeBits:32] map[Name:sk_tsflags OffsetBits:5248 SizeBits:32] map[Name:__cacheline_group_end__sock_write_rxtx OffsetBits:5280 SizeBits:0] map[Name:__cacheline_group_begin__sock_write_tx OffsetBits:5280 SizeBits:0] map[Name:sk_write_pending OffsetBits:5280 SizeBits:32] map[Name:sk_omem_alloc OffsetBits:5312 SizeBits:32] map[Name:sk_err_soft OffsetBits:5344 SizeBits:32] map[Name:sk_wmem_queued OffsetBits:5376 SizeBits:32] map[Name:sk_wmem_alloc OffsetBits:5408 SizeBits:32] map[Name:sk_tsq_flags OffsetBits:5440 SizeBits:64] map[Name: OffsetBits:5504 SizeBits:64] map[Name:sk_write_queue OffsetBits:5568 SizeBits:704] map[Name:sk_frag OffsetBits:6272 SizeBits:128] map[Name: OffsetBits:6400 SizeBits:640] map[Name:sk_pacing_rate OffsetBits:7040 SizeBits:64] map[Name:sk_zckey OffsetBits:7104 SizeBits:32] map[Name:sk_tskey OffsetBits:7136 SizeBits:32] map[Name:sk_tx_queue_mapping_jiffies OffsetBits:7168 SizeBits:64] map[Name:__cacheline_group_end__sock_write_tx OffsetBits:7232 SizeBits:0] map[Name:__cacheline_group_begin__sock_read_tx OffsetBits:7232 SizeBits:0] map[Name:sk_dst_pending_confirm OffsetBits:7232 SizeBits:32] map[Name:sk_pacing_status OffsetBits:7264 SizeBits:32] map[Name:sk_max_pacing_rate OffsetBits:7296 SizeBits:64] map[Name:sk_sndtimeo OffsetBits:7360 SizeBits:64] map[Name:sk_priority OffsetBits:7424 SizeBits:32] map[Name:sk_mark OffsetBits:7456 SizeBits:32] map[Name:sk_uid OffsetBits:7488 SizeBits:32] map[Name:sk_protocol OffsetBits:7520 SizeBits:16] map[Name:sk_type OffsetBits:7536 SizeBits:16] map[Name:sk_dst_cache OffsetBits:7552 SizeBits:64] map[Name:sk_route_caps OffsetBits:7616 SizeBits:64] map[Name:sk_validate_xmit_skb OffsetBits:7680 SizeBits:64] map[Name:sk_gso_type OffsetBits:7744 SizeBits:16] map[Name:sk_gso_max_segs OffsetBits:7760 SizeBits:16] map[Name:sk_gso_max_size OffsetBits:7776 SizeBits:32] map[Name:sk_allocation OffsetBits:7808 SizeBits:32] map[Name:sk_txhash OffsetBits:7840 SizeBits:32] map[Name:sk_sndbuf OffsetBits:7872 SizeBits:32] map[Name:sk_pacing_shift OffsetBits:7904 SizeBits:8] map[Name:sk_use_task_frag OffsetBits:7912 SizeBits:8] map[Name:__cacheline_group_end__sock_read_tx OffsetBits:7920 SizeBits:0] map[Name:sk_gso_disabled OffsetBits:7920 SizeBits:1] map[Name:sk_kern_sock OffsetBits:7921 SizeBits:1] map[Name:sk_no_check_tx OffsetBits:7922 SizeBits:1] map[Name:sk_no_check_rx OffsetBits:7923 SizeBits:1] map[Name:sk_shutdown OffsetBits:7928 SizeBits:8] map[Name:sk_lingertime OffsetBits:7936 SizeBits:64] map[Name:sk_prot_creator OffsetBits:8000 SizeBits:64] map[Name:sk_callback_lock OffsetBits:8064 SizeBits:512] map[Name:sk_ack_backlog OffsetBits:8576 SizeBits:32] map[Name:sk_max_ack_backlog OffsetBits:8608 SizeBits:32] map[Name:sk_ino OffsetBits:8640 SizeBits:64] map[Name:sk_peer_lock OffsetBits:8704 SizeBits:512] map[Name:sk_bind_phc OffsetBits:9216 SizeBits:32] map[Name:sk_peer_pid OffsetBits:9280 SizeBits:64] map[Name:sk_peer_cred OffsetBits:9344 SizeBits:64] map[Name:sk_stamp OffsetBits:9408 SizeBits:64] map[Name:sk_disconnects OffsetBits:9472 SizeBits:32] map[Name: OffsetBits:9504 SizeBits:8] map[Name:sk_clockid OffsetBits:9512 SizeBits:8] map[Name:sk_txtime_deadline_mode OffsetBits:9520 SizeBits:1] map[Name:sk_txtime_report_errors OffsetBits:9521 SizeBits:1] map[Name:sk_txtime_unused OffsetBits:9522 SizeBits:6] map[Name:sk_bpf_cb_flags OffsetBits:9528 SizeBits:8] map[Name:sk_user_data OffsetBits:9536 SizeBits:64] map[Name:sk_security OffsetBits:9600 SizeBits:64] map[Name:sk_cgrp_data OffsetBits:9664 SizeBits:128] map[Name:sk_state_change OffsetBits:9792 SizeBits:64] map[Name:sk_write_space OffsetBits:9856 SizeBits:64] map[Name:sk_error_report OffsetBits:9920 SizeBits:64] map[Name:sk_backlog_rcv OffsetBits:9984 SizeBits:64] map[Name:sk_destruct OffsetBits:10048 SizeBits:64] map[Name:sk_reuseport_cb OffsetBits:10112 SizeBits:64] map[Name:sk_bpf_storage OffsetBits:10176 SizeBits:64] map[Name:sk_drop_counters OffsetBits:10240 SizeBits:64] map[Name: OffsetBits:10304 SizeBits:128] map[Name:ns_tracker OffsetBits:10432 SizeBits:64] map[Name:sk_user_frags OffsetBits:10496 SizeBits:640] map[Name:sk_owner OffsetBits:11136 SizeBits:64]]]
|
| 19/2 |
2026/05/31 19:17 |
llm |
expert |
0m
Model:
Tokens:
input: 17439
output: 34
thoughts: 29
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Investigating `sk_rmem_alloc` Placement**
Alright, hang on a second. I'm seeing `sk_rmem_alloc` called within `__sk_common`. That feels a bit... unexpected. I could have sworn that function was part of the more generic socket memory management routines. Okay, before I jump to any conclusions, let's just confirm where `sk_rmem_alloc` *should* be. I need to make sure I understand the scope of `struct sock_common` thoroughly. I'll need to double check how this interacts with the various memory management components within the socket structure. I need to understand what this means for things like receive buffer allocation strategies, memory accounting, and potentially even socket lifecycle interactions. Let's get a look at `struct sock_common` and see what's what.
|
| 20/2 |
2026/05/31 19:17 |
tool |
codesearch-struct-layout |
0m
Args: map[ContextFile:include/net/sock.h Name:sock_common]
Results: map[Fields:[map[Name: OffsetBits:0 SizeBits:64] map[Name: OffsetBits:64 SizeBits:32] map[Name: OffsetBits:96 SizeBits:32] map[Name:skc_family OffsetBits:128 SizeBits:16] map[Name:skc_state OffsetBits:144 SizeBits:8] map[Name:skc_reuse OffsetBits:152 SizeBits:4] map[Name:skc_reuseport OffsetBits:156 SizeBits:1] map[Name:skc_ipv6only OffsetBits:157 SizeBits:1] map[Name:skc_net_refcnt OffsetBits:158 SizeBits:1] map[Name:skc_bypass_prot_mem OffsetBits:159 SizeBits:1] map[Name:skc_bound_dev_if OffsetBits:160 SizeBits:32] map[Name: OffsetBits:192 SizeBits:128] map[Name:skc_prot OffsetBits:320 SizeBits:64] map[Name:skc_net OffsetBits:384 SizeBits:64] map[Name:skc_v6_daddr OffsetBits:448 SizeBits:128] map[Name:skc_v6_rcv_saddr OffsetBits:576 SizeBits:128] map[Name:skc_cookie OffsetBits:704 SizeBits:64] map[Name: OffsetBits:768 SizeBits:64] map[Name:skc_dontcopy_begin OffsetBits:832 SizeBits:0] map[Name: OffsetBits:832 SizeBits:128] map[Name:skc_tx_queue_mapping OffsetBits:960 SizeBits:16] map[Name:skc_rx_queue_mapping OffsetBits:976 SizeBits:16] map[Name: OffsetBits:992 SizeBits:32] map[Name:skc_refcnt OffsetBits:1024 SizeBits:32] map[Name:skc_dontcopy_end OffsetBits:1056 SizeBits:0] map[Name: OffsetBits:1056 SizeBits:32]]]
|
| 21/2 |
2026/05/31 19:17 |
llm |
expert |
0m
Model:
Tokens:
input: 18287
output: 57
thoughts: 20
Thoughts: Here's my thought process, summarized as you requested:
**Navigating the Codebase: The Hunt for `sk_rmem_alloc`**
Okay, hold on a second. I need to refresh my mental map of this codebase. I'm looking for the `sk_rmem_alloc` function. *Where* exactly is that defined? It's not immediately obvious, and I don't want to waste time sifting through irrelevant files. Time to employ some efficient search techniques. Let's fire up the search tools and get this located quickly.
|
| 22/2 |
2026/05/31 19:18 |
tool |
codesearch-find-references |
0m
Error:
Args: map[ContextFile:include/net/sock.h IncludeSnippetLines:0 Name:sk_rmem_alloc SourceTreePrefix:include/net/sock.h]
Results: map[References:<nil> TruncatedOutput:false]
|
| 23/2 |
2026/05/31 19:18 |
llm |
expert |
0m
Model:
Tokens:
input: 18505
output: 33
thoughts: 44
Thoughts: Okay, here's my take on those thoughts, framed as my own:
**Digging into Socket Memory Allocation**
Right, let me see... `sk_rmem_alloc`... I'm pretty sure that's a key element within the `struct sock` structure. I need to refresh my memory on the details. Okay, let's just confirm this. Time to delve into the `include/net/sock.h` header file. Let me quickly cross-reference that `sk_rmem_alloc` variable and its exact definition. Gotta check the fine print and what that `atomic_t` really signifies in this context β especially in relation to the overall socket memory management. I really need to get the precise picture here.
|
| 24/2 |
2026/05/31 19:18 |
tool |
grepper |
0m
Args: map[Expression:sk_rmem_alloc PathPrefix:include/net/sock.h]
Results: map[Output:include/net/sock.h=360=struct sock {
--
include/net/sock.h-420- } sk_backlog;
include/net/sock.h:421:#define sk_rmem_alloc sk_backlog.rmem_alloc
include/net/sock.h-422-
--
include/net/sock.h=1148=static inline bool sk_rcvqueues_full(const struct sock *sk, unsigned int limit)
include/net/sock.h-1149-{
include/net/sock.h:1150: unsigned int qsize = sk->sk_backlog.len + atomic_read(&sk->sk_rmem_alloc);
include/net/sock.h-1151-
--
include/net/sock.h=1601=static inline int sk_unused_reserved_mem(const struct sock *sk)
--
include/net/sock.h-1608- unused_mem = sk->sk_reserved_mem - sk->sk_wmem_queued -
include/net/sock.h:1609: atomic_read(&sk->sk_rmem_alloc);
include/net/sock.h-1610-
--
include/net/sock.h=2344=static inline int sk_wmem_alloc_get(const struct sock *sk)
--
include/net/sock.h-2349-/**
include/net/sock.h:2350: * sk_rmem_alloc_get - returns read allocations
include/net/sock.h-2351- * @sk: socket
include/net/sock.h-2352- *
include/net/sock.h:2353: * Return: sk_rmem_alloc
include/net/sock.h-2354- */
include/net/sock.h:2355:static inline int sk_rmem_alloc_get(const struct sock *sk)
include/net/sock.h-2356-{
include/net/sock.h:2357: return atomic_read(&sk->sk_rmem_alloc);
include/net/sock.h-2358-}
--
include/net/sock.h=2366=static inline bool sk_has_allocations(const struct sock *sk)
include/net/sock.h-2367-{
include/net/sock.h:2368: return sk_wmem_alloc_get(sk) || sk_rmem_alloc_get(sk);
include/net/sock.h-2369-}
--
include/net/sock.h=2447=static inline void skb_set_owner_r(struct sk_buff *skb, struct sock *sk)
--
include/net/sock.h-2451- skb->destructor = sock_rfree;
include/net/sock.h:2452: atomic_add(skb->truesize, &sk->sk_rmem_alloc);
include/net/sock.h-2453- sk_mem_charge(sk, skb->truesize);
]
|
| 25/2 |
2026/05/31 19:18 |
llm |
expert |
0m
Model:
Tokens:
input: 19525
output: 57
thoughts: 264
Thoughts: **Analyzing a Kernel Crash**
Okay, here's what's running through my mind as I dig into this kernel crash. I see this `#define` β `sk_rmem_alloc` is just an alias for `sk_backlog.rmem_alloc`. I know from experience that `sk_backlog` is offset at 2560 bits, which is 320 bytes, or 0x140 in hex. So, that means `sk_backlog.rmem_alloc` sits right at offset 0x140 within the `struct sock`. The crash log points to a 4-byte write at address 0x140. That's immediately triggering a red flag β a write at that offset, means `sk_atm(vcc)` must be NULL!
Wait a second, let me think. `sk_atm(vcc)` is really just `(struct sock *)vcc`. That solidifies it: the value of `vcc` is NULL, which is why we have a crash. Alright, let's step back and look at the `send_to_lecd` function.
```c
if (!priv || !priv->lecd)
return -1;
...
atm_force_charge(priv->lecd, skb->truesize);
```
Okay, I see that the function first checks if `!priv || !priv->lecd`. But then, later down the line, we call `atm_force_charge` with `priv->lecd`. Now, if `priv->lecd` is not NULL at the start, how can it be NULL when we get to `atm_force_charge`? Could there be a data race here? Because `priv->lecd` is being accessed without any locks, I need to check where `priv->lecd` is being modified. I should check how it is used in the other part of the code. Thatβs where the key will be.
|
| 26/2 |
2026/05/31 19:18 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/atm/lec.c IncludeSnippetLines:0 Name:lec_priv::lecd SourceTreePrefix:net/atm/lec.c]
Results: map[References:[map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:lec_atm_close SourceFile:net/atm/lec.c SourceLine:475] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:lec_atm_send SourceFile:net/atm/lec.c SourceLine:452] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:lec_atm_send SourceFile:net/atm/lec.c SourceLine:453] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:lec_handle_bridge SourceFile:net/atm/lec.c SourceLine:157] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:lec_handle_bridge SourceFile:net/atm/lec.c SourceLine:158] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:lec_push SourceFile:net/atm/lec.c SourceLine:621] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:lec_start_xmit SourceFile:net/atm/lec.c SourceLine:219] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:lecd_attach SourceFile:net/atm/lec.c SourceLine:756] map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:lecd_attach SourceFile:net/atm/lec.c SourceLine:761] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:send_to_lecd SourceFile:net/atm/lec.c SourceLine:517] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:send_to_lecd SourceFile:net/atm/lec.c SourceLine:535] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:send_to_lecd SourceFile:net/atm/lec.c SourceLine:536] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:send_to_lecd SourceFile:net/atm/lec.c SourceLine:542]] TruncatedOutput:false]
|
| 27/2 |
2026/05/31 19:18 |
llm |
expert |
0m
Model:
Tokens:
input: 20937
output: 71
thoughts: 19
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in the field and considering how I'd approach it:
**Analyzing `lec_atm_close` and `lecd_attach`**
Alright, my brain just went straight to those two functions, `lec_atm_close` and `lecd_attach`. Knowing the likely context, I'm guessing these are part of a larger system or process related to something like atmospheric modeling, computational fluid dynamics, or perhaps even a more specialized application. The naming convention suggests they're linked and probably part of a structured approach, so my first move is to quickly pull up the relevant documentation or source code for both.
My initial line of inquiry would revolve around their relationships and roles:
* What is the overall architecture or process these functions fit into? I'm already mentally forming a picture of how data might be handled.
* `lec_atm_close`: This likely handles the "closing" or cleanup phase, perhaps after an atmospheric simulation or data transfer. What resources are being freed? Are there specific error conditions or states being handled during the closing? Understanding the specific resources (memory, file handles, etc.) involved is crucial.
* `lecd_attach`: This has the "attach" element, so it likely links something. Is it linking to a data structure, a shared memory region, or maybe another process or thread? I want to know exactly what is being attached, and what considerations are made around concurrency or synchronization, if any.
* Are there common data structures or variables used by both, which might indicate a shared state or communication mechanism?
* What is the error handling like in each function? It's key to identify any potential failure points.
Essentially, I'm trying to map out a clear process flow and figure out how they interact with the overall system state. I'm looking for potential bottlenecks, race conditions, or areas where the system could be optimized or where it could fail unexpectedly. Knowing those functions, their purposes and the larger system would be key.
|
| 28/2 |
2026/05/31 19:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/lec.c Name:lec_atm_close]
Results: map[SourceCode: 469: static void lec_atm_close(struct atm_vcc *vcc)
470: {
471: struct sk_buff *skb;
472: struct net_device *dev = (struct net_device *)vcc->proto_data;
473: struct lec_priv *priv = netdev_priv(dev);
474:
475: priv->lecd = NULL;
476: /* Do something needful? */
477:
478: netif_stop_queue(dev);
479: lec_arp_destroy(priv);
480:
481: if (skb_peek(&sk_atm(vcc)->sk_receive_queue))
482: pr_info("%s closing with messages pending\n", dev->name);
483: while ((skb = skb_dequeue(&sk_atm(vcc)->sk_receive_queue))) {
484: atm_return(vcc, skb->truesize);
485: dev_kfree_skb(skb);
486: }
487:
488: pr_info("%s: Shut down!\n", dev->name);
489: module_put(THIS_MODULE);
490: }
SourceFile:net/atm/lec.c]
|
| 29/2 |
2026/05/31 19:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/lec.c Name:lecd_attach]
Results: map[SourceCode: 725: /* Initialize device. */
726: static int lecd_attach(struct atm_vcc *vcc, int arg)
727: {
728: int i;
729: struct lec_priv *priv;
730:
731: lockdep_assert_held(&lec_mutex);
732: if (arg < 0)
733: arg = 0;
734: if (arg >= MAX_LEC_ITF)
735: return -EINVAL;
736: i = array_index_nospec(arg, MAX_LEC_ITF);
737: if (!dev_lec[i]) {
738: int size;
739:
740: size = sizeof(struct lec_priv);
741: dev_lec[i] = alloc_etherdev(size);
742: if (!dev_lec[i])
743: return -ENOMEM;
744: dev_lec[i]->netdev_ops = &lec_netdev_ops;
745: dev_lec[i]->max_mtu = 18190;
746: snprintf(dev_lec[i]->name, IFNAMSIZ, "lec%d", i);
747: if (register_netdev(dev_lec[i])) {
748: free_netdev(dev_lec[i]);
749: dev_lec[i] = NULL;
750: return -EINVAL;
751: }
752:
753: priv = netdev_priv(dev_lec[i]);
754: } else {
755: priv = netdev_priv(dev_lec[i]);
756: if (priv->lecd)
757: return -EADDRINUSE;
758: }
759: lec_arp_init(priv);
760: priv->itfnum = i; /* LANE2 addition */
761: priv->lecd = vcc;
762: vcc->dev = &lecatm_dev;
763: vcc_insert_socket(sk_atm(vcc));
764:
765: vcc->proto_data = dev_lec[i];
766: set_bit(ATM_VF_META, &vcc->flags);
767: set_bit(ATM_VF_READY, &vcc->flags);
768:
769: /* Set default values to these variables */
770: priv->maximum_unknown_frame_count = 1;
771: priv->max_unknown_frame_time = (1 * HZ);
772: priv->vcc_timeout_period = (1200 * HZ);
773: priv->max_retry_count = 1;
774: priv->aging_time = (300 * HZ);
775: priv->forward_delay_time = (15 * HZ);
776: priv->topology_change = 0;
777: priv->arp_response_time = (1 * HZ);
778: priv->flush_timeout = (4 * HZ);
779: priv->path_switching_delay = (6 * HZ);
780:
781: if (dev_lec[i]->flags & IFF_UP)
782: netif_start_queue(dev_lec[i]);
783: __module_get(THIS_MODULE);
784: return i;
785: }
SourceFile:net/atm/lec.c]
|
| 30/2 |
2026/05/31 19:18 |
llm |
expert |
0m
Model:
Tokens:
input: 22872
output: 56
thoughts: 159
Thoughts: **Concurrent Access Concerns: A Deep Dive**
Okay, so I'm looking at this code and immediately I see a potential race condition. `lec_atm_close` is setting `priv->lecd` to `NULL`. That's the key. And then, `send_to_lecd` comes along and does a null check on `priv->lecd` before attempting to use it with `atm_force_charge`.
The problem? Concurrency. If `lec_atm_close` runs *concurrently* with `send_to_lecd`, the null check in `send_to_lecd` could pass *just fine*, but then, *between* the null check and the call to `atm_force_charge`, `lec_atm_close` could null out `priv->lecd`. Boom! Use-after-free, undefined behavior, and probably a kernel panic right in our faces. Not good.
Wait a second, though... does `lec_atm_close` actually *free* `vcc`? Because if it just sets `priv->lecd` to `NULL` but doesn't release the underlying resources, the potential for a double-free situation is a major concern. I need to trace this function back to its callers. Where is `lec_atm_close` being called from? I need to understand the control flow here to definitively assess the potential impact of this race. This warrants a detailed code inspection, ideally with the help of a dynamic analysis tool.
|
| 31/2 |
2026/05/31 19:18 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/atm/lec.c IncludeSnippetLines:0 Name:lec_atm_close SourceTreePrefix:net/atm/lec.c]
Results: map[References:<nil> TruncatedOutput:false]
|
| 32/2 |
2026/05/31 19:18 |
llm |
expert |
1m
Model:
Tokens:
input: 23428
output: 32
thoughts: 19
Thoughts: Okay, here's my summary, presented as a direct reflection of my thought process:
**Static Function Investigation**
Alright, hang on a second. I'm looking at `lec_atm_close` and my brain immediately flags it as a *static* function. That's a red flag, right? It's not the usual behavior for something that's likely part of a broader system. So my immediate thought is: where the heck is this thing actually *used*? A static function's scope is incredibly limited, typically within a single translation unit or file, but its impact can be felt everywhere. I need to trace its references to understand its purpose and, potentially, any dependencies it creates. Let's dig in and figure out where this function is called and what it's doing.
|
| 33/2 |
2026/05/31 19:19 |
tool |
grepper |
0m
Args: map[Expression:lec_atm_close PathPrefix:net/atm/lec.c]
Results: map[Output:net/atm/lec.c=343=static int lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
--
net/atm/lec.c-468-
net/atm/lec.c:469:static void lec_atm_close(struct atm_vcc *vcc)
net/atm/lec.c-470-{
--
net/atm/lec.c=492=static const struct atmdev_ops lecdev_ops = {
net/atm/lec.c:493: .close = lec_atm_close,
net/atm/lec.c-494- .send = lec_atm_send
]
|
| 34/2 |
2026/05/31 19:19 |
llm |
expert |
0m
Model:
Tokens:
input: 23829
output: 36
thoughts: 697
Thoughts: **Analyzing a Potential Data Race in `lecdev_ops.close`**
Okay, so I'm looking at the `.close` callback of `lecdev_ops`. This is part of how `lecatm_dev` is structured, and it's used when an ATM socket closes. Specifically, `vcc_release` calls `vcc->dev->ops->close(vcc)`.
My initial concern is this: if `priv->lecd` is set to NULL, does the `send_to_lecd` function try to reload `priv->lecd`? I know that if `!priv || !priv->lecd` in `send_to_lecd`, it returns, so that's a check, but then the code uses it, so, could it be reloaded from memory? The compiler might cache it, so then it wouldn't be NULL; however, if the compiler reloads it, it *could* be NULL!
Then I thought, even if `priv->lecd` isn't NULL, if `vcc` is freed, it would be a use-after-free, right? But here, it's a null-ptr-deref, which means `priv->lecd` was reloaded and was NULL.
So, I should check the assembly for `send_to_lecd`, or just assume there's a data race. My first thought is: is `send_to_lecd` called with any locks held? `send_to_lecd` is called from `lec_arp_resolve`, which does hold a spinlock: `spin_lock_irqsave(&priv->lec_arp_lock, flags);`.
Now, does `lec_atm_close` hold `lec_arp_lock`? If `lec_atm_close` doesn't hold the lock, there's the possibility of a data race when it sets `priv->lecd = NULL`. And, it doesn't! So there's definitely a data race potential.
Wait, what about `lec_arp_destroy`? Does *it* hold the lock? I should check that.
|
| 35/2 |
2026/05/31 19:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/lec.c Name:lec_arp_destroy]
Results: map[SourceCode:1469: /*
1470: * Destruction of arp-cache
1471: */
1472: static void lec_arp_destroy(struct lec_priv *priv)
1473: {
1474: unsigned long flags;
1475: struct hlist_node *next;
1476: struct lec_arp_table *entry;
1477: int i;
1478:
1479: cancel_delayed_work_sync(&priv->lec_arp_work);
1480:
1481: /*
1482: * Remove all entries
1483: */
1484:
1485: spin_lock_irqsave(&priv->lec_arp_lock, flags);
1486: for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
1487: hlist_for_each_entry_safe(entry, next,
1488: &priv->lec_arp_tables[i], next) {
1489: lec_arp_remove(priv, entry);
1490: lec_arp_put(entry);
1491: }
1492: INIT_HLIST_HEAD(&priv->lec_arp_tables[i]);
1493: }
1494:
1495: hlist_for_each_entry_safe(entry, next,
1496: &priv->lec_arp_empty_ones, next) {
1497: timer_delete_sync(&entry->timer);
1498: lec_arp_clear_vccs(entry);
1499: hlist_del(&entry->next);
1500: lec_arp_put(entry);
1501: }
1502: INIT_HLIST_HEAD(&priv->lec_arp_empty_ones);
1503:
1504: hlist_for_each_entry_safe(entry, next,
1505: &priv->lec_no_forward, next) {
1506: timer_delete_sync(&entry->timer);
1507: lec_arp_clear_vccs(entry);
1508: hlist_del(&entry->next);
1509: lec_arp_put(entry);
1510: }
1511: INIT_HLIST_HEAD(&priv->lec_no_forward);
1512:
1513: hlist_for_each_entry_safe(entry, next, &priv->mcast_fwds, next) {
1514: /* No timer, LANEv2 7.1.20 and 2.3.5.3 */
1515: lec_arp_clear_vccs(entry);
1516: hlist_del(&entry->next);
1517: lec_arp_put(entry);
1518: }
1519: INIT_HLIST_HEAD(&priv->mcast_fwds);
1520: priv->mcast_vcc = NULL;
1521: spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
1522: }
SourceFile:net/atm/lec.c]
|
| 36/2 |
2026/05/31 19:19 |
llm |
expert |
0m
Model:
Tokens:
input: 25902
output: 55
thoughts: 560
Thoughts: **My Analysis of a Potential NULL Pointer Dereference in ATM LEC Code**
Okay, so I've been digging into this ATM LEC code, and I think I've found something. I'm seeing that `lec_arp_destroy` acquires `lec_arp_lock`, but `priv->lecd` is set to `NULL` *before* that. This means `send_to_lecd` could be running concurrently with `priv->lecd` becoming `NULL`.
The problematic function, `send_to_lecd`, does a check like: `if (!priv || !priv->lecd) return -1;`, but then later uses `priv->lecd` in `atm_force_charge`. The *critical* thing is that `atm_force_charge` *could* reload `priv->lecd` from memory. If it *does* reload it, and it's now NULL, we have a problem.
I see it now - if `priv->lecd` is indeed NULL when `atm_force_charge` runs, it calls `atomic_add` on what it thinks is `sk_atm(vcc)->sk_rmem_alloc`. However, `sk_atm(vcc)` is just a `(struct sock *)vcc`. This is key. If `vcc` is NULL at this point, `&sk_atm(vcc)->sk_rmem_alloc` becomes `&((struct sock *)NULL)->sk_rmem_alloc`. We know `sk_rmem_alloc` is at offset 0x140 in `sock`.
So, it's writing a value (around 256 bytes) to address `0x140`. That's a tiny address, and therefore unmapped in user space due to `mmap_min_addr`. It's a NULL pointer dereference, plain and simple! This is going to cause a kernel panic because the kernel will try to access the unmapped memory.
The issue is that it's *not* exploitable for privilege escalation or memory corruption. It's *just* a Denial of Service. It looks like it would crash the system.
Now, the question is, can an unprivileged user trigger this? I need to check how ATM sockets are used in `net/atm/lec.c`, particularly how `lecd_attach` is called. That's the next step to figure out the full impact of this.
|
| 37/2 |
2026/05/31 19:19 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/atm/lec.c IncludeSnippetLines:0 Name:lecd_attach SourceTreePrefix:net/atm/lec.c]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lane_ioctl SourceFile:net/atm/lec.c SourceLine:1015]] TruncatedOutput:false]
|
| 38/2 |
2026/05/31 19:19 |
llm |
expert |
0m
Model:
Tokens:
input: 27114
output: 34
thoughts: 14
Thoughts: Okay, here's my interpretation of that.
**My Analysis of the `lecd_attach` Call**
Alright, so the clue is that `lecd_attach` is called from `lane_ioctl`. The relationship is instantly clear. `lane_ioctl` is the entry point for ioctl requests related to a particular "lane" abstraction β whatever that specifically means in this context, I can infer it's likely a hardware or logical component of a communications channel or data path. The fact that `lecd_attach` is called from *within* `lane_ioctl` tells me that `lecd_attach` is almost certainly involved in attaching or configuring something specific *for* or *to* a given lane based on an ioctl command. It's likely involved with the initialization or setup of something.
Given my understanding of these types of systems, `lecd_attach` is probably responsible for hooking up a low-level entity (`lecd`) to the more abstract "lane" representation. It could be allocating resources, establishing a mapping between a hardware resource and a logical lane, or perhaps registering a callback. The "attach" part of the name is the key. The details of what it's attaching, and why, would be clarified by further analysis, but based on the immediate context, I can place it in a particular functional class.
|
| 39/2 |
2026/05/31 19:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/lec.c Name:lane_ioctl]
Results: map[SourceCode: 996: static int lane_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
997: {
998: struct atm_vcc *vcc = ATM_SD(sock);
999: int err = 0;
1000:
1001: switch (cmd) {
1002: case ATMLEC_CTRL:
1003: case ATMLEC_MCAST:
1004: case ATMLEC_DATA:
1005: if (!capable(CAP_NET_ADMIN))
1006: return -EPERM;
1007: break;
1008: default:
1009: return -ENOIOCTLCMD;
1010: }
1011:
1012: mutex_lock(&lec_mutex);
1013: switch (cmd) {
1014: case ATMLEC_CTRL:
1015: err = lecd_attach(vcc, (int)arg);
1016: if (err >= 0)
1017: sock->state = SS_CONNECTED;
1018: break;
1019: case ATMLEC_MCAST:
1020: err = lec_mcast_attach(vcc, (int)arg);
1021: break;
1022: case ATMLEC_DATA:
1023: err = lec_vcc_attach(vcc, (void __user *)arg);
1024: break;
1025: }
1026:
1027: mutex_unlock(&lec_mutex);
1028: return err;
1029: }
SourceFile:net/atm/lec.c]
|
| 40/2 |
2026/05/31 19:19 |
llm |
expert |
0m
Model:
Tokens:
input: 27926
output: 30
thoughts: 332
Thoughts: **Analyzing the Security Implications of `lane_ioctl`**
Okay, so I'm digging into this `lane_ioctl` function. The first thing that jumps out is that it checks for `CAP_NET_ADMIN` capability. Specifically, that line of code:
```c
switch (cmd) {
case ATMLEC_CTRL:
case ATMLEC_MCAST:
case ATMLEC_DATA:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
break;
```
That's pretty straightforward. It's using `capable()`, not `ns_capable()`. That means it's checking the capability of the initial user namespace, which is important. This means an unprivileged user, *even inside a user namespace*, can't trigger this vulnerability. Thatβs a good starting point. I don't see an immediate vector for exploitation, which is often the best result.
But hold on, I need to be thorough. I need to be sure there's *no other* way to call `send_to_lecd`. Let's map out the call paths.
`send_to_lecd` is called from a handful of places: `lec_arp_resolve`, `lec_arp_check_empties`, `lec_mcast_make`, `lec_vcc_close`, and `lec_vcc_added`. These all look like they're part of the `lec` (LAN Emulation) driver. Okay, so how do you even *create* a `lec` device? I need to understand that.
Right, I remember now. You create a `lec` device via `lecd_attach`, and that's done through the `ATMLEC_CTRL` ioctl on an ATM socket. And that ioctl... requires `CAP_NET_ADMIN`. So that's the only way to get a LEC device. No surprises there.
But... is there *another* way? I have to be sure. Could I create a `lec` device via netlink (rtnl)? I should look into that next.
|
| 41/2 |
2026/05/31 19:19 |
tool |
grepper |
0m
Error:
Args: map[Expression:lec_setup PathPrefix:net/atm/lec.c]
Results:
|
| 42/2 |
2026/05/31 19:20 |
llm |
expert |
0m
Model:
Tokens:
input: 28761
output: 33
thoughts: 22
Thoughts: Here's my thought process, as if I were considering this code:
**Diving into the Network ATM Landscape**
Okay, so we're talking `rtnl_link_ops` β that means we're dealing with the network link operations, right? Specifically, I'm thinking about how the system interacts with network interfaces from the perspective of the route netlink. The context here is ATM. Given the age of ATM, it's not the first place I'd look in contemporary codebases, but itβs still good practice to be able to navigate it. Therefore I need to figure out where the ATM-specific implementations of these operations reside. Where else would this be implemented? Where else would one find this? Time to check `net/atm/lec.c`. That's likely where the ATM LAN Emulation (LEC) code, and its related link configuration, lives. I'm expecting to find the function pointers for things like setting link attributes, getting link statistics, maybe even some specific setup for the ATM virtual circuit handling. Let's see whatβs there. This should be a good starting point for understanding how ATM interfaces are managed within this particular kernel version.
|
| 43/2 |
2026/05/31 19:20 |
tool |
grepper |
0m
Error:
Args: map[Expression:rtnl_link_ops PathPrefix:net/atm/lec.c]
Results:
|
| 44/2 |
2026/05/31 19:20 |
llm |
expert |
0m
Model:
Tokens:
input: 29079
output: 32
thoughts: 467
Thoughts: **Analyzing the LEC Device Vulnerability**
Okay, so the initial thought is that `lec` devices are *only* created via the `ATMLEC_CTRL` ioctl, not netlink. That's the baseline assumption. But, what if the administrator already set one up? That changes things. Could an unprivileged user exploit the issue then? The vulnerability is triggered by sending a packet through the `lec` device. Specifically, `lec_start_xmit` is the `ndo_start_xmit` function for the `lec` device, and an unprivileged user can send packets through the device if itβs up and has an IP address, by using raw sockets (assuming `CAP_NET_RAW`), or just by using normal UDP/TCP traffic.
If the device is up, regular traffic will make its way through `lec_start_xmit`. That eventually leads to `lec_arp_resolve`, and then to `send_to_lecd`. The critical part, though, is that to trigger the race condition, the ATM socket associated with the `lec` device has to be *closed concurrently*. That socket is owned by the process that originally called `ATMLEC_CTRL`, usually a daemon like `zeus` or `lecd`. When the daemon closes the socket, `lec_atm_close` is called.
The next question: Can an unprivileged user force the daemon to close the socket? Probably not directly. But, a daemon *might* close the socket if it runs into an error, or the administrator could kill the daemon. However, the bug report mentioned that the crash occurred in `dhcpcd`, which is a DHCP client. That throws a wrench in the typical daemon assumption.
`dhcpcd` was sending a packet, probably a DHCP discover broadcast. The packet went through `lec_start_xmit`. *At the same time*, the ATM socket was being closed. This gets tricky because the syzkaller reproducer probably created the `lec` device itself, because syzkaller runs as root or has `CAP_NET_ADMIN`. But wait! syzkaller usually runs in a user namespace. If that's the case, then it *has* `CAP_NET_ADMIN` in that user namespace, but `lane_ioctl` uses `capable(CAP_NET_ADMIN)`, which checks the initial user namespace. This needs to be verified.
|
| 45/2 |
2026/05/31 19:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/capability.h Name:capable]
Results: map[SourceCode: 404: /**
405: * capable - Determine if the current task has a superior capability in effect
406: * @cap: The capability to be tested for
407: *
408: * Return true if the current task has the given superior capability currently
409: * available for use, false if not.
410: *
411: * This sets PF_SUPERPRIV on the task if the capability is available on the
412: * assumption that it's about to be used.
413: */
414: bool capable(int cap)
415: {
416: return ns_capable(&init_user_ns, cap);
417: }
SourceFile:kernel/capability.c]
|
| 46/2 |
2026/05/31 19:20 |
llm |
expert |
0m
Model:
Tokens:
input: 30310
output: 52
thoughts: 101
Thoughts: **Analyzing the Root Cause**
Okay, so I'm thinking about this `capable(CAP_NET_ADMIN)` check related to `init_user_ns`. Right, the implication is that syzkaller must have been running with root privileges within the initial user namespace to even *trigger* this vulnerability. That makes sense, but now I'm hitting a snag. If syzkaller is running as root *in* the initial user namespace, then it shouldn't be accessible from unprivileged processes or any created user namespaces, right? That setup seems problematic, I need to make sure my understanding of this is correct.
I need to confirm that that's the only way to reach `lecd_attach`. Is `lane_ioctl` the only function that calls it? I need to double-check there isn't some obscure path I'm missing.
|
| 47/2 |
2026/05/31 19:20 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/atm/lec.c IncludeSnippetLines:0 Name:lecd_attach SourceTreePrefix:net/atm/]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lane_ioctl SourceFile:net/atm/lec.c SourceLine:1015]] TruncatedOutput:false]
|
| 48/2 |
2026/05/31 19:20 |
llm |
expert |
0m
Model:
Tokens:
input: 30712
output: 51
thoughts: 23
Thoughts: Here's my take on the situation:
**Investigating the `lane_ioctl` Caller**
Okay, so I'm looking at this code, and it seems like `lane_ioctl` is the sole function calling whatever we're examining. That's good; it simplifies things. We can focus our attention on understanding how `lane_ioctl` itself is functioning. Now, I need to check how itβs being registered within the system to truly understand its impact. Knowing where it's hooked in is key to tracing its behaviour.
|
| 49/2 |
2026/05/31 19:20 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/atm/lec.c IncludeSnippetLines:0 Name:lane_ioctl SourceTreePrefix:net/atm/]
Results: map[References:<nil> TruncatedOutput:false]
|
| 50/2 |
2026/05/31 19:20 |
llm |
expert |
0m
Model:
Tokens:
input: 30918
output: 30
thoughts: 17
Thoughts: Here's my thought process, summarized as you requested:
**Debugging Static Function Usage**
Okay, so I'm looking at `lane_ioctl` and immediately my brain flags it as a `static` function. That's a red flag. A `static` function's scope is limited to the file it's defined in. This immediately makes me wonder... where the hell is this thing actually *being* called? If it's `static`, and seemingly unused within the immediate context, I need to understand its relationship to the broader system. I need to trace its usage to understand its purpose. This is step one for me. Without knowing where it is utilized, any analysis is premature.
|
| 51/2 |
2026/05/31 19:20 |
tool |
grepper |
0m
Args: map[Expression:lane_ioctl PathPrefix:net/atm/lec.c]
Results: map[Output:net/atm/lec.c=988=static const struct seq_operations lec_seq_ops = {
--
net/atm/lec.c-995-
net/atm/lec.c:996:static int lane_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
net/atm/lec.c-997-{
--
net/atm/lec.c-1030-
net/atm/lec.c:1031:static struct atm_ioctl lane_ioctl_ops = {
net/atm/lec.c-1032- .owner = THIS_MODULE,
net/atm/lec.c:1033: .ioctl = lane_ioctl,
net/atm/lec.c-1034-};
--
net/atm/lec.c=1036=static int __init lane_module_init(void)
--
net/atm/lec.c-1048-
net/atm/lec.c:1049: register_atm_ioctl(&lane_ioctl_ops);
net/atm/lec.c-1050- pr_info("lec.c: initialized\n");
--
net/atm/lec.c=1054=static void __exit lane_module_cleanup(void)
--
net/atm/lec.c-1061-
net/atm/lec.c:1062: deregister_atm_ioctl(&lane_ioctl_ops);
net/atm/lec.c-1063-
]
|
| 52/2 |
2026/05/31 19:20 |
llm |
expert |
0m
Model:
Tokens:
input: 31495
output: 35
thoughts: 44
Thoughts: Okay, here's my interpretation of those thoughts, presented as my own mental processing as an expert in the field:
**Internal ATM IOCTL Analysis**
Alright, let's see, I'm trying to trace the flow of ATM control operations within the kernel. The first thing I note is that this functionality is registered via `register_atm_ioctl`. That's the entry point, the hook. Now, the key question is, *how* is this IOCTL actually invoked? Ah, here we go, it appears the `lane_ioctl` function, which I suspect handles LANE-specific commands, is triggered from within `vcc_ioctl`. And where is `vcc_ioctl` defined? That's right, it's in `net/atm/ioctl.c`. So the execution path is `vcc_ioctl` in `ioctl.c` calls `lane_ioctl`. Got it. Now I need to understand what specific requests are being handled in that area.
|
| 53/2 |
2026/05/31 19:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/ioctl.c Name:vcc_ioctl]
Results: map[SourceCode: 218: int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
219: {
220: return do_vcc_ioctl(sock, cmd, arg, 0);
221: }
SourceFile:net/atm/ioctl.c]
|
| 54/2 |
2026/05/31 19:20 |
llm |
expert |
0m
Model:
Tokens:
input: 31878
output: 37
thoughts: 12
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person:
**Diving into `do_vcc_ioctl`**
Alright, time to get my hands dirty. Let's see what `do_vcc_ioctl` is doing. I need to refresh my memory on the specific context here β what's the broader picture of this Virtual Channel Controller? What's the typical flow that leads to this function call? I'm already anticipating potential areas for investigation: error handling, resource management, and, of course, the actual data transfer mechanisms. Are there any known issues associated with this ioctl handler that I need to keep in mind? I need to quickly identify the potential vulnerabilities, any race conditions, or performance bottlenecks, and map them to their potential causes. Time to step in.
|
| 55/2 |
2026/05/31 19:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/ioctl.c Name:do_vcc_ioctl]
Results: map[SourceCode: 51: static int do_vcc_ioctl(struct socket *sock, unsigned int cmd,
52: unsigned long arg, int compat)
53: {
54: struct sock *sk = sock->sk;
55: struct atm_vcc *vcc;
56: int error;
57: struct list_head *pos;
58: void __user *argp = (void __user *)arg;
59: void __user *buf;
60: int __user *len;
61:
62: vcc = ATM_SD(sock);
63: switch (cmd) {
64: case SIOCOUTQ:
65: if (sock->state != SS_CONNECTED ||
66: !test_bit(ATM_VF_READY, &vcc->flags)) {
67: error = -EINVAL;
68: goto done;
69: }
70: error = put_user(sk->sk_sndbuf - sk_wmem_alloc_get(sk),
71: (int __user *)argp);
72: goto done;
73: case SIOCINQ:
74: {
75: struct sk_buff *skb;
76: int amount;
77:
78: if (sock->state != SS_CONNECTED) {
79: error = -EINVAL;
80: goto done;
81: }
82: spin_lock_irq(&sk->sk_receive_queue.lock);
83: skb = skb_peek(&sk->sk_receive_queue);
84: amount = skb ? skb->len : 0;
85: spin_unlock_irq(&sk->sk_receive_queue.lock);
86: error = put_user(amount, (int __user *)argp);
87: goto done;
88: }
89: case ATM_SETSC:
90: net_warn_ratelimited("ATM_SETSC is obsolete; used by %s:%d\n",
91: current->comm, task_pid_nr(current));
92: error = 0;
93: goto done;
94: case ATMSIGD_CTRL:
95: if (!capable(CAP_NET_ADMIN)) {
96: error = -EPERM;
97: goto done;
98: }
99: /*
100: * The user/kernel protocol for exchanging signalling
101: * info uses kernel pointers as opaque references,
102: * so the holder of the file descriptor can scribble
103: * on the kernel... so we should make sure that we
104: * have the same privileges that /proc/kcore needs
105: */
106: if (!capable(CAP_SYS_RAWIO)) {
107: error = -EPERM;
108: goto done;
109: }
110: #ifdef CONFIG_COMPAT
111: /* WTF? I don't even want to _think_ about making this
112: work for 32-bit userspace. TBH I don't really want
113: to think about it at all. dwmw2. */
114: if (compat) {
115: net_warn_ratelimited("32-bit task cannot be atmsigd\n");
116: error = -EINVAL;
117: goto done;
118: }
119: #endif
120: error = sigd_attach(vcc);
121: if (!error)
122: sock->state = SS_CONNECTED;
123: goto done;
124: case ATM_SETBACKEND:
125: case ATM_NEWBACKENDIF:
126: {
127: atm_backend_t backend;
128: error = get_user(backend, (atm_backend_t __user *)argp);
129: if (error)
130: goto done;
131: switch (backend) {
132: case ATM_BACKEND_PPP:
133: request_module("pppoatm");
134: break;
135: case ATM_BACKEND_BR2684:
136: request_module("br2684");
137: break;
138: }
139: break;
140: }
141: case ATMMPC_CTRL:
142: case ATMMPC_DATA:
143: request_module("mpoa");
144: break;
145: case ATMARPD_CTRL:
146: request_module("clip");
147: break;
148: case ATMLEC_CTRL:
149: request_module("lec");
150: break;
151: }
152:
153: error = -ENOIOCTLCMD;
154:
155: mutex_lock(&ioctl_mutex);
156: list_for_each(pos, &ioctl_list) {
157: struct atm_ioctl *ic = list_entry(pos, struct atm_ioctl, list);
158: if (try_module_get(ic->owner)) {
159: error = ic->ioctl(sock, cmd, arg);
160: module_put(ic->owner);
161: if (error != -ENOIOCTLCMD)
162: break;
163: }
164: }
165: mutex_unlock(&ioctl_mutex);
166:
167: if (error != -ENOIOCTLCMD)
168: goto done;
169:
170: if (cmd == ATM_GETNAMES) {
171: if (IS_ENABLED(CONFIG_COMPAT) && compat) {
172: #ifdef CONFIG_COMPAT
173: struct compat_atm_iobuf __user *ciobuf = argp;
174: compat_uptr_t cbuf;
175: len = &ciobuf->length;
176: if (get_user(cbuf, &ciobuf->buffer))
177: return -EFAULT;
178: buf = compat_ptr(cbuf);
179: #endif
180: } else {
181: struct atm_iobuf __user *iobuf = argp;
182: len = &iobuf->length;
183: if (get_user(buf, &iobuf->buffer))
184: return -EFAULT;
185: }
186: error = atm_getnames(buf, len);
187: } else {
188: int number;
189:
190: if (IS_ENABLED(CONFIG_COMPAT) && compat) {
191: #ifdef CONFIG_COMPAT
192: struct compat_atmif_sioc __user *csioc = argp;
193: compat_uptr_t carg;
194:
195: len = &csioc->length;
196: if (get_user(carg, &csioc->arg))
197: return -EFAULT;
198: buf = compat_ptr(carg);
199: if (get_user(number, &csioc->number))
200: return -EFAULT;
201: #endif
202: } else {
203: struct atmif_sioc __user *sioc = argp;
204:
205: len = &sioc->length;
206: if (get_user(buf, &sioc->arg))
207: return -EFAULT;
208: if (get_user(number, &sioc->number))
209: return -EFAULT;
210: }
211: error = atm_dev_ioctl(cmd, buf, len, number, compat);
212: }
213:
214: done:
215: return error;
216: }
SourceFile:net/atm/ioctl.c]
|
| 56/2 |
2026/05/31 19:20 |
llm |
expert |
0m
Model:
Tokens:
input: 34442
output: 53
thoughts: 841
Thoughts: **Analyzing a Potential NULL Pointer Dereference in ATM LEC**
Okay, let's break this down. My initial focus is on the `do_vcc_ioctl` function. It seems to iterate through an `ioctl_list` and calls `ic->ioctl()`. `lane_ioctl` is one of these, and the code indicates that specific `ATMLEC_CTRL`, `ATMLEC_MCAST`, and `ATMLEC_DATA` ioctl commands require `CAP_NET_ADMIN` in the initial user namespace. That's a first layer of defense.
I'm trying to figure out if there's any way for an unprivileged user to get involved with `lecd_attach`. It looks like `lecd_attach` is statically defined within `net/atm/lec.c`, which makes it a safe assumption that a non-privileged user cannot create a `lec` device directly. Good.
However, the scenario shifts if the `lec` device is *already* created by an administrator. If that's the case, `lecd_attach` has already been called. The device is up, running, and accessible. An unprivileged user could then potentially send packets through it, perhaps UDP packets routed through the device. This activity would trigger `lec_start_xmit`.
Here's where the race condition starts to appear. Simultaneously, the administrator or the `lecd` daemon *might* close the ATM socket. This call to `lec_atm_close` is crucial because it sets `priv->lecd = NULL`. If an unprivileged user's packet is currently in the processing pipeline β `lec_start_xmit` -> `lec_arp_resolve` -> `send_to_lecd` β then a race condition exists. It can potentially dereference the now-NULL `priv->lecd`. I'm seeing a possible NULL pointer dereference. The unprivileged user *could* trigger this, *if* the administrator has already set up the device *and* the daemon closes the socket concurrently.
Can the unprivileged user force the daemon to close the socket? Probably not. The daemon likely closes it on exit or administrative shutdown. But, what if the unprivileged user floods the device with packets? If the administrator then decides to shut down the daemon, the kernel crashes. Would this be considered an "Accessible From Unprivileged Processes" DoS? Typically, yes, since an unprivileged user is actively participating in the triggering of the bug with a normal admin action occurring at the same time.
Wait a second. The unprivileged user doesn't even necessarily need to *send* packets. Normal network traffic might be enough to trigger it. If they intentionally flood it to win the race, that's definitely an exploit. But the race is between `send_to_lecd` and `lec_atm_close`. The latter is only called on socket closure. The unprivileged user can't directly trigger `lec_atm_close`; they can only influence `send_to_lecd`.
So, is this "Accessible From Unprivileged Processes?" Yes, because the user participates in triggering the bug. But they need the administrator to close the socket.
I'm thinking this through further. Can they really trigger the bug on demand? No, they have to wait for the admin to close the socket and *hope* to win the race.
Could the unprivileged user close the ATM socket themselves? No, it's owned by the `lecd` daemon, running as root.
What if they could *make* the ATM connection drop? If the connection drops, does the kernel automatically close the socket? ATM PVCs/SVCs might be closed when the remote end disconnects. Let me check if the ATM socket is closed when the connection is remotely disconnected. This is the next thing to investigate.
|
| 57/2 |
2026/05/31 19:20 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/atm/lec.c IncludeSnippetLines:0 Name:lec_atm_close SourceTreePrefix:net/atm/]
Results: map[References:<nil> TruncatedOutput:false]
|
| 58/2 |
2026/05/31 19:20 |
llm |
expert |
0m
Model:
Tokens:
input: 36165
output: 54
thoughts: 46
Thoughts: Here's my understanding of the situation, framed as my own thought process:
**Understanding the ATM Connection Closure**
Okay, so I'm looking at how an ATM connection gets closed, and it's pretty straightforward. `lec_atm_close` is the crucial function here, responsible for the actual closing operation. The key thing to note is that `lec_atm_close` is *only* ever invoked through the device's operational methods. Specifically, it's called via `vcc->dev->ops->close(vcc)`. This implies that the entire close procedure is tightly integrated with the underlying network device's driver. This is standard, good practice.
Now, where does that device driver `close` method get called? I trace it back to `vcc_release` within `net/atm/common.c`. This makes sense, as `vcc_release` seems to be the place where we manage the virtual channel context. So, when the virtual channel is being released, and its resources are being freed up, then the device-specific closing operation is triggered. This aligns with a clean resource management strategy within the ATM stack. It's a nicely encapsulated approach.
|
| 59/2 |
2026/05/31 19:20 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/atm/common.c IncludeSnippetLines:0 Name:atmdev_ops::close SourceTreePrefix:net/atm/]
Results: map[References:[map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:vcc_destroy_socket SourceFile:net/atm/common.c SourceLine:180] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:vcc_destroy_socket SourceFile:net/atm/common.c SourceLine:181]] TruncatedOutput:false]
|
| 60/2 |
2026/05/31 19:20 |
llm |
expert |
0m
Model:
Tokens:
input: 36637
output: 37
thoughts: 12
Thoughts: Here's my take on those thoughts:
**Analyzing `vcc_destroy_socket`**
Alright, let's dive into this `vcc_destroy_socket` function. My expert eye immediately goes to its purpose β presumably, it's responsible for properly cleaning up a socket that was previously established within the `vcc` context. That means I need to assess what resources are freed, how this cleanup interacts with the underlying socket library, and if there are any potential race conditions or resource leaks within the implementation. Does it properly handle errors? Does it consider the socket's state (connected, listening, etc.)? I'm particularly interested in how it prevents double-frees or attempts to destroy a socket thatβs already been invalidated. It also warrants consideration to how it interacts with the garbage collection of the `vcc` system. I'll need to check if there are any subtle memory management pitfalls or concurrency issues lurking within its implementation. Time to dig in!
|
| 61/2 |
2026/05/31 19:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/common.c Name:vcc_destroy_socket]
Results: map[SourceCode: 173: static void vcc_destroy_socket(struct sock *sk)
174: {
175: struct atm_vcc *vcc = atm_sk(sk);
176: struct sk_buff *skb;
177:
178: set_bit(ATM_VF_CLOSE, &vcc->flags);
179: clear_bit(ATM_VF_READY, &vcc->flags);
180: if (vcc->dev && vcc->dev->ops->close)
181: vcc->dev->ops->close(vcc);
182: if (vcc->push)
183: vcc->push(vcc, NULL); /* atmarpd has no push */
184: module_put(vcc->owner);
185:
186: while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
187: atm_return(vcc, skb->truesize);
188: kfree_skb(skb);
189: }
190:
191: if (vcc->dev && vcc->dev->ops->owner) {
192: module_put(vcc->dev->ops->owner);
193: atm_dev_put(vcc->dev);
194: }
195:
196: vcc_remove_socket(sk);
197: }
SourceFile:net/atm/common.c]
|
| 62/2 |
2026/05/31 19:20 |
llm |
expert |
0m
Model:
Tokens:
input: 37325
output: 35
thoughts: 17
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**My Current Deduction: Connecting `vcc_destroy_socket` and `vcc_release`**
Alright, let's unpack this. The core thought here is the relationship between `vcc_destroy_socket` and `vcc_release`. Thinking about the architecture, the fact that `vcc_destroy_socket` is *called from* `vcc_release` immediately tells me something about the resource management strategy within this component. It strongly suggests a cascading or hierarchical destruction model, likely related to the overall lifecycle of a "vcc" (whatever that abstraction represents).
My immediate assumption is that `vcc_release` is a function responsible for releasing the resources associated with a `vcc` instance. The presence of `vcc_destroy_socket` within it implies that the socket, whatever the nature of its association to the vcc, is a *critical* resource that must be cleaned up as part of the `vcc` release process. It's not a standalone cleanup; it's *part* of a larger, coordinated cleanup.
This could mean a few things:
* **Socket Ownership:** `vcc_release` likely has *ownership* of the socket or is responsible for its life-cycle. Destroying the socket is the final step for properly managing its associated file descriptors, buffer associations, and network resources.
* **Resource Dependencies:** The vcc's other resources and associated state may depend on the socket being valid.
* **Error Handling:** The implementation of the `vcc_release` function would need very careful error handling. If `vcc_destroy_socket` were to fail, the entire release might be put in a failed state.
* **Memory Management:** The nature of the socket's lifetime could influence memory management strategy. It might be socket-per-connection, or re-used in a pool. I need to dig deeper into the actual implementations to confirm.
* **Concurrency:** If there are multiple threads involved, I need to consider synchronization. Are there locks involved? If `vcc_release` is being called concurrently, the destruction of the socket must be thread-safe.
In summary, this small piece of information, "called from", paints a picture of controlled resource release. Now I need to see the implementations of both functions to confirm these inferences and understand the broader context.
|
| 63/2 |
2026/05/31 19:21 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/common.c Name:vcc_release]
Results: map[SourceCode: 199: int vcc_release(struct socket *sock)
200: {
201: struct sock *sk = sock->sk;
202:
203: if (sk) {
204: lock_sock(sk);
205: vcc_destroy_socket(sock->sk);
206: release_sock(sk);
207: sock_put(sk);
208: }
209:
210: return 0;
211: }
SourceFile:net/atm/common.c]
|
| 64/2 |
2026/05/31 19:21 |
llm |
expert |
0m
Model:
Tokens:
input: 38083
output: 54
thoughts: 400
Thoughts: **My Assessment of the ATM Socket Vulnerability**
Okay, so `vcc_release` is essentially the cleanup function for the ATM socket. This means it only gets called when the socket file descriptor is closed, or when the process using it exits. Therefore, and this is important, an unprivileged user *shouldn't* be able to trigger the core issue, `lec_atm_close`, because they can't kill the `lecd` daemon or force it to close the socket. `lecd` runs as root, so that's a hard stop.
But...wait. Syzkaller found it. Syzkaller runs with root privileges (or at least `CAP_NET_ADMIN` in the initial namespace, which is effectively the same). So, Syzkaller *can* trigger the issue because it can open the ATM socket, call `ATMLEC_CTRL`, and then close the socket while packets are being sent. That makes sense.
Now, let's break down the impact:
* **Exploitable?** No, itβs not really. It's a NULL pointer dereference, a classic kernel crash. It's not leading to remote code execution. Itβs just going to crash the kernel if it's triggered.
* **Denial of Service?** Yes, definitely. A kernel crash *is* a denial of service.
* **Accessible from Unprivileged Processes?** No, not directly. It requires `CAP_NET_ADMIN` in the initial user namespace to create the `lec` device and, crucially, to close the socket at the right time.
* **Accessible from User Namespaces?** Nope. `capable(CAP_NET_ADMIN)` is checked in the initial user namespace, so it's not a user namespace escape.
* **VM Guest Trigger?** No.
* **VM Host Trigger in the Confidential Computing Context?** No.
* **Ethernet Network Trigger?** Hmmm... the race condition relies on closing the ATM socket, which sounds local. But if the `lec` device *is* up, could receiving a packet from the network trigger `send_to_lecd` and indirectly cause the problem? That's the part to look at. We need to check if `send_to_lecd` is even called on the receive path.
|
| 65/2 |
2026/05/31 19:21 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/atm/lec.c IncludeSnippetLines:0 Name:send_to_lecd SourceTreePrefix:net/atm/]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__lec_arp_check_expire SourceFile:net/atm/lec.c SourceLine:1635] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lane2_associate_req SourceFile:net/atm/lec.c SourceLine:1151] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lane2_resolve SourceFile:net/atm/lec.c SourceLine:1109] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lane2_resolve SourceFile:net/atm/lec.c SourceLine:1117] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lec_arp_expire_arp SourceFile:net/atm/lec.c SourceLine:1572] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lec_arp_expire_arp SourceFile:net/atm/lec.c SourceLine:1575] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lec_arp_resolve SourceFile:net/atm/lec.c SourceLine:1788] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lec_arp_resolve SourceFile:net/atm/lec.c SourceLine:1791] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lec_arp_update SourceFile:net/atm/lec.c SourceLine:1925]] TruncatedOutput:false]
|
| 66/2 |
2026/05/31 19:21 |
llm |
expert |
0m
Model:
Tokens:
input: 39522
output: 53
thoughts: 166
Thoughts: **Analyzing the LEC ARP Update Flow**
Okay, let me break this down. My initial observation is that `lec_arp_update` is being triggered from two primary locations: `lec_arp_check_empties` and `lec_mcast_make`. Interesting. `lec_arp_check_empties` itself gets called from `lec_arp_work`, which is likely a workqueue function. Now, `lane2_associate_req` is called from `lec_mcast_make` and `lane2_resolve`. `lec_arp_resolve` gets called from `lec_start_xmit`.
Putting that together, it looks like `send_to_lecd` is primarily driven by the transmission path via `lec_start_xmit` and these timers or workqueues, indicated by `lec_arp_work`. The crucial question, and this is where I need to dig deeper, is whether it's also invoked from the receive path. If so, that would indicate a more complex interaction, and I need to understand that.
To figure this out, I'm going to turn my attention to the routines `lec_vcc_added` and `lec_vcc_close`, which could be triggered by receive events. These are good candidates to investigate next.
|
| 67/2 |
2026/05/31 19:21 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/atm/lec.c IncludeSnippetLines:0 Name:lec_arp_update SourceTreePrefix:net/atm/]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lec_atm_send SourceFile:net/atm/lec.c SourceLine:385]] TruncatedOutput:false]
|
| 68/2 |
2026/05/31 19:21 |
llm |
expert |
0m
Model:
Tokens:
input: 40087
output: 36
thoughts: 70
Thoughts: Here's my understanding, as if I were thinking it:
**Decoding the ATM Network Interaction**
Okay, so I'm tracing through this code related to ATM and I see `lec_atm_send` popping up. That rings a bell, I know that function is handling the sending operation for the ATM device. But hold on a second... the context where it's called is intriguing. It's called when a packet is *received* from the ATM network? Hmm, that's a bit counterintuitive, I need to investigate further. Now, this `lec_atm_send` calls `lec_arp_update`, which suggests some kind of address resolution or cache update is happening during this send operation. Alright, let's dive into `lec_atm_send` itself and see exactly what's going on within that function.
|
| 69/2 |
2026/05/31 19:21 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/lec.c Name:lec_atm_send]
Results: map[SourceCode: 343: static int lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
344: {
345: static const u8 zero_addr[ETH_ALEN] = {};
346: unsigned long flags;
347: struct net_device *dev = (struct net_device *)vcc->proto_data;
348: struct lec_priv *priv = netdev_priv(dev);
349: struct atmlec_msg *mesg;
350: struct lec_arp_table *entry;
351: char *tmp; /* FIXME */
352:
353: WARN_ON(refcount_sub_and_test(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc));
354: mesg = (struct atmlec_msg *)skb->data;
355: tmp = skb->data;
356: tmp += sizeof(struct atmlec_msg);
357: pr_debug("%s: msg from zeppelin:%d\n", dev->name, mesg->type);
358: switch (mesg->type) {
359: case l_set_mac_addr:
360: eth_hw_addr_set(dev, mesg->content.normal.mac_addr);
361: break;
362: case l_del_mac_addr:
363: eth_hw_addr_set(dev, zero_addr);
364: break;
365: case l_addr_delete:
366: lec_addr_delete(priv, mesg->content.normal.atm_addr,
367: mesg->content.normal.flag);
368: break;
369: case l_topology_change:
370: priv->topology_change = mesg->content.normal.flag;
371: break;
372: case l_flush_complete:
373: lec_flush_complete(priv, mesg->content.normal.flag);
374: break;
375: case l_narp_req: /* LANE2: see 7.1.35 in the lane2 spec */
376: spin_lock_irqsave(&priv->lec_arp_lock, flags);
377: entry = lec_arp_find(priv, mesg->content.normal.mac_addr);
378: lec_arp_remove(priv, entry);
379: spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
380:
381: if (mesg->content.normal.no_source_le_narp)
382: break;
383: fallthrough;
384: case l_arp_update:
385: lec_arp_update(priv, mesg->content.normal.mac_addr,
386: mesg->content.normal.atm_addr,
387: mesg->content.normal.flag,
388: mesg->content.normal.targetless_le_arp);
389: pr_debug("in l_arp_update\n");
390: if (mesg->sizeoftlvs != 0) { /* LANE2 3.1.5 */
391: pr_debug("LANE2 3.1.5, got tlvs, size %d\n",
392: mesg->sizeoftlvs);
393: lane2_associate_ind(dev, mesg->content.normal.mac_addr,
394: tmp, mesg->sizeoftlvs);
395: }
396: break;
397: case l_config:
398: priv->maximum_unknown_frame_count =
399: mesg->content.config.maximum_unknown_frame_count;
400: priv->max_unknown_frame_time =
401: (mesg->content.config.max_unknown_frame_time * HZ);
402: priv->max_retry_count = mesg->content.config.max_retry_count;
403: priv->aging_time = (mesg->content.config.aging_time * HZ);
404: priv->forward_delay_time =
405: (mesg->content.config.forward_delay_time * HZ);
406: priv->arp_response_time =
407: (mesg->content.config.arp_response_time * HZ);
408: priv->flush_timeout = (mesg->content.config.flush_timeout * HZ);
409: priv->path_switching_delay =
410: (mesg->content.config.path_switching_delay * HZ);
411: priv->lane_version = mesg->content.config.lane_version;
412: /* LANE2 */
413: priv->lane2_ops = NULL;
414: if (priv->lane_version > 1)
415: priv->lane2_ops = &lane2_ops;
416: rtnl_lock();
417: if (dev_set_mtu(dev, mesg->content.config.mtu))
418: pr_info("%s: change_mtu to %d failed\n",
419: dev->name, mesg->content.config.mtu);
420: rtnl_unlock();
421: priv->is_proxy = mesg->content.config.is_proxy;
422: break;
423: case l_flush_tran_id:
424: lec_set_flush_tran_id(priv, mesg->content.normal.atm_addr,
425: mesg->content.normal.flag);
426: break;
427: case l_set_lecid:
428: priv->lecid =
429: (unsigned short)(0xffff & mesg->content.normal.flag);
430: break;
431: case l_should_bridge:
432: #if IS_ENABLED(CONFIG_BRIDGE)
433: {
434: pr_debug("%s: bridge zeppelin asks about %pM\n",
435: dev->name, mesg->content.proxy.mac_addr);
436:
437: if (br_fdb_test_addr_hook == NULL)
438: break;
439:
440: if (br_fdb_test_addr_hook(dev, mesg->content.proxy.mac_addr)) {
441: /* hit from bridge table, send LE_ARP_RESPONSE */
442: struct sk_buff *skb2;
443: struct sock *sk;
444:
445: pr_debug("%s: entry found, responding to zeppelin\n",
446: dev->name);
447: skb2 = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC);
448: if (skb2 == NULL)
449: break;
450: skb2->len = sizeof(struct atmlec_msg);
451: skb_copy_to_linear_data(skb2, mesg, sizeof(*mesg));
452: atm_force_charge(priv->lecd, skb2->truesize);
453: sk = sk_atm(priv->lecd);
454: skb_queue_tail(&sk->sk_receive_queue, skb2);
455: sk->sk_data_ready(sk);
456: }
457: }
458: #endif /* IS_ENABLED(CONFIG_BRIDGE) */
459: break;
460: default:
461: pr_info("%s: Unknown message type %d\n", dev->name, mesg->type);
462: dev_kfree_skb(skb);
463: return -EINVAL;
464: }
465: dev_kfree_skb(skb);
466: return 0;
467: }
SourceFile:net/atm/lec.c]
|
| 70/2 |
2026/05/31 19:21 |
llm |
expert |
0m
Model:
Tokens:
input: 42664
output: 54
thoughts: 189
Thoughts: **Analyzing the ATM Packet Flow and Potential Vulnerability**
Okay, so I'm tracing through this code, and it's starting to make sense... well, mostly. I see that `lec_atm_send` is the key function when a packet hits the ATM network. That makes perfect sense; it's the `send` operation. And I see that it, in turn, can call `lec_arp_update`, which then calls `send_to_lecd`. Further, I see `l_should_bridge` can directly call `atm_force_charge` using this structure: `atm_force_charge(priv->lecd, skb2->truesize);`.
Right, so when a packet arrives from the ATM network, this triggers a call to `atm_force_charge`. The immediate alarm bell is that if the socket is closed *concurrently*, the `priv->lecd` pointer could be null. And that would be bad news bears.
But hold on... who's *actually* calling `lec_atm_send`? It *is* the device `send` operation. Ah, got it. It's called from `atm_dev_ops->send`. Now I have to check who's calling *that*...
|
| 71/2 |
2026/05/31 19:21 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/atm/lec.c IncludeSnippetLines:0 Name:atmdev_ops::send SourceTreePrefix:net/atm/]
Results: map[References:[map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:atm_init_aal34 SourceFile:net/atm/raw.c SourceLine:79] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:atm_init_aal5 SourceFile:net/atm/raw.c SourceLine:91] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:atm_send_aal0 SourceFile:net/atm/raw.c SourceLine:59] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:vcc_sendmsg SourceFile:net/atm/common.c SourceLine:650]] TruncatedOutput:false]
|
| 72/2 |
2026/05/31 19:21 |
llm |
expert |
1m
Model:
Tokens:
input: 43395
output: 34
thoughts: 121
Thoughts: **Understanding the Message Flow in the LEC Daemon**
Okay, so I'm tracing the path of a message originating from user space, specifically the `lecd` daemon, and heading towards the ATM network. The entry point is `vcc_sendmsg`. I know that because it's the `sendmsg` operation for the ATM socket. That means the daemon is sending control information, not receiving data from the network.
`vcc_sendmsg` then calls `vcc->dev->ops->send`. This is key. That "send" operation is going to be specialized for the `lec` device, and in this case, it calls `lec_atm_send`. So, `lec_atm_send` handles the actual transmission of the user space control message.
This clarifies the direction. The message flow is *from* the `lecd` daemon *to* the kernel. And the kernel then takes that control message and does the appropriate thing for the ATM network. That is NOT receiving packets from the ATM network. The `lec` device actually *receives* the ATM packets via `lec_push`, a completely separate operation. Right, now I'm certain that the direction of flow is correct.
|
| 73/2 |
2026/05/31 19:22 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/lec.c Name:lec_push]
Results: map[SourceCode: 582: static void lec_push(struct atm_vcc *vcc, struct sk_buff *skb)
583: {
584: unsigned long flags;
585: struct net_device *dev = (struct net_device *)vcc->proto_data;
586: struct lec_priv *priv = netdev_priv(dev);
587:
588: #if DUMP_PACKETS > 0
589: printk(KERN_DEBUG "%s: vcc vpi:%d vci:%d\n",
590: dev->name, vcc->vpi, vcc->vci);
591: #endif
592: if (!skb) {
593: pr_debug("%s: null skb\n", dev->name);
594: lec_vcc_close(priv, vcc);
595: return;
596: }
597: #if DUMP_PACKETS >= 2
598: #define MAX_SKB_DUMP 99
599: #elif DUMP_PACKETS >= 1
600: #define MAX_SKB_DUMP 30
601: #endif
602: #if DUMP_PACKETS > 0
603: printk(KERN_DEBUG "%s: rcv datalen:%ld lecid:%4.4x\n",
604: dev->name, skb->len, priv->lecid);
605: print_hex_dump(KERN_DEBUG, "", DUMP_OFFSET, 16, 1,
606: skb->data, min(MAX_SKB_DUMP, skb->len), true);
607: #endif /* DUMP_PACKETS > 0 */
608: if (memcmp(skb->data, lec_ctrl_magic, 4) == 0) {
609: /* Control frame, to daemon */
610: struct sock *sk = sk_atm(vcc);
611:
612: pr_debug("%s: To daemon\n", dev->name);
613: skb_queue_tail(&sk->sk_receive_queue, skb);
614: sk->sk_data_ready(sk);
615: } else { /* Data frame, queue to protocol handlers */
616: struct lec_arp_table *entry;
617: unsigned char *src, *dst;
618:
619: atm_return(vcc, skb->truesize);
620: if (*(__be16 *) skb->data == htons(priv->lecid) ||
621: !priv->lecd || !(dev->flags & IFF_UP)) {
622: /*
623: * Probably looping back, or if lecd is missing,
624: * lecd has gone down
625: */
626: pr_debug("Ignoring frame...\n");
627: dev_kfree_skb(skb);
628: return;
629: }
630: dst = ((struct lecdatahdr_8023 *)skb->data)->h_dest;
631:
632: /*
633: * If this is a Data Direct VCC, and the VCC does not match
634: * the LE_ARP cache entry, delete the LE_ARP cache entry.
635: */
636: spin_lock_irqsave(&priv->lec_arp_lock, flags);
637: if (lec_is_data_direct(vcc)) {
638: src = ((struct lecdatahdr_8023 *)skb->data)->h_source;
639: entry = lec_arp_find(priv, src);
640: if (entry && entry->vcc != vcc) {
641: lec_arp_remove(priv, entry);
642: lec_arp_put(entry);
643: }
644: }
645: spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
646:
647: if (!(dst[0] & 0x01) && /* Never filter Multi/Broadcast */
648: !priv->is_proxy && /* Proxy wants all the packets */
649: memcmp(dst, dev->dev_addr, dev->addr_len)) {
650: dev_kfree_skb(skb);
651: return;
652: }
653: if (!hlist_empty(&priv->lec_arp_empty_ones))
654: lec_arp_check_empties(priv, vcc, skb);
655: skb_pull(skb, 2); /* skip lec_id */
656: skb->protocol = eth_type_trans(skb, dev);
657: dev->stats.rx_packets++;
658: dev->stats.rx_bytes += skb->len;
659: memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data));
660: netif_rx(skb);
661: }
662: }
SourceFile:net/atm/lec.c]
|
| 74/2 |
2026/05/31 19:22 |
llm |
expert |
0m
Model:
Tokens:
input: 45285
output: 39
thoughts: 61
Thoughts: Okay, let's break this down.
**Analyzing the ATM Packet Reception Process**
Alright, so the `lec_push` function is the entry point, the first place my code gets called when an ATM packet comes in. My primary concern here is how it handles Address Resolution Protocol (ARP) resolution, which is critical for mapping ATM addresses to the correct Logical Link Control (LLC) addresses. It looks like it directly calls `lec_arp_check_empties` under a certain condition. The crucial point is checking whether `priv->lec_arp_empty_ones` is empty or not. If it's *not* empty, meaning there are entries with empty LLC addresses, then `lec_arp_check_empties` gets executed. This suggests there might be some kind of deferred ARP resolution going on. Perhaps there's a backlog of ARP requests or perhaps there are entries with empty LLC addresses due to transient network conditions. The next logical step is to dive into `lec_arp_check_empties` to understand how it deals with these potentially unresolved entries.
|
| 75/2 |
2026/05/31 19:22 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/lec.c Name:lec_arp_check_empties]
Results: map[SourceCode:2217: static void
2218: lec_arp_check_empties(struct lec_priv *priv,
2219: struct atm_vcc *vcc, struct sk_buff *skb)
2220: {
2221: unsigned long flags;
2222: struct hlist_node *next;
2223: struct lec_arp_table *entry, *tmp;
2224: struct lecdatahdr_8023 *hdr = (struct lecdatahdr_8023 *)skb->data;
2225: unsigned char *src = hdr->h_source;
2226:
2227: spin_lock_irqsave(&priv->lec_arp_lock, flags);
2228: hlist_for_each_entry_safe(entry, next,
2229: &priv->lec_arp_empty_ones, next) {
2230: if (vcc == entry->vcc) {
2231: timer_delete(&entry->timer);
2232: ether_addr_copy(entry->mac_addr, src);
2233: entry->status = ESI_FORWARD_DIRECT;
2234: entry->last_used = jiffies;
2235: /* We might have got an entry */
2236: tmp = lec_arp_find(priv, src);
2237: if (tmp) {
2238: lec_arp_remove(priv, tmp);
2239: lec_arp_put(tmp);
2240: }
2241: hlist_del(&entry->next);
2242: lec_arp_add(priv, entry);
2243: goto out;
2244: }
2245: }
2246: pr_debug("LEC_ARP: Arp_check_empties: entry not found!\n");
2247: out:
2248: spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
2249: }
SourceFile:net/atm/lec.c]
|
| 76/2 |
2026/05/31 19:22 |
llm |
expert |
0m
Model:
Tokens:
input: 46185
output: 54
thoughts: 843
Thoughts: **My Analysis of the LEC ATM Device Vulnerability**
Okay, so I've been digging into this `lec_arp_check_empties` function, and my initial thought was that it might be related to a call to `send_to_lecd`. But, that was a red herring. I was confusing it with other functions. It turns out `lec_arp_update` is referenced in `lec_atm_send`, which is in turn, called by `vcc_sendmsg`. So, `send_to_lecd` is *not* triggered by incoming network traffic (RX path) β that's a key observation.
Instead, the `send_to_lecd` function is only ever called from the transmit path (`lec_start_xmit`), via `lec_atm_send` (essentially user-space calls through `vcc_sendmsg`), or through timers/workqueues using `lec_arp_work`. This completely changes the attack surface. This bug can't be triggered by receiving packets, so it rules out any remote exploitation based on network activity.
So, here's how I think this race condition is triggered:
1. An administrator needs to create a `lec` device, which requires `CAP_NET_ADMIN` privileges.
2. Someone then sends packets through that `lec` device. This is standard network behavior when the interface is enabled.
3. *Concurrently*, the administrator closes the `lec` device. This can be done by killing the associated daemon, or by explicitly closing the socket.
The crucial part is the simultaneous sending of packets and the device teardown. It's a race condition! This is a DoS, which causes a kernel crash.
Let's break down the exploit dimensions:
* **Exploitable:** No. This is a NULL pointer dereference, a classic crash.
* **Denial of Service:** Yes, it crashes the kernel.
* **Accessible from Unprivileged Processes:** No. Only the administrator can create and close the interface, so the critical race condition is inaccessible to standard users. A standard user can only send packets, but not close the socket.
* **Accessible from User Namespaces:** No, the required `CAP_NET_ADMIN` check is against the initial user namespace.
* **VM Guest/Host/Other Triggers:** All no, it is not possible to exploit these attack vectors.
* **Ethernet/Other Triggers:** No, this is all based on the transmit path, not receiving.
I considered a follow-up: Could an unprivileged user somehow create the `lec` device? We've already confirmed that `lecd_attach` needs `CAP_NET_ADMIN` in the initial user namespace, and that's not possible, so it cannot be triggered.
Then I considered other ways to close the ATM socket... Is it possible for a remote disconnect to cause the socket to close? My instincts told me that a remote disconnect will likely just set the socket error flag (`sk_err`) and wake up the socket. It is the responsibility of the daemon running in user space to close the socket. Therefore, remote disconnects don't trigger `lec_atm_close` or anything similar that would trigger the race. I'll need to confirm by checking the code. I'll need to check if the ATM driver calls `vcc_release` or `vcc->dev->ops->close` on a remote disconnect, which I can do in `vcc_sendmsg`.
Finally, just to be absolutely sure, I'll double-check: Can `send_to_lecd` somehow be called from the RX path? I've checked `lec_push` and it doesn't. What about `lec_vcc_added`?
|
| 77/2 |
2026/05/31 19:22 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/atm/lec.c IncludeSnippetLines:0 Name:lec_vcc_added SourceTreePrefix:net/atm/]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lec_vcc_attach SourceFile:net/atm/lec.c SourceLine:706]] TruncatedOutput:false]
|
| 78/2 |
2026/05/31 19:22 |
llm |
expert |
0m
Model:
Tokens:
input: 47966
output: 54
thoughts: 48
Thoughts: Okay, here's my interpretation of those thoughts, presented as if they're my own:
**Investigating the ATM LEC Interaction**
Right, so `lec_vcc_attach`... that's triggered from `lane_ioctl`, and specifically, with `ATMLEC_DATA`. That confirms it β it's definitely being called from a user-space `ioctl` call. I've got a handle on that part.
Now, the natural follow-up question is: What's the corresponding cleanup or release function? Let me think... what about `lec_vcc_close`? Is that the counterpart? I need to understand the lifecycle of these VCCs within the LEC context, and knowing how they're established and, crucially, how they're released is absolutely essential for understanding this architecture properly. That's the key to avoid any resource leaks. I need to trace the call paths associated with `lec_vcc_close` next to ensure proper resource management.
|
| 79/2 |
2026/05/31 19:22 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/atm/lec.c IncludeSnippetLines:0 Name:lec_vcc_close SourceTreePrefix:net/atm/]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:lec_push SourceFile:net/atm/lec.c SourceLine:594]] TruncatedOutput:false]
|
| 80/2 |
2026/05/31 19:22 |
llm |
expert |
0m
Model:
Tokens:
input: 48346
output: 37
thoughts: 37
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Investigating a Null SKB and LEC_VCC_CLOSE**
Right, so `lec_push` has a conditional: If `skb` is NULL, it calls `lec_vcc_close`. Interesting. That immediately suggests a potential null pointer dereference vulnerability, or at the very least, a likely point of error handling. My immediate inclination is to understand the implications of this condition.
Therefore, the next step is to drill down into `lec_vcc_close`. I need to see what `lec_vcc_close` does with the VCC, specifically how it handles potential resources, deallocates any memory, or interacts with other structures. I need to understand if this is a graceful shutdown, or a potential source of memory corruption or leaks if the VCC state is inconsistent. I'll need to check the calling context of `lec_vcc_close` to understand why `skb` might be NULL in the first place, and if there are any race conditions. Let's see what that function actually *does*.
|
| 81/2 |
2026/05/31 19:23 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/lec.c Name:lec_vcc_close]
Results: map[SourceCode:2160: static void lec_vcc_close(struct lec_priv *priv, struct atm_vcc *vcc)
2161: {
2162: unsigned long flags;
2163: struct hlist_node *next;
2164: struct lec_arp_table *entry;
2165: int i;
2166:
2167: pr_debug("LEC_ARP: lec_vcc_close vpi:%d vci:%d\n", vcc->vpi, vcc->vci);
2168: dump_arp_table(priv);
2169:
2170: spin_lock_irqsave(&priv->lec_arp_lock, flags);
2171:
2172: for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
2173: hlist_for_each_entry_safe(entry, next,
2174: &priv->lec_arp_tables[i], next) {
2175: if (vcc == entry->vcc) {
2176: lec_arp_remove(priv, entry);
2177: lec_arp_put(entry);
2178: if (priv->mcast_vcc == vcc)
2179: priv->mcast_vcc = NULL;
2180: }
2181: }
2182: }
2183:
2184: hlist_for_each_entry_safe(entry, next,
2185: &priv->lec_arp_empty_ones, next) {
2186: if (entry->vcc == vcc) {
2187: lec_arp_clear_vccs(entry);
2188: timer_delete(&entry->timer);
2189: hlist_del(&entry->next);
2190: lec_arp_put(entry);
2191: }
2192: }
2193:
2194: hlist_for_each_entry_safe(entry, next,
2195: &priv->lec_no_forward, next) {
2196: if (entry->recv_vcc == vcc) {
2197: lec_arp_clear_vccs(entry);
2198: timer_delete(&entry->timer);
2199: hlist_del(&entry->next);
2200: lec_arp_put(entry);
2201: }
2202: }
2203:
2204: hlist_for_each_entry_safe(entry, next, &priv->mcast_fwds, next) {
2205: if (entry->recv_vcc == vcc) {
2206: lec_arp_clear_vccs(entry);
2207: /* No timer, LANEv2 7.1.20 and 2.3.5.3 */
2208: hlist_del(&entry->next);
2209: lec_arp_put(entry);
2210: }
2211: }
2212:
2213: spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
2214: dump_arp_table(priv);
2215: }
SourceFile:net/atm/lec.c]
|
| 82/2 |
2026/05/31 19:23 |
llm |
expert |
0m
Model:
Tokens:
input: 49591
output: 38
thoughts: 86
Thoughts: Here's my thought process, distilled:
**Digging into the LEC/VCC Closing Process**
Okay, so I'm trying to trace the lifecycle of a VCC closure and, specifically, when `send_to_lecd` gets invoked. It looks like `lec_vcc_close` itself doesn't directly call `send_to_lecd`. That's important. It means this isn't happening in the straightforward RX data path, which simplifies things (or, potentially, complicates them depending on where else we look).
But, wait a minute... could `send_to_lecd` still be invoked as part of this close-down sequence? Thinking about the broader architecture, I have to consider delayed work. Right! `lec_arp_work` is a delayed work queue, isn't it? And that function, as I recall, eventually calls `lec_arp_check_expire`. I need to review how those functions interact to see if that's where `send_to_lecd` is being triggered. I need to investigate that connection more closely.
|
| 83/2 |
2026/05/31 19:23 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/lec.c Name:lec_arp_check_expire]
Results: map[SourceCode:1650: /*
1651: * Expire entries.
1652: * 1. Re-set timer
1653: * 2. For each entry, delete entries that have aged past the age limit.
1654: * 3. For each entry, depending on the status of the entry, perform
1655: * the following maintenance.
1656: * a. If status is ESI_VC_PENDING or ESI_ARP_PENDING then if the
1657: * tick_count is above the max_unknown_frame_time, clear
1658: * the tick_count to zero and clear the packets_flooded counter
1659: * to zero. This supports the packet rate limit per address
1660: * while flooding unknowns.
1661: * b. If the status is ESI_FLUSH_PENDING and the tick_count is greater
1662: * than or equal to the path_switching_delay, change the status
1663: * to ESI_FORWARD_DIRECT. This causes the flush period to end
1664: * regardless of the progress of the flush protocol.
1665: */
1666: static void lec_arp_check_expire(struct work_struct *work)
1667: {
1668: unsigned long flags;
1669: struct lec_priv *priv =
1670: container_of(work, struct lec_priv, lec_arp_work.work);
1671: struct hlist_node *next;
1672: struct lec_arp_table *entry;
1673: unsigned long now;
1674: int i;
1675:
1676: pr_debug("%p\n", priv);
1677: now = jiffies;
1678: restart:
1679: spin_lock_irqsave(&priv->lec_arp_lock, flags);
1680: for (i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
1681: hlist_for_each_entry_safe(entry, next,
1682: &priv->lec_arp_tables[i], next) {
1683: if (__lec_arp_check_expire(entry, now, priv)) {
1684: struct sk_buff *skb;
1685: struct atm_vcc *vcc = entry->vcc;
1686:
1687: spin_unlock_irqrestore(&priv->lec_arp_lock,
1688: flags);
1689: while ((skb = skb_dequeue(&entry->tx_wait)))
1690: lec_send(vcc, skb);
1691: entry->last_used = jiffies;
1692: entry->status = ESI_FORWARD_DIRECT;
1693: lec_arp_put(entry);
1694:
1695: goto restart;
1696: }
1697: }
1698: }
1699: spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
1700:
1701: schedule_delayed_work(&priv->lec_arp_work, LEC_ARP_REFRESH_INTERVAL);
1702: }
SourceFile:net/atm/lec.c]
|
| 84/2 |
2026/05/31 19:23 |
llm |
expert |
0m
Model:
Tokens:
input: 50868
output: 35
thoughts: 1073
Thoughts: **My Analysis of the Vulnerability**
Okay, let's break this down. My initial assessment was that this NULL pointer dereference, triggered by a race condition involving `__lec_arp_check_expire` calling `send_to_lecd` and potential concurrent execution with `lec_atm_close` in a workqueue, wasn't exploitable by an unprivileged user. My reasoning was solid: `lec_atm_close` is only initiated when the ATM socket closes, and that socket is owned by the daemon (root). So, without root access, an unprivileged user can't directly trigger the socket closure, which is the crucial point.
My initial summary for the tool reflected this: Exploitable: False (NULL pointer dereference). DenialOfService: True (Kernel crash). Unprivileged: False. The rest (UserNamespace, VMGuestTrigger, etc.) all fell into place based on this initial understanding of the required privileges and triggering conditions. My understanding then, was that even if the daemon crashes, that's a daemon-level bug, not a kernel-level vulnerability that an unprivileged user can leverage.
Then, the question arose about a pre-existing `lec` device and a daemon restart/exit. The question was, if an unprivileged user sends a packet and the daemon is restarted normally, is it then accessible to an unprivileged process? My conclusion was still that it's *not* accessible. While the unprivileged user *participates* in triggering the bug by sending the packet, they can't *cause* the closure of the ATM socket which is the essential step of the race condition.
I then went back to review how `syzkaller` was able to trigger it, and was thrown by the fact that the bug report suggests that an unprivileged user was able to trigger the issue, due to the user being the user `dhcpcd`. While `syzkaller` runs as root by default, it might be in a user namespace, which means it has `CAP_NET_ADMIN`. But `capable(CAP_NET_ADMIN)` checks the initial namespace.
And then I saw it. The bug report itself provided the crucial insight. `dhcpcd` was the triggering process, but running as UID 101, i.e. the unprivileged user account. Its actions were to send a packet (UDP broadcast), which triggered a series of kernel functions, ultimately calling `send_to_lecd`. The critical point here is that an unprivileged process *can* trigger the sending of the packet that is the first step of the race.
So, the other part of the race condition, `lec_atm_close` must have happened separately, *likely* triggered by another task (like `syzkaller`) that *does* have root or `CAP_NET_ADMIN` privileges. So it's a race, where the unprivileged process is only *participating*. If the user cannot make it happen consistently on demand, it should still be flagged as `False`.
In effect, while `dhcpcd` *could* have participated, `dhcpcd` *cannot* close the socket by itself. Thus, the unprivileged user doesn't have the power to close the ATM socket. My final check was to consider whether an unprivileged user could create the socket. If not, then the bug, as it stands, is not something that can be triggered by a user without special privileges.
|
| 85/2 |
2026/05/31 19:23 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/common.c Name:vcc_create]
Results: map[SourceCode: 140: int vcc_create(struct net *net, struct socket *sock, int protocol, int family, int kern)
141: {
142: struct sock *sk;
143: struct atm_vcc *vcc;
144:
145: sock->sk = NULL;
146: if (sock->type == SOCK_STREAM)
147: return -EINVAL;
148: sk = sk_alloc(net, family, GFP_KERNEL, &vcc_proto, kern);
149: if (!sk)
150: return -ENOMEM;
151: sock_init_data(sock, sk);
152: sk->sk_state_change = vcc_def_wakeup;
153: sk->sk_write_space = vcc_write_space;
154:
155: vcc = atm_sk(sk);
156: vcc->dev = NULL;
157: memset(&vcc->local, 0, sizeof(struct sockaddr_atmsvc));
158: memset(&vcc->remote, 0, sizeof(struct sockaddr_atmsvc));
159: vcc->qos.txtp.max_sdu = 1 << 16; /* for meta VCs */
160: refcount_set(&sk->sk_wmem_alloc, SK_WMEM_ALLOC_BIAS);
161: atomic_set(&sk->sk_rmem_alloc, 0);
162: vcc->push = NULL;
163: vcc->pop = NULL;
164: vcc->owner = NULL;
165: vcc->push_oam = NULL;
166: vcc->release_cb = NULL;
167: vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */
168: vcc->atm_options = vcc->aal_options = 0;
169: sk->sk_destruct = vcc_sock_destruct;
170: return 0;
171: }
SourceFile:net/atm/common.c]
|
| 86/2 |
2026/05/31 19:23 |
llm |
expert |
0m
Model:
Tokens:
input: 53310
output: 37
thoughts: 2040
Thoughts: **Analyzing the ATM LANE Driver Bug**
Okay, let's break this down. First, can an unprivileged user exploit this? It seems they *can* create an ATM socket using `socket(AF_ATMPVC, SOCK_DGRAM, 0)`. However, the code's designed so they can't call `ATMLEC_CTRL` via `ioctl` to attach it to a `lec` device because of a `CAP_NET_ADMIN` check in `lane_ioctl`. So, no dice. They also can't close the socket, because they can't attach it to the `lec` device in the first place. Therefore, `Unprivileged = False`.
Now, is this `Exploitable`? The bug involves writing to address `0x140` when `vcc` is NULL. That address is in the unmapped first page of memory. It will trigger a page fault, a kernel panic, but not arbitrary code execution. It's a crash, not a privilege escalation. So, `Exploitable = False`.
What about `DenialOfService`? Yeah, it crashes the kernel. So, `DenialOfService = True`.
Okay, what about the `NetworkTrigger`? Can it be triggered by network traffic? The bug is in the TX path, so I need to see if RX traffic can kick that off. Examining the code, `lec_push` and `lec_vcc_close` *don't* directly trigger the TX path. However, a received packet could trigger an ARP request/reply. Specifically, an ICMP echo reply triggered by ingress traffic would go through `lec_start_xmit`, triggering the bug.
But, is it "Ethernet Network Trigger"? LANE emulates Ethernet over ATM, so the `lec` device *is* an Ethernet device. However, to cause a reply to be sent, it would involve a local administrative action (closing the socket). I'm defining "Network Trigger" as an attacker triggering the bug *without* local admin help. So the attacker can't independently trigger the bug. Therefore, I will set `NetworkTrigger = False`.
However, what if there's a remote ATM network trigger? If an ATM connection drops, the `atmsigd` daemon *might* close the socket and thus trigger `lec_atm_close`. That daemon handles SVCs. So, a remote ATM host dropping the connection *can* cause the race to occur! Thus, `RemoteTrigger = True`.
And what about a "true" Ethernet Network Trigger? An attacker on the Ethernet network can send Ethernet traffic to cause TX traffic, but they can't drop the underlying ATM connection. So, no.
Finally, let's verify if `atmsigd` actually does close the socket. Yep. The `atmsigd` daemon closes the file descriptor, which leads to `vcc_release`, `vcc_destroy_socket`, and `vcc->dev->ops->close(vcc)`. A remote ATM switch or host can release the SVC, which triggers the close. However, *is* the closing of the socket involved in the bug the control VCC or the data direct VCC? It seems it's the control VCC. If the `lec` device is using SVCs, they can be released, causing the bug. But `lec_atm_close` is used for the control VCC. And the control VCC is used for kernel/user communication. Thus, it's not connected to the remote ATM network!
|
| 87/2 |
2026/05/31 19:23 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/atm/lec.c Name:lec_vcc_attach]
Results: map[SourceCode: 683: static int lec_vcc_attach(struct atm_vcc *vcc, void __user *arg)
684: {
685: struct lec_vcc_priv *vpriv;
686: int bytes_left;
687: struct atmlec_ioc ioc_data;
688:
689: lockdep_assert_held(&lec_mutex);
690: /* Lecd must be up in this case */
691: bytes_left = copy_from_user(&ioc_data, arg, sizeof(struct atmlec_ioc));
692: if (bytes_left != 0)
693: pr_info("copy from user failed for %d bytes\n", bytes_left);
694: if (ioc_data.dev_num < 0 || ioc_data.dev_num >= MAX_LEC_ITF)
695: return -EINVAL;
696: ioc_data.dev_num = array_index_nospec(ioc_data.dev_num, MAX_LEC_ITF);
697: if (!dev_lec[ioc_data.dev_num])
698: return -EINVAL;
699: vpriv = kmalloc_obj(struct lec_vcc_priv);
700: if (!vpriv)
701: return -ENOMEM;
702: vpriv->xoff = 0;
703: vpriv->old_pop = vcc->pop;
704: vcc->user_back = vpriv;
705: vcc->pop = lec_pop;
706: lec_vcc_added(netdev_priv(dev_lec[ioc_data.dev_num]),
707: &ioc_data, vcc, vcc->push);
708: vcc->proto_data = dev_lec[ioc_data.dev_num];
709: vcc->push = lec_push;
710: return 0;
711: }
SourceFile:net/atm/lec.c]
|
| 88/2 |
2026/05/31 19:23 |
llm |
expert |
0m
Model:
Tokens:
input: 56720
output: 54
thoughts: 434
Thoughts: **My Analysis of the `lec_vcc_attach` and `lecd` Control Channel**
Okay, so I'm looking at how Data Direct VCCs, linked to the remote ATM network, are handled in the `lec_vcc_attach` function. It seems straightforward: it sets `vcc->push = lec_push`. But then, `lecd_attach` handles the attachment of the control VCC, which is referred to as `priv->lecd`.
Right, `priv->lecd` is indeed the control VCC. It's the one used by `send_to_lecd`. This means this `priv->lecd` control channel is how the local system communicates with the `lecd` daemon. Crucially, this control VCC is *not* connected to the remote ATM network. So, a remote ATM host can't directly drop the connection. Thus, `RemoteTrigger` is definitely `False`.
Let me just confirm that line of reasoning. The `lecd` daemon does open an ATM socket and binds it. Then it calls `ATMLEC_CTRL`. But what does it actually connect the socket to? Aha! It doesn't connect it to anything external. It just uses it for purely local communication, receiving messages from the kernel via `skb_queue_tail` and sending messages to the kernel via `vcc_sendmsg`. It's a purely local channel. Therefore, the only ways that socket can be closed is if the `lecd` daemon itself closes it or exits. Since this is a local root process, no remote attacker or unprivileged local user could force it to close the socket.
Okay, so that means the bug can only be triggered by a local root user, or someone with `CAP_NET_ADMIN` in the initial namespace, closing the socket while traffic is flowing. That means itβs completely inaccessible from unprivileged processes and user namespaces, not triggerable remotely, and not affected by network traffic.
Wait a second, before I get too confident, I should double check. Are there any other ways `priv->lecd` could be set to NULL? Gotta look at all the places `priv->lecd` is written to.
|
| 89/2 |
2026/05/31 19:24 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/atm/lec.c IncludeSnippetLines:0 Name:lec_priv::lecd SourceTreePrefix:net/atm/]
Results: map[References:[map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:lec_atm_close SourceFile:net/atm/lec.c SourceLine:475] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:lec_atm_send SourceFile:net/atm/lec.c SourceLine:452] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:lec_atm_send SourceFile:net/atm/lec.c SourceLine:453] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:lec_handle_bridge SourceFile:net/atm/lec.c SourceLine:157] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:lec_handle_bridge SourceFile:net/atm/lec.c SourceLine:158] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:lec_push SourceFile:net/atm/lec.c SourceLine:621] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:lec_start_xmit SourceFile:net/atm/lec.c SourceLine:219] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:lecd_attach SourceFile:net/atm/lec.c SourceLine:756] map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:lecd_attach SourceFile:net/atm/lec.c SourceLine:761] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:send_to_lecd SourceFile:net/atm/lec.c SourceLine:517] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:send_to_lecd SourceFile:net/atm/lec.c SourceLine:535] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:send_to_lecd SourceFile:net/atm/lec.c SourceLine:536] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:send_to_lecd SourceFile:net/atm/lec.c SourceLine:542]] TruncatedOutput:false]
|
| 90/2 |
2026/05/31 19:24 |
llm |
expert |
0m
Model:
Tokens:
input: 58336
output: 66
thoughts: 735
Thoughts: **My Analysis of the Kernel Vulnerability**
Okay, let's break this down. My initial assessment is that the only functions writing to `priv->lecd` are `lec_atm_close` and `lecd_attach`. The only way this pointer can become NULL is through `lec_atm_close`, which itself is only called when the control VCC is closed. Since the local root daemon owns the control VCC, only that daemon can close it. Therefore, this looks like a local root DoS vulnerability, or perhaps a race condition during a legitimate teardown initiated by root.
I need to confirm whether `lecd_attach` can be called multiple times, potentially overwriting `priv->lecd`. Looking at the code:
```c
} else {
priv = netdev_priv(dev_lec[i]);
if (priv->lecd)
return -EADDRINUSE;
}
```
No, it explicitly checks if `priv->lecd` is already set and returns `-EADDRINUSE` if it is. So the initial hypothesis is sound.
The exploitability analysis so far is:
- Exploitable: False
- DenialOfService: True
- Unprivileged: False
- UserNamespace: False
- VMGuestTrigger: False
- VMHostTrigger: False
- NetworkTrigger: False
- RemoteTrigger: False
- PeripheralTrigger: False
- FilesystemTrigger: False
But wait, I need to consider all angles. Could this involve `dhcpcd` somehow? Maybe it triggered the close? Let's check the context. No, `dhcpcd` was in the `sendmsg` path: `sendmsg` -> `packet_sendmsg` -> `dev_queue_xmit` -> `lec_start_xmit`. `dhcpcd` was _sending_ a packet. That means something else must have closed the socket.
Aha! Syzkaller! Syzkaller is multi-threaded. One thread might have been sending a packet (via `dhcpcd` or syzkaller itself - I have to remember that Syzkaller can run programs *or* inject packets), while another thread closed the ATM socket. Remember, Syzkaller often creates network namespaces and runs `dhcpcd` inside to get an IP address. So the crash happened *in* `dhcpcd` because it was sending a DHCP Discover packet on the newly created `lec` device, but, simultaneously, syzkallerβs main thread closed the ATM socket. *That* perfectly explains the race!
So, the assessment is solid.
Now, let's systematically revisit the questions and solidify the answers:
- Exploitable: False. The crash is a NULL pointer dereference at 0x140.
- DenialOfService: True. It's a kernel crash.
- Unprivileged: False. Creation of the `lec` device and attachment of the control VCC requires `CAP_NET_ADMIN` in `init_user_ns`.
- UserNamespace: False. The `capable(CAP_NET_ADMIN)` check is performed in the `init_user_ns`.
- VMGuestTrigger: False.
- VMHostTrigger: False.
- NetworkTrigger: False. The race is between local TX and local socket close.
- RemoteTrigger: False.
- PeripheralTrigger: False.
- FilesystemTrigger: False.
I'm satisfied with this analysis. I will now `set-results` with these values.
|
| 91/2 |
2026/05/31 19:24 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 92/2 |
2026/05/31 19:24 |
llm |
expert |
0m
Model:
Tokens:
input: 59957
output: 574
thoughts: 511
Thoughts: **Final Reasoning - ATM LANE Driver NULL Pointer Dereference**
Okay, here's my final assessment. I've analyzed the results and I'm ready to articulate the reasoning behind my conclusions.
First, the core of the problem lies in a NULL pointer dereference within the ATM LAN Emulation (LANE) driver, specifically in `net/atm/lec.c`. The crash originates in the `atm_force_charge()` function, which is called by `send_to_lecd()`. I've confirmed that `send_to_lecd()` checks if `priv->lecd` is NULL, but *later* accesses it without any locking mechanisms. This is the race condition. If the ATM control socket (`priv->lecd`) is closed concurrently, likely through `lec_atm_close()`, it sets `priv->lecd` to NULL. Consequently, a subsequent call to `atm_force_charge(priv->lecd, ...)` causes the NULL pointer dereference. The code then attempts to write to the `sk_rmem_alloc` field of the socket structure at offset `0x140`.
This brings me to the second point - exploitability. While this is a clear bug, the immediate consequence is a write to the virtual address `0x140`, which resides within the unmapped zero page protected by `mmap_min_addr`. This will cause a deterministic page fault and a kernel panic. *Fortunately*, this address is in a safe location that is not mapped by the kernel. The value written is a 4-byte value (`truesize`), which means the impact is limited to a Denial of Service and does not allow for privilege escalation or memory corruption of valid kernel objects.
The third point, naturally, is that this bug results in a Denial of Service. The kernel crashes because of the NULL pointer dereference.
Fourth, considering privilege requirements, an attacker needs to be able to create a LANE device and attach an ATM control socket using the `ATMLEC_CTRL` ioctl on an ATM socket. The `lane_ioctl()` function explicitly checks for `capable(CAP_NET_ADMIN)`. This is key. The attacker *must* have the `CAP_NET_ADMIN` capability in the initial user namespace to trigger the vulnerability. So, unprivileged users and those within unprivileged user namespaces are out of luck; they can't exploit it.
Finally, regarding the network or remote triggering, this is a local issue. The race occurs between a local packet transmission (e.g., via `lec_start_xmit()`) and the local closure of the ATM control socket. Since the control socket is a local communication channel between the kernel and the `lecd` daemon, and can not be closed remotely, remote exploitation is impossible. This bug is not exposed to remote network traffic.
And that covers the entire assessment.
|
| 93/1 |
2026/05/31 19:24 |
action |
format-explanation |
0m
Results: map[Explanation:**Bug Analysis:**
The crash is a NULL pointer dereference in `atm_force_charge()`, which is called
from `send_to_lecd()` in the ATM LAN Emulation (LANE) driver (`net/atm/lec.c`).
The function `send_to_lecd()` checks if the control socket `priv->lecd` is NULL
at the beginning of the function. However, it accesses `priv->lecd` later
without holding any locks. If the ATM control socket is closed concurrently by
the user-space daemon, the `lec_atm_close()` function is invoked, which sets
`priv->lecd = NULL`. When `send_to_lecd()` subsequently calls
`atm_force_charge(priv->lecd, ...)`, it dereferences the now-NULL pointer.
Specifically, `atm_force_charge()` attempts to execute an `atomic_add()` on the
`sk_rmem_alloc` field of the socket structure. The `sk_rmem_alloc` field is
located at offset `0x140` within the `struct sock`.
**Exploitability:**
The bug results in a 4-byte write (the `truesize` of the `skb`) to the virtual
address `0x140`. Because this address is within the unmapped zero page (which is
protected by `mmap_min_addr` in modern kernels), the access will
deterministically cause a page fault and a kernel panic. It cannot be exploited
for privilege escalation or memory corruption of valid kernel objects.
**Denial of Service:**
Triggering the bug causes a kernel panic, leading to a Denial of Service (DoS).
**Privilege Requirements:**
To trigger the race condition, an attacker must be able to create a LANE device
and attach an ATM control socket to it. This setup is performed via the
`ATMLEC_CTRL` ioctl on an ATM socket. The `lane_ioctl()` function explicitly
checks for `capable(CAP_NET_ADMIN)`. Because it uses `capable()` rather than
`ns_capable()`, it strictly requires the `CAP_NET_ADMIN` capability in the
**initial user namespace**. Therefore, unprivileged users and users within
unprivileged user namespaces cannot reach the vulnerable code or trigger the
teardown process.
**Network/Remote Trigger:**
The race condition occurs between the local transmission of a packet (e.g., via
`lec_start_xmit()`) and the local closure of the ATM control socket. The control
socket is a local communication channel between the kernel and the `lecd`
daemon, and it cannot be closed remotely. Thus, the bug cannot be triggered by
remote network traffic or remote ATM hosts.]
|