| 0/0 |
2026/05/16 03:25 |
flow |
assessment-security |
4m
Results: map[DenialOfService:false Explanation:Based on the analysis of the provided KCSAN bug report and the kernel source
code, here is the assessment of the security impact:
### Bug Mechanism
The KCSAN report identifies a data race between two execution contexts:
1. **CPU 0 (Interrupt/Softirq context):** The `bcm_tx_timeout_handler` timer
callback fires and calls `bcm_can_tx()`, which reads the CAN frame payload
(`cf`) and copies it into a newly allocated socket buffer (`skb`) using
`skb_put_data()`.
2. **CPU 1 (Process context):** A user-space process calls `sendmsg()` on a CAN
BCM socket, which reaches `bcm_tx_setup()`. This function updates the CAN frame
payload (`cf`) by copying data from user-space using `memcpy_from_msg()`.
Because `bcm_tx_setup()` does not hold the `op->bcm_tx_lock` while copying the
payload from user-space, the timer callback can concurrently read the payload
while it is being updated. This results in a data race on the CAN frame payload.
### Security Impact Assessment
**1. Exploitable: False**
This data race is strictly on the payload of the CAN frame being sent and does
not lead to memory corruption or privilege escalation.
- **No Out-of-Bounds Access:** The size of the copy (`op->cfsiz`) is fixed at
the creation of the BCM operation and cannot be changed concurrently. The `skb`
is allocated with exactly this size, and `skb_put_data()` copies exactly this
size. Thus, there is no out-of-bounds read from the `op->frames` array or
out-of-bounds write to the `skb`.
- **No TOCTOU on Payload Length:** The user-space process could attempt to write
an invalid payload length (`cf->len`) during the race. However, `bcm_can_tx()`
copies the entire frame into the `skb` first, and then passes the `skb` to
`can_send()`. `can_send()` validates the `cf->len` field from the *copied* data
in the `skb` (via `can_is_can_skb()` or `can_is_canfd_skb()`). If the copied
length is invalid, `can_send()` safely drops the packet. Since the validation
happens on the copied data, there is no Time-of-Check to Time-of-Use (TOCTOU)
vulnerability.
- The worst-case consequence is that a corrupted CAN frame (containing a mix of
old and new data) is transmitted on the CAN bus, which is a functional issue but
not a kernel security vulnerability.
**2. Denial Of Service: False**
The bug does not cause kernel crashes, hangs, deadlocks, or resource leaks.
- There are other minor data races on timer intervals (`op->kt_ival1`,
`op->kt_ival2`) which are updated without a lock. A torn read of these 64-bit
`ktime_t` values on 32-bit systems could result in a corrupted timer expiry.
However, because the maximum allowed timer value (`BCM_TIMER_SEC_MAX`) ensures
the sign bit is always 0, a torn read cannot result in a negative time value.
The timer will simply fire at an unexpected positive interval, which does not
cause a softirq storm or system hang.
**3. Accessible From Unprivileged Processes: True**
Unprivileged users can create CAN sockets (`PF_CAN`) and use the BCM protocol
(`CAN_BCM`). The kernel automatically loads the `can-bcm` module if it is not
already loaded, and there are no `capable()` checks restricting the creation of
these sockets or the use of `sendmsg()`.
**4. Accessible From User Namespaces: True**
Since the bug is reachable by unprivileged users in the initial namespace, it is
also reachable from within user namespaces.
**5. Other Triggers (Network, Remote, Peripheral, Filesystem, VM): False**
The bug is triggered locally via the `sendmsg()` syscall on a CAN socket. It is
not triggered by processing ingress network traffic, remote connections,
plugging in malicious peripherals, mounting filesystems, or specific VM
interactions.
### Conclusion
The KCSAN report highlights a true positive data race, but it is benign from a
kernel security perspective. It does not compromise kernel memory safety, cannot
be exploited for privilege escalation, and does not cause a Denial of Service. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/05/16 03:25 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/05/16 03:25 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/39056aef3a53503f1a05892aad8990e208e793fc]
|
| 3/1 |
2026/05/16 03:25 |
action |
kernel-builder |
0m
Results: map[KernelObj:/app/workdir/cache/build/b973c5f6f2e5f17ebaaf39ce43b84b03b5b532c2]
|
| 4/1 |
2026/05/16 03:25 |
action |
codesearch-prepare |
0m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/05/16 03:25 |
agent |
expert |
4m
Model:
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
Instruction:
You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by greeping
".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).
Don't make assumptions; verify them with source code access. 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 completly 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 tranfers, that's 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 exploitabability 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 Confidetial 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.
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: KCSAN: data-race in _copy_from_iter / bcm_can_tx
read to 0xffff8881242845c0 of 72 bytes by interrupt on cpu 0:
skb_put_data include/linux/skbuff.h:2813 [inline]
bcm_can_tx+0x1b3/0x4c0 net/can/bcm.c:327
bcm_tx_timeout_handler+0xe5/0x290 net/can/bcm.c:477
__run_hrtimer kernel/time/hrtimer.c:1930 [inline]
__hrtimer_run_queues+0x276/0x4f0 kernel/time/hrtimer.c:1994
hrtimer_run_softirq+0xe2/0x520 kernel/time/hrtimer.c:2011
handle_softirqs+0xb9/0x280 kernel/softirq.c:622
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0x42/0xd0 kernel/softirq.c:735
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1061 [inline]
sysvec_apic_timer_interrupt+0x37/0x80 arch/x86/kernel/apic/apic.c:1061
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:697
write to 0xffff8881242845c0 of 72 bytes by task 4914 on cpu 1:
instrument_copy_from_user_before include/linux/instrumented.h:147 [inline]
copy_from_user_iter lib/iov_iter.c:66 [inline]
iterate_iovec include/linux/iov_iter.h:52 [inline]
iterate_and_advance2 include/linux/iov_iter.h:304 [inline]
iterate_and_advance include/linux/iov_iter.h:330 [inline]
__copy_from_iter lib/iov_iter.c:261 [inline]
_copy_from_iter+0x258/0xea0 lib/iov_iter.c:272
copy_from_iter include/linux/uio.h:228 [inline]
copy_from_iter_full include/linux/uio.h:245 [inline]
memcpy_from_msg include/linux/skbuff.h:4298 [inline]
bcm_tx_setup+0xaeb/0xde0 net/can/bcm.c:965
bcm_sendmsg+0x357/0x4a0 net/can/bcm.c:1433
sock_sendmsg_nosec net/socket.c:787 [inline]
__sock_sendmsg net/socket.c:802 [inline]
____sys_sendmsg+0x563/0x5b0 net/socket.c:2698
___sys_sendmsg+0x195/0x1e0 net/socket.c:2752
__sys_sendmsg net/socket.c:2784 [inline]
__do_sys_sendmsg net/socket.c:2789 [inline]
__se_sys_sendmsg net/socket.c:2787 [inline]
__x64_sys_sendmsg+0xd4/0x160 net/socket.c:2787
x64_sys_call+0x194c/0x3020 arch/x86/include/generated/asm/syscalls_64.h:47
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x12c/0x3b0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Reported by Kernel Concurrency Sanitizer on:
CPU: 1 UID: 0 PID: 4914 Comm: syz.4.421 Tainted: G W syzkaller #0 PREEMPT(full)
Tainted: [W]=WARN
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/2026
==================================================================
Reply: Based on the analysis of the provided KCSAN bug report and the kernel source code, here is the assessment of the security impact:
### Bug Mechanism
The KCSAN report identifies a data race between two execution contexts:
1. **CPU 0 (Interrupt/Softirq context):** The `bcm_tx_timeout_handler` timer callback fires and calls `bcm_can_tx()`, which reads the CAN frame payload (`cf`) and copies it into a newly allocated socket buffer (`skb`) using `skb_put_data()`.
2. **CPU 1 (Process context):** A user-space process calls `sendmsg()` on a CAN BCM socket, which reaches `bcm_tx_setup()`. This function updates the CAN frame payload (`cf`) by copying data from user-space using `memcpy_from_msg()`.
Because `bcm_tx_setup()` does not hold the `op->bcm_tx_lock` while copying the payload from user-space, the timer callback can concurrently read the payload while it is being updated. This results in a data race on the CAN frame payload.
### Security Impact Assessment
**1. Exploitable: False**
This data race is strictly on the payload of the CAN frame being sent and does not lead to memory corruption or privilege escalation.
- **No Out-of-Bounds Access:** The size of the copy (`op->cfsiz`) is fixed at the creation of the BCM operation and cannot be changed concurrently. The `skb` is allocated with exactly this size, and `skb_put_data()` copies exactly this size. Thus, there is no out-of-bounds read from the `op->frames` array or out-of-bounds write to the `skb`.
- **No TOCTOU on Payload Length:** The user-space process could attempt to write an invalid payload length (`cf->len`) during the race. However, `bcm_can_tx()` copies the entire frame into the `skb` first, and then passes the `skb` to `can_send()`. `can_send()` validates the `cf->len` field from the *copied* data in the `skb` (via `can_is_can_skb()` or `can_is_canfd_skb()`). If the copied length is invalid, `can_send()` safely drops the packet. Since the validation happens on the copied data, there is no Time-of-Check to Time-of-Use (TOCTOU) vulnerability.
- The worst-case consequence is that a corrupted CAN frame (containing a mix of old and new data) is transmitted on the CAN bus, which is a functional issue but not a kernel security vulnerability.
**2. Denial Of Service: False**
The bug does not cause kernel crashes, hangs, deadlocks, or resource leaks.
- There are other minor data races on timer intervals (`op->kt_ival1`, `op->kt_ival2`) which are updated without a lock. A torn read of these 64-bit `ktime_t` values on 32-bit systems could result in a corrupted timer expiry. However, because the maximum allowed timer value (`BCM_TIMER_SEC_MAX`) ensures the sign bit is always 0, a torn read cannot result in a negative time value. The timer will simply fire at an unexpected positive interval, which does not cause a softirq storm or system hang.
**3. Accessible From Unprivileged Processes: True**
Unprivileged users can create CAN sockets (`PF_CAN`) and use the BCM protocol (`CAN_BCM`). The kernel automatically loads the `can-bcm` module if it is not already loaded, and there are no `capable()` checks restricting the creation of these sockets or the use of `sendmsg()`.
**4. Accessible From User Namespaces: True**
Since the bug is reachable by unprivileged users in the initial namespace, it is also reachable from within user namespaces.
**5. Other Triggers (Network, Remote, Peripheral, Filesystem, VM): False**
The bug is triggered locally via the `sendmsg()` syscall on a CAN socket. It is not triggered by processing ingress network traffic, remote connections, plugging in malicious peripherals, mounting filesystems, or specific VM interactions.
### Conclusion
The KCSAN report highlights a true positive data race, but it is benign from a kernel security perspective. It does not compromise kernel memory safety, cannot be exploited for privilege escalation, and does not cause a Denial of Service.
|
| 6/2 |
2026/05/16 03:25 |
llm |
expert |
0m
Model:
Tokens:
input: 5559
output: 131
thoughts: 127
Thoughts: **Analyzing a KCSAN Data-Race Report in the CAN Subsystem**
Alright, let's dive into this KCSAN report. I've got a data-race flagged in `_copy_from_iter` and `bcm_can_tx`, which is pretty interesting. From what I see, there's a read operation happening within `bcm_can_tx`, specifically within the `skb_put_data` call. That call originates from `bcm_tx_timeout_handler`, which is a high-resolution timer callback, which is significant from a concurrency perspective. On the other hand, the write is occurring in `_copy_from_iter`, going through `memcpy_from_msg`, coming from `bcm_tx_setup` which is called from `bcm_sendmsg`. So, there's a clear read-write conflict here, and my initial thought is there could be potential issues with concurrent access to shared data structures. Given the functions involved, I'm already thinking about potential synchronization problems within the CAN BCM driver's transmit path.
My next step is to get my hands dirty and examine the `net/can/bcm.c` file. This is where I'll need to dig into the implementation of these functions, look at how the data is being handled, and figure out exactly what data is being shared and where. I'll be looking for shared buffers, potential race conditions on the descriptors, and, of course, any missing or incorrect locking. I'm especially interested in the context switches and interrupt handling that occurs with the timer and message-sending mechanisms; I'll be looking for possible non-atomic accesses and other concurrency issues.
|
| 7/2 |
2026/05/16 03:25 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:bcm_tx_setup]
Results: map[SourceCode: 924: /*
925: * bcm_tx_setup - create or update a bcm tx op (for bcm_sendmsg)
926: */
927: static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
928: int ifindex, struct sock *sk)
929: {
930: struct bcm_sock *bo = bcm_sk(sk);
931: struct bcm_op *op;
932: struct canfd_frame *cf;
933: unsigned int i;
934: int err;
935:
936: /* we need a real device to send frames */
937: if (!ifindex)
938: return -ENODEV;
939:
940: /* check nframes boundaries - we need at least one CAN frame */
941: if (msg_head->nframes < 1 || msg_head->nframes > MAX_NFRAMES)
942: return -EINVAL;
943:
944: /* check timeval limitations */
945: if ((msg_head->flags & SETTIMER) && bcm_is_invalid_tv(msg_head))
946: return -EINVAL;
947:
948: /* check the given can_id */
949: op = bcm_find_op(&bo->tx_ops, msg_head, ifindex);
950: if (op) {
951: /* update existing BCM operation */
952:
953: /*
954: * Do we need more space for the CAN frames than currently
955: * allocated? -> This is a _really_ unusual use-case and
956: * therefore (complexity / locking) it is not supported.
957: */
958: if (msg_head->nframes > op->nframes)
959: return -E2BIG;
960:
961: /* update CAN frames content */
962: for (i = 0; i < msg_head->nframes; i++) {
963:
964: cf = op->frames + op->cfsiz * i;
965: err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz);
966:
967: if (op->flags & CAN_FD_FRAME) {
968: if (cf->len > 64)
969: err = -EINVAL;
970: } else {
971: if (cf->len > 8)
972: err = -EINVAL;
973: }
974:
975: if (err < 0)
976: return err;
977:
978: if (msg_head->flags & TX_CP_CAN_ID) {
979: /* copy can_id into frame */
980: cf->can_id = msg_head->can_id;
981: }
982: }
983: op->flags = msg_head->flags;
984:
985: /* only lock for unlikely count/nframes/currframe changes */
986: if (op->nframes != msg_head->nframes ||
987: op->flags & TX_RESET_MULTI_IDX ||
988: op->flags & SETTIMER) {
989:
990: spin_lock_bh(&op->bcm_tx_lock);
991:
992: if (op->nframes != msg_head->nframes ||
993: op->flags & TX_RESET_MULTI_IDX) {
994: /* potentially update changed nframes */
995: op->nframes = msg_head->nframes;
996: /* restart multiple frame transmission */
997: op->currframe = 0;
998: }
999:
1000: if (op->flags & SETTIMER)
1001: op->count = msg_head->count;
1002:
1003: spin_unlock_bh(&op->bcm_tx_lock);
1004: }
1005:
1006: } else {
1007: /* insert new BCM operation for the given can_id */
1008:
1009: op = kzalloc(OPSIZ, GFP_KERNEL);
1010: if (!op)
1011: return -ENOMEM;
1012:
1013: spin_lock_init(&op->bcm_tx_lock);
1014: op->can_id = msg_head->can_id;
1015: op->cfsiz = CFSIZ(msg_head->flags);
1016: op->flags = msg_head->flags;
1017: op->nframes = msg_head->nframes;
1018:
1019: if (op->flags & SETTIMER)
1020: op->count = msg_head->count;
1021:
1022: /* create array for CAN frames and copy the data */
1023: if (msg_head->nframes > 1) {
1024: op->frames = kmalloc_array(msg_head->nframes,
1025: op->cfsiz,
1026: GFP_KERNEL);
1027: if (!op->frames) {
1028: kfree(op);
1029: return -ENOMEM;
1030: }
1031: } else
1032: op->frames = &op->sframe;
1033:
1034: for (i = 0; i < msg_head->nframes; i++) {
1035:
1036: cf = op->frames + op->cfsiz * i;
1037: err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz);
1038: if (err < 0)
1039: goto free_op;
1040:
1041: if (op->flags & CAN_FD_FRAME) {
1042: if (cf->len > 64)
1043: err = -EINVAL;
1044: } else {
1045: if (cf->len > 8)
1046: err = -EINVAL;
1047: }
1048:
1049: if (err < 0)
1050: goto free_op;
1051:
1052: if (msg_head->flags & TX_CP_CAN_ID) {
1053: /* copy can_id into frame */
1054: cf->can_id = msg_head->can_id;
1055: }
1056: }
1057:
1058: /* tx_ops never compare with previous received messages */
1059: op->last_frames = NULL;
1060:
1061: /* bcm_can_tx / bcm_tx_timeout_handler needs this */
1062: op->sk = sk;
1063: op->ifindex = ifindex;
1064:
1065: /* initialize uninitialized (kzalloc) structure */
1066: hrtimer_setup(&op->timer, bcm_tx_timeout_handler, CLOCK_MONOTONIC,
1067: HRTIMER_MODE_REL_SOFT);
1068:
1069: /* currently unused in tx_ops */
1070: hrtimer_setup(&op->thrtimer, hrtimer_dummy_timeout, CLOCK_MONOTONIC,
1071: HRTIMER_MODE_REL_SOFT);
1072:
1073: /* add this bcm_op to the list of the tx_ops */
1074: list_add(&op->list, &bo->tx_ops);
1075:
1076: } /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */
1077:
1078: if (op->flags & SETTIMER) {
1079: /* set timer values */
1080: op->ival1 = msg_head->ival1;
1081: op->ival2 = msg_head->ival2;
1082: op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
1083: op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
1084:
1085: /* disable an active timer due to zero values? */
1086: if (!op->kt_ival1 && !op->kt_ival2)
1087: hrtimer_cancel(&op->timer);
1088: }
1089:
1090: if (op->flags & STARTTIMER) {
1091: hrtimer_cancel(&op->timer);
1092: /* spec: send CAN frame when starting timer */
1093: op->flags |= TX_ANNOUNCE;
1094: }
1095:
1096: if (op->flags & TX_ANNOUNCE)
1097: bcm_can_tx(op);
1098:
1099: if (op->flags & STARTTIMER)
1100: bcm_tx_start_timer(op);
1101:
1102: return msg_head->nframes * op->cfsiz + MHSIZ;
1103:
1104: free_op:
1105: if (op->frames != &op->sframe)
1106: kfree(op->frames);
1107: kfree(op);
1108: return err;
1109: }
SourceFile:net/can/bcm.c]
|
| 8/2 |
2026/05/16 03:25 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:bcm_can_tx]
Results: map[SourceCode: 288: /*
289: * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface
290: * of the given bcm tx op
291: */
292: static void bcm_can_tx(struct bcm_op *op)
293: {
294: struct sk_buff *skb;
295: struct can_skb_ext *csx;
296: struct net_device *dev;
297: struct canfd_frame *cf;
298: int err;
299:
300: /* no target device? => exit */
301: if (!op->ifindex)
302: return;
303:
304: /* read currframe under lock protection */
305: spin_lock_bh(&op->bcm_tx_lock);
306: cf = op->frames + op->cfsiz * op->currframe;
307: spin_unlock_bh(&op->bcm_tx_lock);
308:
309: dev = dev_get_by_index(sock_net(op->sk), op->ifindex);
310: if (!dev) {
311: /* RFC: should this bcm_op remove itself here? */
312: return;
313: }
314:
315: skb = alloc_skb(op->cfsiz, gfp_any());
316: if (!skb)
317: goto out;
318:
319: csx = can_skb_ext_add(skb);
320: if (!csx) {
321: kfree_skb(skb);
322: goto out;
323: }
324:
325: csx->can_iif = dev->ifindex;
326:
327: skb_put_data(skb, cf, op->cfsiz);
328:
329: /* send with loopback */
330: skb->dev = dev;
331: can_skb_set_owner(skb, op->sk);
332: err = can_send(skb, 1);
333:
334: /* update currframe and count under lock protection */
335: spin_lock_bh(&op->bcm_tx_lock);
336:
337: if (!err)
338: op->frames_abs++;
339:
340: op->currframe++;
341:
342: /* reached last frame? */
343: if (op->currframe >= op->nframes)
344: op->currframe = 0;
345:
346: if (op->count > 0)
347: op->count--;
348:
349: spin_unlock_bh(&op->bcm_tx_lock);
350: out:
351: dev_put(dev);
352: }
SourceFile:net/can/bcm.c]
|
| 9/2 |
2026/05/16 03:25 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:bcm_tx_timeout_handler]
Results: map[SourceCode: 453: /* bcm_tx_timeout_handler - performs cyclic CAN frame transmissions */
454: static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
455: {
456: struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
457: struct bcm_msg_head msg_head;
458:
459: if (op->kt_ival1 && (op->count > 0)) {
460: bcm_can_tx(op);
461: if (!op->count && (op->flags & TX_COUNTEVT)) {
462:
463: /* create notification to user */
464: memset(&msg_head, 0, sizeof(msg_head));
465: msg_head.opcode = TX_EXPIRED;
466: msg_head.flags = op->flags;
467: msg_head.count = op->count;
468: msg_head.ival1 = op->ival1;
469: msg_head.ival2 = op->ival2;
470: msg_head.can_id = op->can_id;
471: msg_head.nframes = 0;
472:
473: bcm_send_to_user(op, &msg_head, NULL, 0);
474: }
475:
476: } else if (op->kt_ival2) {
477: bcm_can_tx(op);
478: }
479:
480: return bcm_tx_set_expiry(op, &op->timer) ?
481: HRTIMER_RESTART : HRTIMER_NORESTART;
482: }
SourceFile:net/can/bcm.c]
|
| 10/2 |
2026/05/16 03:25 |
llm |
expert |
0m
Model:
Tokens:
input: 10443
output: 41
thoughts: 1466
Thoughts: **Data Race Analysis in BCM CAN TX/Setup**
Okay, let's break this down. It seems we have a classic data race situation between `bcm_can_tx` and `bcm_tx_setup`. My initial assessment is that `bcm_can_tx` is reading CAN frames, while `bcm_tx_setup` is simultaneously writing to them. The key issue is that `bcm_can_tx` reads `op->currframe` under a lock (`op->bcm_tx_lock`) to determine which frame to access, but then *drops the lock* and reads the CAN frame data itself (`cf = op->frames + op->cfsiz * op->currframe`) without any protection. Meanwhile, `bcm_tx_setup` writes to these CAN frames using `memcpy_from_msg` *without* holding that same lock. The lock only comes into play later in `bcm_tx_setup`, and only under specific conditions related to changes in frame count and flags.
So, the race is on the contents of the CAN frame itself. `bcm_can_tx` copies the frame into an `skb` (socket buffer) and sends it. If `bcm_tx_setup` updates the frame concurrently, the sent frame could be a mix of old and new data, leading to a torn read. The size of the copy is `op->cfsiz`, and it's either `sizeof(struct can_frame)` or `sizeof(struct canfd_frame)`. `skb_put_data` is just a `memcpy`, which could read torn data if it's updated concurrently.
Now, is there any memory corruption? `bcm_can_tx` allocates an `skb` of size `op->cfsiz`, and the size passed to `skb_put_data` is also `op->cfsiz`. `op->cfsiz` is constant for an `op` structure and `bcm_tx_setup` checks if the `nframes` to write to is larger than the allocated space, returning `-E2BIG` if it is. But, what if the number of frames to write to, `msg_head->nframes`, is smaller than `op->nframes`? `bcm_tx_setup` will only update the first `msg_head->nframes`. Then, it changes the value of `op->nframes` *under the lock* (if they're different). But, `bcm_can_tx` may be running concurrently. However, it looks like `bcm_can_tx` uses `op->cfsiz` to copy the CAN frame. This should be safe because `op->frames` is allocated with `op->nframes * op->cfsiz` bytes, and `op->currframe < op->nframes`. Also, `op->nframes` can only decrease or stay the same.
The interesting part is `cf->len`. `bcm_tx_setup` copies the data and then checks `cf->len` for validity (<= 8 for CAN, <= 64 for CAN FD). If `memcpy_from_msg` copies a malicious `cf->len` (e.g., 255), and then `bcm_can_tx` reads and sends the frame, would this cause an issue? `bcm_can_tx` sends the skb via `can_send(skb, 1)`. I *believe* the CAN driver uses `cf->len` to determine how many bytes of payload to send. Therefore, an out-of-bounds read in the CAN driver could happen if `cf->len` is larger than the maximum payload. I need to confirm this by checking `can_send` and the specific CAN driver code.
|
| 11/2 |
2026/05/16 03:25 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/af_can.c IncludeLines:true Name:can_send]
Results: map[SourceCode: 186: /**
187: * can_send - transmit a CAN frame (optional with local loopback)
188: * @skb: pointer to socket buffer with CAN frame in data section
189: * @loop: loopback for listeners on local CAN sockets (recommended default!)
190: *
191: * Due to the loopback this routine must not be called from hardirq context.
192: *
193: * Return:
194: * 0 on success
195: * -ENETDOWN when the selected interface is down
196: * -ENOBUFS on full driver queue (see net_xmit_errno())
197: * -ENOMEM when local loopback failed at calling skb_clone()
198: * -EPERM when trying to send on a non-CAN interface
199: * -EMSGSIZE CAN frame size is bigger than CAN interface MTU
200: * -EINVAL when the skb->data does not contain a valid CAN frame
201: */
202: int can_send(struct sk_buff *skb, int loop)
203: {
204: struct sk_buff *newskb = NULL;
205: struct can_pkg_stats *pkg_stats = dev_net(skb->dev)->can.pkg_stats;
206: int err = -EINVAL;
207:
208: if (can_is_canxl_skb(skb)) {
209: skb->protocol = htons(ETH_P_CANXL);
210: } else if (can_is_can_skb(skb)) {
211: skb->protocol = htons(ETH_P_CAN);
212: } else if (can_is_canfd_skb(skb)) {
213: struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
214:
215: skb->protocol = htons(ETH_P_CANFD);
216:
217: /* set CAN FD flag for CAN FD frames by default */
218: cfd->flags |= CANFD_FDF;
219: } else {
220: goto inval_skb;
221: }
222:
223: /* Make sure the CAN frame can pass the selected CAN netdevice. */
224: if (unlikely(skb->len > READ_ONCE(skb->dev->mtu))) {
225: err = -EMSGSIZE;
226: goto inval_skb;
227: }
228:
229: if (unlikely(skb->dev->type != ARPHRD_CAN)) {
230: err = -EPERM;
231: goto inval_skb;
232: }
233:
234: if (unlikely(!(skb->dev->flags & IFF_UP))) {
235: err = -ENETDOWN;
236: goto inval_skb;
237: }
238:
239: skb->ip_summed = CHECKSUM_UNNECESSARY;
240:
241: skb_reset_mac_header(skb);
242: skb_reset_network_header(skb);
243: skb_reset_transport_header(skb);
244:
245: if (loop) {
246: /* local loopback of sent CAN frames */
247:
248: /* indication for the CAN driver: do loopback */
249: skb->pkt_type = PACKET_LOOPBACK;
250:
251: /* The reference to the originating sock may be required
252: * by the receiving socket to check whether the frame is
253: * its own. Example: can_raw sockopt CAN_RAW_RECV_OWN_MSGS
254: * Therefore we have to ensure that skb->sk remains the
255: * reference to the originating sock by restoring skb->sk
256: * after each skb_clone() or skb_orphan() usage.
257: */
258:
259: if (!(skb->dev->flags & IFF_ECHO)) {
260: /* If the interface is not capable to do loopback
261: * itself, we do it here.
262: */
263: newskb = skb_clone(skb, GFP_ATOMIC);
264: if (!newskb) {
265: kfree_skb(skb);
266: return -ENOMEM;
267: }
268:
269: can_skb_set_owner(newskb, skb->sk);
270: newskb->ip_summed = CHECKSUM_UNNECESSARY;
271: newskb->pkt_type = PACKET_BROADCAST;
272: }
273: } else {
274: /* indication for the CAN driver: no loopback required */
275: skb->pkt_type = PACKET_HOST;
276: }
277:
278: /* send to netdevice */
279: err = dev_queue_xmit(skb);
280: if (err > 0)
281: err = net_xmit_errno(err);
282:
283: if (err) {
284: kfree_skb(newskb);
285: return err;
286: }
287:
288: if (newskb)
289: netif_rx(newskb);
290:
291: /* update statistics */
292: atomic_long_inc(&pkg_stats->tx_frames);
293: atomic_long_inc(&pkg_stats->tx_frames_delta);
294:
295: return 0;
296:
297: inval_skb:
298: kfree_skb(skb);
299: return err;
300: }
SourceFile:net/can/af_can.c]
|
| 12/2 |
2026/05/16 03:25 |
llm |
expert |
0m
Model:
Tokens:
input: 14573
output: 95
thoughts: 41
Thoughts: Okay, let me think this through.
**Analyzing CAN Frame Length Checks in the Kernel**
Alright, I'm digging into these CAN driver functions, specifically `can_is_can_skb` and `can_is_canfd_skb`. My initial thought is that these functions, given their names, likely perform some crucial validation on the Socket Buffer (skb) to determine if it's a standard CAN frame or a CAN FD frame. The names themselves are pretty descriptive, so my gut tells me it's about classification based on frame type.
It's pretty standard practice to check the length of the `skb` when dealing with network frames. That usually tells you the amount of payload or if the frame looks right. So, I need to see *how* they're checking the length and *what* criteria they're using to differentiate between frame types. Are they checking against a hardcoded maximum length? Is there a bitmask involved? Do they use offset and size within the `skb` to check relevant fields? Those are the kinds of questions I'm going to want answered by examining the code. Time to dive in and confirm my hypothesis.
|
| 13/2 |
2026/05/16 03:25 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/can/skb.h IncludeLines:true Name:can_is_can_skb]
Results: map[SourceCode: 88: static inline bool can_is_can_skb(const struct sk_buff *skb)
89: {
90: struct can_frame *cf = (struct can_frame *)skb->data;
91:
92: /* the CAN specific type of skb is identified by its data length */
93: return (skb->len == CAN_MTU && cf->len <= CAN_MAX_DLEN);
94: }
SourceFile:include/linux/can/skb.h]
|
| 14/2 |
2026/05/16 03:25 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/can/skb.h IncludeLines:true Name:can_is_canfd_skb]
Results: map[SourceCode: 96: static inline bool can_is_canfd_skb(const struct sk_buff *skb)
97: {
98: struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
99:
100: /* the CAN specific type of skb is identified by its data length */
101: return (skb->len == CANFD_MTU && cfd->len <= CANFD_MAX_DLEN);
102: }
SourceFile:include/linux/can/skb.h]
|
| 15/2 |
2026/05/16 03:25 |
llm |
expert |
0m
Model:
Tokens:
input: 15273
output: 48
thoughts: 631
Thoughts: **Analyzing Potential Data Races in CAN Frame Handling**
Okay, so I'm looking at how CAN frames are handled and trying to spot any potential race conditions, specifically around the `cf->len` field. My initial thought was that `can_is_can_skb` and `can_is_canfd_skb` check the frame length (`cf->len`), right? I see that it uses `skb->len` and also compares it with `cf->len`. It looks like it's reading `cf->len` from `skb->data`.
Then I remember that `skb->data` is populated by `skb_put_data(skb, cf, op->cfsiz)` inside `bcm_can_tx`. This is key. The critical point is that a *copy* is done; `cf` is copied into `skb->data`. Then, `can_send` is called. It checks `cf->len` *again*, but from `skb->data`.
Ah ha! Since the copy is done *before* `can_send` checks the data, there's no TOCTOU issue on `cf->len` between the driver and `can_send`. `can_send` will read the copied value. It's safe since the copy has already happened, it will check the data in the SKB itself. The copy is done first!
Now, does anything modify `skb->data` after `can_send`? I don't think so, and it is a local SKB. So, `can_send` will see the same `cf->len` value as the driver saw in the copy operation.
The interesting question is what happens if there's corruption during the copy, like a torn read? Then `can_send` checks the data, specifically in that block:
```c
if (can_is_canxl_skb(skb)) {
skb->protocol = htons(ETH_P_CANXL);
} else if (can_is_can_skb(skb)) {
skb->protocol = htons(ETH_P_CAN);
} else if (can_is_canfd_skb(skb)) {
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
skb->protocol = htons(ETH_P_CANFD);
/* set CAN FD flag for CAN FD frames by default */
cfd->flags |= CANFD_FDF;
} else {
goto inval_skb;
}
```
If `cf->len` is invalid β too large for the frame type β `can_is_can_skb` or `can_is_canfd_skb` will return false. The code *should* go to `inval_skb` and drop the packet. This seems to be the intended fail-safe.
But what if `skb->len == CAN_MTU` and `cf->len > CAN_MAX_DLEN`? Then it will go to `inval_skb` because it is invalid, right?
I wonder about the case where `can_is_canxl_skb` could return `true`. I should look into that code path now.
|
| 16/2 |
2026/05/16 03:25 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/can/skb.h IncludeLines:true Name:can_is_canxl_skb]
Results: map[SourceCode: 104: static inline bool can_is_canxl_skb(const struct sk_buff *skb)
105: {
106: const struct canxl_frame *cxl = (struct canxl_frame *)skb->data;
107:
108: if (skb->len < CANXL_HDR_SIZE + CANXL_MIN_DLEN || skb->len > CANXL_MTU)
109: return false;
110:
111: /* this also checks valid CAN XL data length boundaries */
112: if (skb->len != CANXL_HDR_SIZE + cxl->len)
113: return false;
114:
115: return cxl->flags & CANXL_XLF;
116: }
SourceFile:include/linux/can/skb.h]
|
| 17/2 |
2026/05/16 03:25 |
llm |
expert |
0m
Model:
Tokens:
input: 16929
output: 39
thoughts: 253
Thoughts: **Analyzing a Potential CAN XL Issue in the BCM Implementation**
Okay, so I'm looking at this `can_is_canxl_skb` function, and it's doing a length check: `skb->len != CANXL_HDR_SIZE + cxl->len`. Seems straightforward enough, but my expert eye is raising a red flag. The thing is, `bcm_can_tx` allocates the Socket Buffer (SKB) with `op->cfsiz`. `cfsiz` is set up to be either `CAN_MTU` or `CANFD_MTU` depending on the situation.
Now, `CAN_MTU` is 16 bytes and `CANFD_MTU` is 72 bytes. We know `CANXL_HDR_SIZE` is 12 bytes, and `CANXL_MIN_DLEN` is 1. Therefore, when the SKB is allocated, `skb->len` is either 16 or 72 bytes. Let's trace this out. If `skb->len` is 16, then `CANXL_HDR_SIZE + cxl->len == 16` would imply `cxl->len == 4`. If the SKB length is 72, then `CANXL_HDR_SIZE + cxl->len == 72` implies `cxl->len == 60`.
This doesn't quite sit right with me. I'm pretty sure BCM, as it's implemented now, doesn't actually *support* CAN XL yet, so these scenarios don't seem like they should be possible. I'm wondering if this length check is correct within the current BCM framework, or if it's potentially leftover from some experimental CAN XL integration. I'd better double check the `CFSIZ` macro definition within `net/can/bcm.c` to be certain.
|
| 18/2 |
2026/05/16 03:26 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:CFSIZ]
Results: map[SourceCode: SourceFile:]
|
| 19/2 |
2026/05/16 03:26 |
llm |
expert |
0m
Model:
Tokens:
input: 17659
output: 18
thoughts: 8
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm already deep in the weeds of whatever problem this refers to:
**Grasping for Context with `grep`**
Alright, the wheels are turning. "CFSIZ"... that rings a bell, but I need to quickly refresh my memory and pinpoint what we're dealing with. It's time to leverage the power of the command line. Let's see if I can find some immediate relevance. I'm going to reach for `grep` right away. Itβs the fastest way to get a quick overview of where CFSIZ appears in relation to the codebase or data in my environment. I'm hoping to quickly identify relevant files, variables, configurations, anything really, that mentions `CFSIZ`. This initial search will help me understand its context and guide my next steps, giving me something more to work with. Let's see what `grep` unearths!
|
| 20/2 |
2026/05/16 03:26 |
tool |
grepper |
0m
Args: map[Expression:#define CFSIZ]
Results: map[Output:net/can/bcm.c=167=static bool bcm_is_invalid_tv(struct bcm_msg_head *msg_head)
--
net/can/bcm.c-181-
net/can/bcm.c:182:#define CFSIZ(flags) ((flags & CAN_FD_FRAME) ? CANFD_MTU : CAN_MTU)
net/can/bcm.c-183-#define OPSIZ sizeof(struct bcm_op)
]
|
| 21/2 |
2026/05/16 03:26 |
llm |
expert |
0m
Model:
Tokens:
input: 18014
output: 40
thoughts: 365
Thoughts: **Analyzing CAN XL Frame Handling in the Driver**
Okay, so I'm looking at how we're dealing with CAN XL frames, specifically how the system distinguishes them from standard CAN FD frames. The key seems to hinge on `op->cfsiz`, which is either `CANFD_MTU` (72) or `CAN_MTU` (16). The concern is around the situation where `skb->len` equals 72. That's a valid size for both a CAN FD frame with maximum payload and CAN XL.
If the skb length is 72, then `can_is_canxl_skb` *might* return true if `cxl->len` equals 60 and `cxl->flags` has the `CANXL_XLF` flag set. It seems like the critical piece here is that if `can_is_canxl_skb` *does* return true, the `skb->protocol` gets set to `htons(ETH_P_CANXL)`. That's important because it tells the driver this is a CAN XL frame.
So, the driver will see `skb->len` equal to 72, which is valid for CAN XL. But, and here's the tricky part, the `skb->data` comes from a `canfd_frame`, specifically from the `cf` variable. My initial concern was a data race, but if a user *could* craft a `canfd_frame` that looks like a valid `canxl_frame`, I realize they could bypass protections anyway! We need to follow this line of thought.
We know `bcm_tx_setup` is responsible for copying the user-provided `msg` into the `cf` variable, which is where the data comes from. But, *wait*, the code in `bcm_tx_setup` seems to have some checks:
```c
if (op->flags & CAN_FD_FRAME) {
if (cf->len > 64)
err = -EINVAL;
} else {
if (cf->len > 8)
err = -EINVAL;
}
```
Okay, this is important. It limits `cf->len` to 64 for CAN FD frames and 8 for standard CAN frames, but the CAN XL frame's length is offset by 8 bytes. Let's dig into the `struct canxl_frame` definition.
|
| 22/2 |
2026/05/16 03:26 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/can.h IncludeLines:true Name:canxl_frame]
Results: map[SourceCode: 202: /**
203: * struct canxl_frame - CAN with e'X'tended frame 'L'ength frame structure
204: * @prio: 11 bit arbitration priority with zero'ed CAN_*_FLAG flags / VCID
205: * @flags: additional flags for CAN XL
206: * @sdt: SDU (service data unit) type
207: * @len: frame payload length in byte (CANXL_MIN_DLEN .. CANXL_MAX_DLEN)
208: * @af: acceptance field
209: * @data: CAN XL frame payload (CANXL_MIN_DLEN .. CANXL_MAX_DLEN byte)
210: *
211: * @prio shares the same position as @can_id from struct can[fd]_frame.
212: */
213: struct canxl_frame {
214: canid_t prio; /* 11 bit priority for arbitration / 8 bit VCID */
215: __u8 flags; /* additional flags for CAN XL */
216: __u8 sdt; /* SDU (service data unit) type */
217: __u16 len; /* frame payload length in byte */
218: __u32 af; /* acceptance field */
219: __u8 data[CANXL_MAX_DLEN];
220: };
SourceFile:include/uapi/linux/can.h]
|
| 23/2 |
2026/05/16 03:26 |
llm |
expert |
0m
Model:
Tokens:
input: 19379
output: 40
thoughts: 26
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:
**Analyzing the Frame Structure**
Alright, let's see... I'm tracing the layout, trying to get a handle on where things reside within this structure. Specifically, I'm focusing on that `len` field. Thinking about the `canfd_frame` definition, I'm mentally calculating its offset.
So, starting with the base, and knowing the standard layout, let me see... I'm working from the beginning of the `canfd_frame`. I have to account for the leading fields. It looks like the data length code, or `len`, is located at offset 4 (assuming a typical framing). Then I recall that I have to increment for 1 additional item, with another plus 1 increment. It follows that the offset I am looking for `len` is likely at position 6.
I should double-check that this aligns perfectly with my understanding of the data format, but that's my initial assessment of the `canfd_frame` and the field offsets.
|
| 24/2 |
2026/05/16 03:26 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/can.h IncludeLines:true Name:canfd_frame]
Results: map[SourceCode: 164: /**
165: * struct canfd_frame - CAN flexible data rate frame structure
166: * @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
167: * @len: frame payload length in byte (0 .. CANFD_MAX_DLEN)
168: * @flags: additional flags for CAN FD
169: * @__res0: reserved / padding
170: * @__res1: reserved / padding
171: * @data: CAN FD frame payload (up to CANFD_MAX_DLEN byte)
172: */
173: struct canfd_frame {
174: canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
175: __u8 len; /* frame payload length in byte */
176: __u8 flags; /* additional flags for CAN FD */
177: __u8 __res0; /* reserved / padding */
178: __u8 __res1; /* reserved / padding */
179: __u8 data[CANFD_MAX_DLEN] __attribute__((aligned(8)));
180: };
SourceFile:include/uapi/linux/can.h]
|
| 25/2 |
2026/05/16 03:26 |
llm |
expert |
0m
Model:
Tokens:
input: 20052
output: 45
thoughts: 1749
Thoughts: **Analyzing CAN FD/CAN XL Vulnerabilities in the BCM Layer**
Okay, so I've been dissecting this BCM code. Let's start with the base assumptions and the structure of `canfd_frame`. My initial thought was: the offset of `len` in a `canfd_frame` is at 4, and `flags` is at 5, and it appears that `__res0` and `__res1` (at offsets 6 and 7) overlap with part of a `canxl_frame->len` (16-bit). *That* seemed promising for an overwrite, but `bcm_tx_setup` doesn't check those fields. So a crafted `canfd_frame` could masquerade as a valid `canxl_frame`. If that happened, `can_send` *would* treat it as CAN XL and set the protocol, which then goes to the driver. The driver then *could* send it, assuming it supports CAN XL. But that's not the critical data race I'm looking for - it's just a misinterpretation issue.
Alright, back to the data race. The core is the `cf` buffer (either `canfd_frame` or `can_frame`). `bcm_can_tx` copies this `cf` into `skb->data`. *Then* `can_send` checks `cf->len` (from that copy). I see that `bcm_tx_setup` validates the length during copy, but crucially, *after* the `memcpy_from_msg` call. That call overwrites the contents of `cf`, so if the input has an invalid `cf->len` value (say, 255), the code proceeds to read it *before* the invalid check occurs. This is a classic Time-of-Check-Time-of-Use (TOCTOU) vulnerability! The invalid `cf->len` is then copied into the SKB, and `can_send`'s check will trigger the `inval_skb` handling. This drops the packet, so that's actually *safe*.
I scanned the code again to see if `cf->len` is used elsewhere before `can_send` in `bcm_can_tx`, but it's not. Similarly, I looked for other fields in `cf` that could cause corruption. `can_id` and the `data` are basically payload or identifiers that are not used to write to kernel memory. If a new frame is sent when it's being updated, the data on the CAN bus is corrupt, but it doesn't cause memory corruption in the kernel. The `TX_CP_CAN_ID` flag just copies the can id to the frame which isn't an issue.
Then I considered `op->cfsiz`. That's set when `op` is created, and *never* changed. Also, `op->nframes` is checked under a lock, so the frame indexes into the frames are all valid. What about reallocating the `op->frames` buffer? Not supported. The code errors out if `msg_head->nframes > op->nframes`.
Okay, there's one other field - `op->flags` - which is updated without a lock! It looks like there's no memory corruption potential. I need to keep looking.
|
| 26/2 |
2026/05/16 03:26 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:net/can/bcm.c IncludeSnippetLines:10 Name:bcm_op::flags]
Results: map[References:[map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_delete_rx_op SourceFile:net/can/bcm.c SourceLine:840 SourceSnippet: 830: /*
831: * bcm_delete_rx_op - find and remove a rx op (returns number of removed ops)
832: */
833: static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
834: int ifindex)
835: {
836: struct bcm_op *op, *n;
837:
838: list_for_each_entry_safe(op, n, ops, list) {
839: if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) &&
840: (op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME)) {
841:
842: /* disable automatic timer on frame reception */
843: op->flags |= RX_NO_AUTOTIMER;
844:
845: /*
846: * Don't care if we're bound or not (due to netdev
847: * problems) can_rx_unregister() is always a save
848: * thing to do here.
849: */
850: if (op->ifindex) {
] map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:bcm_delete_rx_op SourceFile:net/can/bcm.c SourceLine:843 SourceSnippet: 833: static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
834: int ifindex)
835: {
836: struct bcm_op *op, *n;
837:
838: list_for_each_entry_safe(op, n, ops, list) {
839: if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) &&
840: (op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME)) {
841:
842: /* disable automatic timer on frame reception */
843: op->flags |= RX_NO_AUTOTIMER;
844:
845: /*
846: * Don't care if we're bound or not (due to netdev
847: * problems) can_rx_unregister() is always a save
848: * thing to do here.
849: */
850: if (op->ifindex) {
851: /*
852: * Only remove subscriptions that had not
853: * been removed due to NETDEV_UNREGISTER
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_delete_tx_op SourceFile:net/can/bcm.c SourceLine:891 SourceSnippet: 881: /*
882: * bcm_delete_tx_op - find and remove a tx op (returns number of removed ops)
883: */
884: static int bcm_delete_tx_op(struct list_head *ops, struct bcm_msg_head *mh,
885: int ifindex)
886: {
887: struct bcm_op *op, *n;
888:
889: list_for_each_entry_safe(op, n, ops, list) {
890: if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) &&
891: (op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME)) {
892: list_del_rcu(&op->list);
893: bcm_remove_op(op);
894: return 1; /* done */
895: }
896: }
897:
898: return 0; /* not found */
899: }
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_find_op SourceFile:net/can/bcm.c SourceLine:789 SourceSnippet: 779: /*
780: * helpers for bcm_op handling: find & delete bcm [rx|tx] op elements
781: */
782: static struct bcm_op *bcm_find_op(struct list_head *ops,
783: struct bcm_msg_head *mh, int ifindex)
784: {
785: struct bcm_op *op;
786:
787: list_for_each_entry(op, ops, list) {
788: if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) &&
789: (op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME))
790: return op;
791: }
792:
793: return NULL;
794: }
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_proc_show SourceFile:net/can/bcm.c SourceLine:236 SourceSnippet: 226:
227: unsigned long reduction;
228:
229: /* print only active entries & prevent division by zero */
230: if (!op->frames_abs)
231: continue;
232:
233: seq_printf(m, "rx_op: %03X %-5s ", op->can_id,
234: bcm_proc_getifname(net, ifname, op->ifindex));
235:
236: if (op->flags & CAN_FD_FRAME)
237: seq_printf(m, "(%u)", op->nframes);
238: else
239: seq_printf(m, "[%u]", op->nframes);
240:
241: seq_printf(m, "%c ", (op->flags & RX_CHECK_DLC) ? 'd' : ' ');
242:
243: if (op->kt_ival1)
244: seq_printf(m, "timeo=%lld ",
245: (long long)ktime_to_us(op->kt_ival1));
246:
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_proc_show SourceFile:net/can/bcm.c SourceLine:241 SourceSnippet: 231: continue;
232:
233: seq_printf(m, "rx_op: %03X %-5s ", op->can_id,
234: bcm_proc_getifname(net, ifname, op->ifindex));
235:
236: if (op->flags & CAN_FD_FRAME)
237: seq_printf(m, "(%u)", op->nframes);
238: else
239: seq_printf(m, "[%u]", op->nframes);
240:
241: seq_printf(m, "%c ", (op->flags & RX_CHECK_DLC) ? 'd' : ' ');
242:
243: if (op->kt_ival1)
244: seq_printf(m, "timeo=%lld ",
245: (long long)ktime_to_us(op->kt_ival1));
246:
247: if (op->kt_ival2)
248: seq_printf(m, "thr=%lld ",
249: (long long)ktime_to_us(op->kt_ival2));
250:
251: seq_printf(m, "# recv %ld (%ld) => reduction: ",
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_proc_show SourceFile:net/can/bcm.c SourceLine:265 SourceSnippet: 255:
256: seq_printf(m, "%s%ld%%\n",
257: (reduction == 100) ? "near " : "", reduction);
258: }
259:
260: list_for_each_entry(op, &bo->tx_ops, list) {
261:
262: seq_printf(m, "tx_op: %03X %s ", op->can_id,
263: bcm_proc_getifname(net, ifname, op->ifindex));
264:
265: if (op->flags & CAN_FD_FRAME)
266: seq_printf(m, "(%u) ", op->nframes);
267: else
268: seq_printf(m, "[%u] ", op->nframes);
269:
270: if (op->kt_ival1)
271: seq_printf(m, "t1=%lld ",
272: (long long)ktime_to_us(op->kt_ival1));
273:
274: if (op->kt_ival2)
275: seq_printf(m, "t2=%lld ",
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_read_op SourceFile:net/can/bcm.c SourceLine:913 SourceSnippet: 903: */
904: static int bcm_read_op(struct list_head *ops, struct bcm_msg_head *msg_head,
905: int ifindex)
906: {
907: struct bcm_op *op = bcm_find_op(ops, msg_head, ifindex);
908:
909: if (!op)
910: return -EINVAL;
911:
912: /* put current values into msg_head */
913: msg_head->flags = op->flags;
914: msg_head->count = op->count;
915: msg_head->ival1 = op->ival1;
916: msg_head->ival2 = op->ival2;
917: msg_head->nframes = op->nframes;
918:
919: bcm_send_to_user(op, msg_head, op->frames, 0);
920:
921: return MHSIZ;
922: }
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_rx_changed SourceFile:net/can/bcm.c SourceLine:503 SourceSnippet: 493:
494: /* prevent statistics overflow */
495: if (op->frames_filtered > ULONG_MAX/100)
496: op->frames_filtered = op->frames_abs = 0;
497:
498: /* this element is not throttled anymore */
499: data->flags &= ~RX_THR;
500:
501: memset(&head, 0, sizeof(head));
502: head.opcode = RX_CHANGED;
503: head.flags = op->flags;
504: head.count = op->count;
505: head.ival1 = op->ival1;
506: head.ival2 = op->ival2;
507: head.can_id = op->can_id;
508: head.nframes = 1;
509:
510: bcm_send_to_user(op, &head, data, 1);
511: }
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_rx_cmp_to_index SourceFile:net/can/bcm.c SourceLine:594 SourceSnippet: 584:
585: /* do a real check in CAN frame data section */
586: for (i = 0; i < rxdata->len; i += 8) {
587: if ((get_u64(cf, i) & get_u64(rxdata, i)) !=
588: (get_u64(cf, i) & get_u64(lcf, i))) {
589: bcm_rx_update_and_send(op, lcf, rxdata, traffic_flags);
590: return;
591: }
592: }
593:
594: if (op->flags & RX_CHECK_DLC) {
595: /* do a real check in CAN frame length */
596: if (rxdata->len != lcf->len) {
597: bcm_rx_update_and_send(op, lcf, rxdata, traffic_flags);
598: return;
599: }
600: }
601: }
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_rx_handler SourceFile:net/can/bcm.c SourceLine:710 SourceSnippet: 700: {
701: struct bcm_op *op = (struct bcm_op *)data;
702: const struct canfd_frame *rxframe = (struct canfd_frame *)skb->data;
703: unsigned int i;
704: unsigned char traffic_flags;
705:
706: if (op->can_id != rxframe->can_id)
707: return;
708:
709: /* make sure to handle the correct frame type (CAN / CAN FD) */
710: if (op->flags & CAN_FD_FRAME) {
711: if (!can_is_canfd_skb(skb))
712: return;
713: } else {
714: if (!can_is_can_skb(skb))
715: return;
716: }
717:
718: /* disable timeout */
719: hrtimer_cancel(&op->timer);
720:
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_rx_handler SourceFile:net/can/bcm.c SourceLine:728 SourceSnippet: 718: /* disable timeout */
719: hrtimer_cancel(&op->timer);
720:
721: /* save rx timestamp */
722: op->rx_stamp = skb->tstamp;
723: /* save originator for recvfrom() */
724: op->rx_ifindex = skb->dev->ifindex;
725: /* update statistics */
726: op->frames_abs++;
727:
728: if (op->flags & RX_RTR_FRAME) {
729: /* send reply for RTR-request (placed in op->frames[0]) */
730: bcm_can_tx(op);
731: return;
732: }
733:
734: /* compute flags to distinguish between own/local/remote CAN traffic */
735: traffic_flags = 0;
736: if (skb->sk) {
737: traffic_flags |= RX_LOCAL;
738: if (skb->sk == op->sk)
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_rx_handler SourceFile:net/can/bcm.c SourceLine:742 SourceSnippet: 732: }
733:
734: /* compute flags to distinguish between own/local/remote CAN traffic */
735: traffic_flags = 0;
736: if (skb->sk) {
737: traffic_flags |= RX_LOCAL;
738: if (skb->sk == op->sk)
739: traffic_flags |= RX_OWN;
740: }
741:
742: if (op->flags & RX_FILTER_ID) {
743: /* the easiest case */
744: bcm_rx_update_and_send(op, op->last_frames, rxframe,
745: traffic_flags);
746: goto rx_starttimer;
747: }
748:
749: if (op->nframes == 1) {
750: /* simple compare with index 0 */
751: bcm_rx_cmp_to_index(op, 0, rxframe, traffic_flags);
752: goto rx_starttimer;
] map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:bcm_rx_setup SourceFile:net/can/bcm.c SourceLine:1167 SourceSnippet:1157: err = memcpy_from_msg(op->frames, msg,
1158: msg_head->nframes * op->cfsiz);
1159: if (err < 0)
1160: return err;
1161:
1162: /* clear last_frames to indicate 'nothing received' */
1163: memset(op->last_frames, 0, msg_head->nframes * op->cfsiz);
1164: }
1165:
1166: op->nframes = msg_head->nframes;
1167: op->flags = msg_head->flags;
1168:
1169: /* Only an update -> do not call can_rx_register() */
1170: do_rx_register = 0;
1171:
1172: } else {
1173: /* insert new BCM operation for the given can_id */
1174: op = kzalloc(OPSIZ, GFP_KERNEL);
1175: if (!op)
1176: return -ENOMEM;
1177:
] map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:bcm_rx_setup SourceFile:net/can/bcm.c SourceLine:1182 SourceSnippet:1172: } else {
1173: /* insert new BCM operation for the given can_id */
1174: op = kzalloc(OPSIZ, GFP_KERNEL);
1175: if (!op)
1176: return -ENOMEM;
1177:
1178: spin_lock_init(&op->bcm_tx_lock);
1179: op->can_id = msg_head->can_id;
1180: op->nframes = msg_head->nframes;
1181: op->cfsiz = CFSIZ(msg_head->flags);
1182: op->flags = msg_head->flags;
1183:
1184: if (msg_head->nframes > 1) {
1185: /* create array for CAN frames and copy the data */
1186: op->frames = kmalloc_array(msg_head->nframes,
1187: op->cfsiz,
1188: GFP_KERNEL);
1189: if (!op->frames) {
1190: kfree(op);
1191: return -ENOMEM;
1192: }
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_rx_setup SourceFile:net/can/bcm.c SourceLine:1245 SourceSnippet:1235: /* add this bcm_op to the list of the rx_ops */
1236: list_add(&op->list, &bo->rx_ops);
1237:
1238: /* call can_rx_register() */
1239: do_rx_register = 1;
1240:
1241: } /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */
1242:
1243: /* check flags */
1244:
1245: if (op->flags & RX_RTR_FRAME) {
1246: struct canfd_frame *frame0 = op->frames;
1247:
1248: /* no timers in RTR-mode */
1249: hrtimer_cancel(&op->thrtimer);
1250: hrtimer_cancel(&op->timer);
1251:
1252: /*
1253: * funny feature in RX(!)_SETUP only for RTR-mode:
1254: * copy can_id into frame BUT without RTR-flag to
1255: * prevent a full-load-loopback-test ... ;-]
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_rx_setup SourceFile:net/can/bcm.c SourceLine:1257 SourceSnippet:1247:
1248: /* no timers in RTR-mode */
1249: hrtimer_cancel(&op->thrtimer);
1250: hrtimer_cancel(&op->timer);
1251:
1252: /*
1253: * funny feature in RX(!)_SETUP only for RTR-mode:
1254: * copy can_id into frame BUT without RTR-flag to
1255: * prevent a full-load-loopback-test ... ;-]
1256: */
1257: if ((op->flags & TX_CP_CAN_ID) ||
1258: (frame0->can_id == op->can_id))
1259: frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
1260:
1261: } else {
1262: if (op->flags & SETTIMER) {
1263:
1264: /* set timer value */
1265: op->ival1 = msg_head->ival1;
1266: op->ival2 = msg_head->ival2;
1267: op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_rx_setup SourceFile:net/can/bcm.c SourceLine:1262 SourceSnippet:1252: /*
1253: * funny feature in RX(!)_SETUP only for RTR-mode:
1254: * copy can_id into frame BUT without RTR-flag to
1255: * prevent a full-load-loopback-test ... ;-]
1256: */
1257: if ((op->flags & TX_CP_CAN_ID) ||
1258: (frame0->can_id == op->can_id))
1259: frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
1260:
1261: } else {
1262: if (op->flags & SETTIMER) {
1263:
1264: /* set timer value */
1265: op->ival1 = msg_head->ival1;
1266: op->ival2 = msg_head->ival2;
1267: op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
1268: op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
1269:
1270: /* disable an active timer due to zero value? */
1271: if (!op->kt_ival1)
1272: hrtimer_cancel(&op->timer);
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_rx_setup SourceFile:net/can/bcm.c SourceLine:1283 SourceSnippet:1273:
1274: /*
1275: * In any case cancel the throttle timer, flush
1276: * potentially blocked msgs and reset throttle handling
1277: */
1278: op->kt_lastmsg = 0;
1279: hrtimer_cancel(&op->thrtimer);
1280: bcm_rx_thr_flush(op);
1281: }
1282:
1283: if ((op->flags & STARTTIMER) && op->kt_ival1)
1284: hrtimer_start(&op->timer, op->kt_ival1,
1285: HRTIMER_MODE_REL_SOFT);
1286: }
1287:
1288: /* now we can register for can_ids, if we added a new bcm_op */
1289: if (do_rx_register) {
1290: if (ifindex) {
1291: struct net_device *dev;
1292:
1293: dev = dev_get_by_index(sock_net(sk), ifindex);
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:bcm_rx_starttimer SourceFile:net/can/bcm.c SourceLine:608 SourceSnippet: 603: /*
604: * bcm_rx_starttimer - enable timeout monitoring for CAN frame reception
605: */
606: static void bcm_rx_starttimer(struct bcm_op *op)
607: {
608: if (op->flags & RX_NO_AUTOTIMER)
609: return;
610:
611: if (op->kt_ival1)
612: hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL_SOFT);
613: }
]] TruncatedOutput:true]
|
| 27/2 |
2026/05/16 03:26 |
llm |
expert |
0m
Model:
Tokens:
input: 29967
output: 43
thoughts: 1092
Thoughts: **Data Race Analysis: CAN Frame Transmission**
Okay, let's break this down. My initial thought was triggered by a potential race condition with `op->flags` in `bcm_tx_timeout_handler`, specifically if that flag update could trigger `TX_COUNTEVT` concurrently with another operation. However, since it's just an integer, that's not the major issue.
The more significant potential data race is around the CAN frame payload itself. I see a `memcpy` from `msg` to `cf` in `bcm_tx_setup` and then a read from `cf` in `bcm_can_tx`. This could be dangerous. My immediate question is, could this lead to memory corruption or, worse, privilege escalation?
I carefully considered the interaction with `can_send`. It copies the payload into an `skb` and *then* checks the copied data's length (`cf->len`). That eliminates a TOCTOU (Time-of-Check-to-Time-of-Use) vulnerability because `can_send` operates on a snapshot of the data. If the copied data is invalid, the packet is dropped; if valid, it's sent. So, the worst case is a corrupted CAN frame being sent on the bus, a minor issue β a data race on the payload, but not a security vulnerability.
I then started to consider if `op->cfsiz` was the same as the allocation size for the `skb`. If not, we might have a buffer overflow in `skb_put_data`. But no, `op->cfsiz` is used for both the allocation and the copying, so the `skb` is large enough.
But... could `skb_put_data` *read* out of bounds from `cf`? `cf` is calculated as `op->frames + op->cfsiz * op->currframe`. And `op->frames` is allocated with `op->nframes * op->cfsiz` bytes, and `op->currframe` is always less than `op->nframes`, so this part seems safe, even with concurrent updates to `op->nframes` under a lock.
Ah, there's a potential area for concern. I see `bcm_tx_setup` *decreasing* `op->nframes`, e.g., from 10 to 5. Under a lock it sets `op->currframe = 0`. So, `bcm_can_tx` reads `op->currframe` under the same lock, so `cf` should always be valid, so that's good.
Wait, though! What if `bcm_can_tx` reads `op->currframe` (e.g. 9), *drops* the lock, and then `bcm_tx_setup` decreases `op->nframes` to 5? This could be bad. So `cf` still points to `op->frames + op->cfsiz * 9`. My immediate question is whether `op->frames` gets reallocated. No. It's *never* reallocated! It was originally allocated for 10 frames, so that location is valid!
However, it will still try to access the old memory. But this is still safe from memory corruption as far as I can see.
Let me double-check... can `op->frames` be freed concurrently? That's the only other vector to consider. I'll need to check the code around `bcm_remove_op`.
|
| 28/2 |
2026/05/16 03:26 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:bcm_remove_op]
Results: map[SourceCode: 809: static void bcm_remove_op(struct bcm_op *op)
810: {
811: hrtimer_cancel(&op->timer);
812: hrtimer_cancel(&op->thrtimer);
813:
814: call_rcu(&op->rcu, bcm_free_op_rcu);
815: }
SourceFile:net/can/bcm.c]
|
| 29/2 |
2026/05/16 03:26 |
llm |
expert |
0m
Model:
Tokens:
input: 32001
output: 46
thoughts: 12
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in this field, presented as a first-person summary:
**Analyzing `bcm_free_op_rcu`**
Alright, let's break this down. Seeing `bcm_free_op_rcu` immediately tells me we're dealing with the Broadcom driver model and a critical memory management function. The `free` implies we're releasing resources, likely some kind of operation structure within the BCM SDK's context. The `rcu` is the key β that screams Read-Copy-Update synchronization. So, right away, I'm thinking about the implications for concurrency. This function is designed to free the memory associated with an operation in a way that allows readers, who may be actively using this structure, to continue their work safely. We're talking about a lockless mechanism, or at least a very carefully coordinated one, to avoid those nasty race conditions that plague concurrent systems.
I'm picturing the typical RCU flow: mark the data as "deleted" or "being freed," publish that change, and then wait for the grace period β the time it takes for all current readers to finish their operations. Only then, *after* that grace period, can we safely reclaim the memory. I'll need to look at the surrounding code to determine how that grace period is managed here. Is it using the standard kernel RCU primitives? Or is BCM implementing its own custom flavor? Are there specific callback functions or event notifications involved?
I'm also wondering about the potential for starvation. If there are long-lived readers holding references to the structure, that grace period could be extended indefinitely, potentially causing memory leaks or preventing the driver from properly releasing resources. I'll need to examine the code to see if there are any mechanisms in place to handle those worst-case scenarios and ensure progress. This is the kind of function that requires close scrutiny when debugging any sort of performance or memory issue. It's a critical piece of the puzzle for a scalable and reliable BCM driver implementation.
|
| 30/2 |
2026/05/16 03:27 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:bcm_free_op_rcu]
Results: map[SourceCode: 796: static void bcm_free_op_rcu(struct rcu_head *rcu_head)
797: {
798: struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu);
799:
800: if ((op->frames) && (op->frames != &op->sframe))
801: kfree(op->frames);
802:
803: if ((op->last_frames) && (op->last_frames != &op->last_sframe))
804: kfree(op->last_frames);
805:
806: kfree(op);
807: }
SourceFile:net/can/bcm.c]
|
| 31/2 |
2026/05/16 03:27 |
llm |
expert |
0m
Model:
Tokens:
input: 32725
output: 42
thoughts: 219
Thoughts: **Analyzing the Removal of Operations in the BCM Driver**
Okay, so I'm looking at the `bcm_remove_op` function and how it relates to socket closure and the `RX_DELETE`/`TX_DELETE` messages. It seems pretty straightforward on the surface. When a socket closes or those delete messages come in, `bcm_remove_op` gets called. Now, if it's a `TX_DELETE`, `bcm_delete_tx_op` handles the initial cleanup, removing the operation from its list and then *calling* `bcm_remove_op`. This is where the magic (or potential issues) reside.
`bcm_remove_op` then takes the crucial step of cancelling the timers associated with the operation. But hang on... `hrtimer_cancel` doesn't just immediately stop the timer. It *waits* for the timer callback, in this case `bcm_tx_timeout_handler` (which in turn calls `bcm_can_tx`), to *finish* if it's currently running. This is important to note as I want to confirm there are no racing conditions that could lead to memory issues. So that's good, `bcm_tx_timeout_handler` will complete before `bcm_remove_op` returns.
After the timer callback has finished, `call_rcu` is used for the memory freeing. This suggests a safe reclamation strategy after all processing is complete. So, given this sequencing, I'm fairly confident that we aren't creating a use-after-free scenario here. It looks like it's handled properly.
However, I need to consider a potential concurrency issue: What happens if `bcm_sendmsg` is called *concurrently* with a `TX_DELETE`? `bcm_sendmsg` should be protecting itself using a spinlock or a mutex. I'm going to double check `bcm_sendmsg` to be certain. Specifically, it should either be taking `rtnl_lock()` or `lock_sock(sk)`. This will be the point of mutual exclusion I am looking for.
|
| 32/2 |
2026/05/16 03:27 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:bcm_sendmsg]
Results: map[SourceCode:1369: /*
1370: * bcm_sendmsg - process BCM commands (opcodes) from the userspace
1371: */
1372: static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
1373: {
1374: struct sock *sk = sock->sk;
1375: struct bcm_sock *bo = bcm_sk(sk);
1376: int ifindex = bo->ifindex; /* default ifindex for this bcm_op */
1377: struct bcm_msg_head msg_head;
1378: int cfsiz;
1379: int ret; /* read bytes or error codes as return value */
1380:
1381: if (!bo->bound)
1382: return -ENOTCONN;
1383:
1384: /* check for valid message length from userspace */
1385: if (size < MHSIZ)
1386: return -EINVAL;
1387:
1388: /* read message head information */
1389: ret = memcpy_from_msg((u8 *)&msg_head, msg, MHSIZ);
1390: if (ret < 0)
1391: return ret;
1392:
1393: cfsiz = CFSIZ(msg_head.flags);
1394: if ((size - MHSIZ) % cfsiz)
1395: return -EINVAL;
1396:
1397: /* check for alternative ifindex for this bcm_op */
1398:
1399: if (!ifindex && msg->msg_name) {
1400: /* no bound device as default => check msg_name */
1401: DECLARE_SOCKADDR(struct sockaddr_can *, addr, msg->msg_name);
1402:
1403: if (msg->msg_namelen < BCM_MIN_NAMELEN)
1404: return -EINVAL;
1405:
1406: if (addr->can_family != AF_CAN)
1407: return -EINVAL;
1408:
1409: /* ifindex from sendto() */
1410: ifindex = addr->can_ifindex;
1411:
1412: if (ifindex) {
1413: struct net_device *dev;
1414:
1415: dev = dev_get_by_index(sock_net(sk), ifindex);
1416: if (!dev)
1417: return -ENODEV;
1418:
1419: if (dev->type != ARPHRD_CAN) {
1420: dev_put(dev);
1421: return -ENODEV;
1422: }
1423:
1424: dev_put(dev);
1425: }
1426: }
1427:
1428: lock_sock(sk);
1429:
1430: switch (msg_head.opcode) {
1431:
1432: case TX_SETUP:
1433: ret = bcm_tx_setup(&msg_head, msg, ifindex, sk);
1434: break;
1435:
1436: case RX_SETUP:
1437: ret = bcm_rx_setup(&msg_head, msg, ifindex, sk);
1438: break;
1439:
1440: case TX_DELETE:
1441: if (bcm_delete_tx_op(&bo->tx_ops, &msg_head, ifindex))
1442: ret = MHSIZ;
1443: else
1444: ret = -EINVAL;
1445: break;
1446:
1447: case RX_DELETE:
1448: if (bcm_delete_rx_op(&bo->rx_ops, &msg_head, ifindex))
1449: ret = MHSIZ;
1450: else
1451: ret = -EINVAL;
1452: break;
1453:
1454: case TX_READ:
1455: /* reuse msg_head for the reply to TX_READ */
1456: msg_head.opcode = TX_STATUS;
1457: ret = bcm_read_op(&bo->tx_ops, &msg_head, ifindex);
1458: break;
1459:
1460: case RX_READ:
1461: /* reuse msg_head for the reply to RX_READ */
1462: msg_head.opcode = RX_STATUS;
1463: ret = bcm_read_op(&bo->rx_ops, &msg_head, ifindex);
1464: break;
1465:
1466: case TX_SEND:
1467: /* we need exactly one CAN frame behind the msg head */
1468: if ((msg_head.nframes != 1) || (size != cfsiz + MHSIZ))
1469: ret = -EINVAL;
1470: else
1471: ret = bcm_tx_send(msg, ifindex, sk, cfsiz);
1472: break;
1473:
1474: default:
1475: ret = -EINVAL;
1476: break;
1477: }
1478:
1479: release_sock(sk);
1480:
1481: return ret;
1482: }
SourceFile:net/can/bcm.c]
|
| 33/2 |
2026/05/16 03:27 |
llm |
expert |
0m
Model:
Tokens:
input: 35036
output: 45
thoughts: 4346
Thoughts: **Analysis of Potential Data Races and Vulnerabilities in bcm_tx_setup and bcm_tx_timeout_handler**
Okay, here's the breakdown of my thought process regarding the observed potential data race and possible vulnerabilities:
First, the core issue is the potential for concurrency between `bcm_tx_setup` (in process context) and `bcm_tx_timeout_handler` (in softirq context). `bcm_sendmsg` takes `lock_sock(sk)`, which establishes mutual exclusion between `TX_SETUP` and `TX_DELETE`, so we're primarily focused on the interaction with the timer handler. Because `memcpy_from_msg` within `bcm_tx_setup` does not disable softirqs, a timer interrupt can preempt it, leading to a possible data race when writing the CAN frame data.
The initial concern was that the partial write to the CAN frame's `cf` struct during a data race with `bcm_can_tx` reading that same memory could lead to a partially updated CAN frame being sent. My thought process followed these threads:
* **Corrupted CAN ID:** If `bcm_can_tx` reads `cf->can_id` while `memcpy_from_msg` is updating it, this can result in a frame with a corrupted CAN ID. The driver uses this value to set the hardware CAN ID. I confirmed that this only results in an invalid CAN ID. This doesn't involve memory corruption in the kernel, and the only result is an invalid CAN ID.
* **Invalid Frame Length (`cf->len`):** `bcm_tx_setup` checks if `cf->len` is within allowed bounds (8 for standard CAN, 64 for CAN FD). If `cf->len` is invalid, the function would have returned an `-EINVAL` error. However, `memcpy_from_msg` has already written the invalid `cf->len` to `cf`, and `bcm_can_tx` might be running at the same time. This is where my detailed thought process kicked in. Then the following considerations needed to be addressed.
* **Can send will detect issues:** `can_send` checks `cf->len` again with `can_is_canfd_skb`, and `can_is_can_skb`, the packets will be dropped, preventing sending an invalid frame.
* **The CAN XL possibility is the problem:** `can_is_canxl_skb` can lead to an issue. I realized that if the CAN FD frame contains specific values in `cf->len`, `__res0` and `__res1` that are valid for CAN XL, then `can_is_canxl_skb` can return true, and the CAN XL packet will be sent to the driver. The driver will read out-of-bounds, since the CAN XL packets will be sent with 60 bytes of data starting from offset 12 from a CAN FD struct. Since CAN FD is 72 bytes, reading 60 bytes will not cause a memory issue, even if we are sending data outside of the struct bounds!
* **Corrupted frames are still sent:** A data race isn't even strictly *required* here. A user could simply update an existing op with an invalid `cf->len`. This means an invalid `cf->len` could be put into `op->frames`. The data race is that if the timer is already running, it could lead to the existing frame being sent, since `bcm_tx_setup` doesn't stop the timer before overwriting the frame. However, this is not a security vulnerability.
The second area of concern was other parts of the `skb` being corrupted. This included:
* **SKB metadata integrity:** After carefully reviewing `bcm_can_tx`, I concluded that `skb_put_data` and `can_skb_set_owner` do not overwrite the `skb` metadata. That means there's no way to overwrite the `skb`'s control data, so this is safe.
Next I focused on the flags:
* **Flags issue:** The updates to `op->flags` in `bcm_tx_setup` are done without locking. `op->flags` is then read in `bcm_tx_timeout_handler`, potentially leading to race conditions that send a notification to userspace. This isn't a memory corruption issue, just a possible notification race.
Additional considerations included:
* **Count and Currframe:** `op->count` and `op->currframe` were being updated in potentially problematic ways. The analysis shows those values are correctly updated.
In summary, there is no real memory corruption
|
| 34/2 |
2026/05/16 03:27 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:bcm_tx_set_expiry]
Results: map[SourceCode: 432: static bool bcm_tx_set_expiry(struct bcm_op *op, struct hrtimer *hrt)
433: {
434: ktime_t ival;
435:
436: if (op->kt_ival1 && op->count)
437: ival = op->kt_ival1;
438: else if (op->kt_ival2)
439: ival = op->kt_ival2;
440: else
441: return false;
442:
443: hrtimer_set_expires(hrt, ktime_add(ktime_get(), ival));
444: return true;
445: }
SourceFile:net/can/bcm.c]
|
| 35/2 |
2026/05/16 03:27 |
llm |
expert |
0m
Model:
Tokens:
input: 40686
output: 45
thoughts: 484
Thoughts: **Analyzing Potential Concurrency Issues in the BCM TX Timeout Handler**
Okay, so I'm looking at this code and immediately I'm thinking about concurrent access. Specifically, `bcm_tx_timeout_handler` and how it interacts with the `op` structure. My initial concern was centered around `op->kt_ival1` and `op->kt_ival2`. These are `ktime_t` values, which are essentially `s64` (64-bit). On 32-bit systems, a concurrent write and read of a 64-bit value *can* lead to a "torn read," where the handler ends up seeing a corrupted, partial value of the time. If this happens, my timer is going to be significantly off, either way out in the future or expiring immediately. This isnβt a memory corruption issue, luckily. Just really bad timing for CAN frame transmission.
Then I shifted my focus to `op->count`. Hmm, this one's a bit more subtle. I saw that `op->count` is updated under the `op->bcm_tx_lock`, which is a good thing. But, the `bcm_tx_timeout_handler` *reads* `op->count` *without* the lock, which immediately set off alarm bells. The code snippet shows:
```c
if (op->kt_ival1 && (op->count > 0)) {
bcm_can_tx(op);
if (!op->count && (op->flags & TX_COUNTEVT)) {
```
So I needed to see what `bcm_can_tx` was doing with `op->count`. It turns out that `bcm_can_tx` *also* decrements `op->count` *under the lock*! So `bcm_tx_timeout_handler` reads `op->count` without lock, calls `bcm_can_tx` which decrements it under lock, then reads it again without lock. This means there's a race condition! I realized that `bcm_tx_setup` also updates `op->count` concurrently and this can result in the timeout handler seeing an inconsistent value. The good news? `op->count` is a 32-bit integer, so there's no risk of a torn read, even on 32-bit systems. But, the value of the count could be old or new. It will just affect whether the timer switches from `ival1` to `ival2`, as far as I can tell. Still, no memory corruption.
Now, let's take a look at the code for `bcm_tx_start_timer`...
|
| 36/2 |
2026/05/16 03:27 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:bcm_tx_start_timer]
Results: map[SourceCode: 447: static void bcm_tx_start_timer(struct bcm_op *op)
448: {
449: if (bcm_tx_set_expiry(op, &op->timer))
450: hrtimer_start_expires(&op->timer, HRTIMER_MODE_ABS_SOFT);
451: }
SourceFile:net/can/bcm.c]
|
| 37/2 |
2026/05/16 03:27 |
llm |
expert |
0m
Model:
Tokens:
input: 41922
output: 41
thoughts: 1820
Thoughts: **Analysis of the BCM CAN Data Race**
Okay, so I've been digging into this KCSAN report. The crux of the issue boils down to a potential data race within the CAN BCM (Broadcast Manager) implementation, specifically between `bcm_tx_setup` and `bcm_can_tx`. The KCSAN report pinpoints the race happening on the CAN frame payload itself.
Let me break down the thought process:
* **The Trigger:** We're dealing with the `STARTTIMER` flag. When it's set, the existing timer associated with a BCM operation is cancelled (using `hrtimer_cancel`) *after* the CAN frame data (potentially modified from user space!) is partially copied via `memcpy_from_msg`. But the timer *can* fire concurrently during the `memcpy_from_msg`, leading to the race on the `cf` buffer. This is because we don't hold any locks here.
* **The Data Race:** Specifically, KCSAN highlights a race between `memcpy_from_msg` (the write) and `skb_put_data` (the read) on the shared memory. `memcpy_from_msg` is copying the frame from user space into a buffer, and `skb_put_data` is then copying this data into an `skb` (socket buffer) to be transmitted. The specific address is `cf`, which is a frame within an operation structure.
* **Impact Assessment:** Crucially, this race doesn't seem to compromise kernel memory safety, privilege levels, or lead to a kernel crash. The data in question (the CAN frame payload) is copied into an `skb` and transmitted on the CAN bus.
* **Worst-Case:** A corrupted CAN frame is sent. This could be a functional bug, but not a security hole. It's just bad data on the bus.
* **Vulnerability Exploration:** I tried looking at other places where `op->frames` is used and how `op->cfsiz` is set. Nothing jumped out as exploitable.
* `op->cfsiz` is set only once at the beginning, at creation.
* `op->frames` is allocated big enough.
* The write is always within the size of `op->frames`
* The read is always within the size of `skb`.
* **Additional Checks:** I examined potentially unsafe functions.
* `skb_put_data` could lead to out of bounds write on the `skb` but the size of the buffer is set to the same size as the data copied in.
* `can_send` could cause an out of bounds read on `skb` but the checks ensure the buffer size is the expected value.
* `can_is_canxl_skb` might be susceptible but it only reads header data from the frame.
* **Security Implications:** Since this race primarily affects the CAN payload, and the payload is being written from user space, the damage is restricted. Corrupted CAN frames can be sent, so it could affect the bus functionality, but no privileged code or data is being modified by this race.
* **Privilege Level:** The question that comes to mind is: can unprivileged processes access CAN sockets and thereby initiate the race? Need to check `can_create`.
In summary, it's a data race, for sure, and one that is caught by KCSAN. But from a security perspective, it appears benign. It doesn't appear to lead to a crash, memory leak, or privilege escalation. It could, however, result in functional issues, sending bad CAN frames, potentially creating a denial-of-service, though it requires unprivileged access to CAN sockets.
|
| 38/2 |
2026/05/16 03:28 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/af_can.c IncludeLines:true Name:can_create]
Results: map[SourceCode: 114: static int can_create(struct net *net, struct socket *sock, int protocol,
115: int kern)
116: {
117: struct sock *sk;
118: const struct can_proto *cp;
119: int err = 0;
120:
121: sock->state = SS_UNCONNECTED;
122:
123: if (protocol < 0 || protocol >= CAN_NPROTO)
124: return -EINVAL;
125:
126: cp = can_get_proto(protocol);
127:
128: #ifdef CONFIG_MODULES
129: if (!cp) {
130: /* try to load protocol module if kernel is modular */
131:
132: err = request_module("can-proto-%d", protocol);
133:
134: /* In case of error we only print a message but don't
135: * return the error code immediately. Below we will
136: * return -EPROTONOSUPPORT
137: */
138: if (err)
139: pr_err_ratelimited("can: request_module (can-proto-%d) failed.\n",
140: protocol);
141:
142: cp = can_get_proto(protocol);
143: }
144: #endif
145:
146: /* check for available protocol and correct usage */
147:
148: if (!cp)
149: return -EPROTONOSUPPORT;
150:
151: if (cp->type != sock->type) {
152: err = -EPROTOTYPE;
153: goto errout;
154: }
155:
156: sock->ops = cp->ops;
157:
158: sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot, kern);
159: if (!sk) {
160: err = -ENOMEM;
161: goto errout;
162: }
163:
164: sock_init_data(sock, sk);
165: sk->sk_destruct = can_sock_destruct;
166:
167: if (sk->sk_prot->init)
168: err = sk->sk_prot->init(sk);
169:
170: if (err) {
171: /* release sk on errors */
172: sock_orphan(sk);
173: sock_put(sk);
174: sock->sk = NULL;
175: } else {
176: sock_prot_inuse_add(net, sk->sk_prot, 1);
177: }
178:
179: errout:
180: can_put_proto(cp);
181: return err;
182: }
SourceFile:net/can/af_can.c]
|
| 39/2 |
2026/05/16 03:28 |
llm |
expert |
0m
Model:
Tokens:
input: 45517
output: 41
thoughts: 96
Thoughts: **Vulnerability Investigation: CAN Socket Creation**
Okay, so I'm digging into this `can_create` function, and the first thing that jumps out at me is the lack of a `capable()` check before allowing socket creation. That's a classic vulnerability point. It means that even a non-privileged process, assuming the CAN protocol module is loaded (or can be autoloaded) could potentially create a CAN socket. That's immediately concerning.
Wait a second⦠I see `request_module("can-proto-%d", protocol)` being called. Ah, the `protocol` variable. I remember, `CAN_BCM` is protocol 2, right? So, this code will attempt to autoload the `can-bcm` module if it's not already loaded. That's the key.
Now I'm asking myself, does `bcm_init` have any capability checks of its own? That's the critical question now. If `bcm_init` *doesn't* validate the caller's privileges, then this autoloading and creation path becomes a huge security hole. I need to dive into the `can-bcm` module code and see if there are any checks in its initialization routine. That will determine if a local unprivileged user can potentially exploit this.
|
| 40/2 |
2026/05/16 03:28 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:bcm_init]
Results: map[SourceCode:1561: /*
1562: * initial settings for all BCM sockets to be set at socket creation time
1563: */
1564: static int bcm_init(struct sock *sk)
1565: {
1566: struct bcm_sock *bo = bcm_sk(sk);
1567:
1568: bo->bound = 0;
1569: bo->ifindex = 0;
1570: bo->dropped_usr_msgs = 0;
1571: bo->bcm_proc_read = NULL;
1572:
1573: INIT_LIST_HEAD(&bo->tx_ops);
1574: INIT_LIST_HEAD(&bo->rx_ops);
1575:
1576: /* set notifier */
1577: spin_lock(&bcm_notifier_lock);
1578: list_add_tail(&bo->notifier, &bcm_notifier_list);
1579: spin_unlock(&bcm_notifier_lock);
1580:
1581: return 0;
1582: }
SourceFile:net/can/bcm.c]
|
| 41/2 |
2026/05/16 03:28 |
llm |
expert |
0m
Model:
Tokens:
input: 46272
output: 47
thoughts: 2561
Thoughts: **Analysis of the CAN BCM Data Race and Potential Exploits**
Okay, so I've got this KCSAN report showing a data race between `bcm_can_tx` and `bcm_tx_setup`. Let's break this down. No capability check is present. Unprivileged processes can create BCM sockets, and thus trigger this data race. However, initially, it looked like a benign data race. The race is on the CAN frame payload and `cf->len`. `cf->len` is validated by `can_send`. The data race itself doesn't cause out-of-bounds access or memory corruption. The only way `bcm_tx_setup` can fail is if `msg_head->nframes` is invalid.
But... could there be something else? Could `can_send` be bypassed? Looking at the code, `can_send` is the *only* place the `skb` gets sent. `bcm_can_tx` copies the frame payload to the `skb`. `skb_put_data` uses `op->cfsiz`, which is set at op creation. This is good. Then the data race comes from the payload and `cf->len`. `can_send` validates the `cf->len` from the `skb`. So there is no TOCTOU, no out-of-bounds reads or writes. This seems, for now, to rule out any security issues in the frame itself. The check on `op->cfsiz` is important because the frame type can't be changed after creation. So no. It's benign.
**Recap and Detailed Analysis**
So, the KCSAN report shows a data race between `bcm_can_tx` (reading CAN frame payload) and `bcm_tx_setup` (updating CAN frame payload from userspace). The read copies data into an `skb`. The copy size (`op->cfsiz`) is fixed. The write copies from userspace. The race can lead to a "torn" read β mixed old and new data. Payload length `cf->len` might be corrupted, but `can_send` validates it from the `skb`, so itβs safe. There is no corruption. There is no denial of service.
The bug *is* accessible from unprivileged processes, and I see now that `can_create` doesn't check capabilities, and `can-bcm` is often autoloaded. However, it is *not exploitable*.
Okay, let's double-check again.
**Second Look and the Timer Storm**
Can a torn read of `ktime_t` lead to a DoS? It could if, for example, the torn read leads to zero ival. Timer goes off, CPU busy. But if both `ival1` and `ival2` are 0, it doesn't restart. So, if `ival1` is zero, we look at `ival2`. Fine. We don't have that problem.
What if `ival` reads as 1 nanosecond? Okay, timer restarts, but the smallest resolution. A user *could* cause a softirq storm if they set the timer interval very small, regardless of the race.
Let's double check `bcm_timeval_to_ktime` function.
**Conclusion**
The data race is real, but it's *benign* from a security perspective. It doesn't allow for code execution, memory corruption, or DoS (unless the user *deliberately* sets a tiny timer interval). The KCSAN report is a true positive, but it does not enable exploitation.
So:
Exploitable: false
DenialOfService: false
Unprivileged: true
UserNamespace: true
The others are false, of course.
Double-checking again, `can_send` does *not* check capabilities. I am comfortable with this analysis.
|
| 42/2 |
2026/05/16 03:28 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:bcm_timeval_to_ktime]
Results: map[SourceCode: 161: static inline ktime_t bcm_timeval_to_ktime(struct bcm_timeval tv)
162: {
163: return ktime_set(tv.tv_sec, tv.tv_usec * NSEC_PER_USEC);
164: }
SourceFile:net/can/bcm.c]
|
| 43/2 |
2026/05/16 03:28 |
llm |
expert |
0m
Model:
Tokens:
input: 49842
output: 45
thoughts: 48
Thoughts: Okay, here's my take on those thoughts, framed as an expert's inner monologue:
**Delving into Microsecond Precision**
Right, so we're dealing with `tv_usec` here. And because it's in microseconds, the absolute smallest, non-zero duration we can represent is, naturally, a single microsecond β that's 1000 nanoseconds. No surprises there. But then I have to look at `bcm_is_invalid_tv` and it immediately forces me to consider what we're actually checking here. Is this for the case when `tv_usec` is zero? Or is there something else going on? We need to remember that these sorts of checks are important when using this kind of timing for scheduling or precise timing operations.
|
| 44/2 |
2026/05/16 03:28 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:bcm_is_invalid_tv]
Results: map[SourceCode: 166: /* check limitations for timeval provided by user */
167: static bool bcm_is_invalid_tv(struct bcm_msg_head *msg_head)
168: {
169: if ((msg_head->ival1.tv_sec < 0) ||
170: (msg_head->ival1.tv_sec > BCM_TIMER_SEC_MAX) ||
171: (msg_head->ival1.tv_usec < 0) ||
172: (msg_head->ival1.tv_usec >= USEC_PER_SEC) ||
173: (msg_head->ival2.tv_sec < 0) ||
174: (msg_head->ival2.tv_sec > BCM_TIMER_SEC_MAX) ||
175: (msg_head->ival2.tv_usec < 0) ||
176: (msg_head->ival2.tv_usec >= USEC_PER_SEC))
177: return true;
178:
179: return false;
180: }
SourceFile:net/can/bcm.c]
|
| 45/2 |
2026/05/16 03:28 |
llm |
expert |
0m
Model:
Tokens:
input: 50440
output: 45
thoughts: 322
Thoughts: **My Analysis of Timer Frequency and Potential Issues**
Okay, so the user has the ability to set `tv_usec` to 1, effectively creating a timer that fires every microsecond. I immediately start thinking about resource limits. Is there a hard limit on the number of these operations a user can create? Yes, memory restrictions are the ultimate constraint, whether it's the socket buffer size or just general system memory.
I'm considering the implications of a 1-microsecond interval. It's going to generate a *lot* of timer firings, and I recall that the `hrtimer` subsystem has safeguards. It should detect timer storms and, at least, increase the minimum interval, along with printing a warning to the logs. So, this scenario, while potentially resource-intensive, doesn't inherently introduce a *new* Denial of Service vulnerability because of the data race. The user can simply set the value, and the protection is in place. The data race isn't the root of the problem.
Now, my mind shifts to a more nuanced concern: what happens if a torn read results in a negative `ktime_t`? I know `ktime_t` is a 64-bit integer (`s64`). The `bcm_timeval_to_ktime` function *should* return a positive value because both `tv_sec` and `tv_usec` are checked to be greater than or equal to zero. But, could a torn read potentially lead to a negative value? I'm now thinking about the potential for bit corruption...
My thought process is now on 32-bit systems. Even though `ktime_t` is 64-bit, if the read is torn, and we're on a 32-bit architecture, what if we read the upper 32 bits from a value where the sign bit is 1? This is interesting.
But wait, I need to consider that the `tv_sec` value is constrained by `BCM_TIMER_SEC_MAX`. I need to look up the exact value of `BCM_TIMER_SEC_MAX` to fully assess the risk here, to see if that value puts an upper bound on what is possible.
|
| 46/2 |
2026/05/16 03:29 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:net/can/bcm.c IncludeLines:true Name:BCM_TIMER_SEC_MAX]
Results: map[SourceCode: SourceFile:]
|
| 47/2 |
2026/05/16 03:29 |
llm |
expert |
0m
Model:
Tokens:
input: 51305
output: 24
thoughts: 15
Thoughts: Here's a first-person summary, tailored for an expert audience, of the thought process described:
**Investigating Timer Limits**
Okay, so I need to find where the upper limit for some timer is defined within this codebase. My immediate instinct is to jump in with a `grep`. I'm specifically thinking about `BCM_TIMER_SEC_MAX`, because that rings a bell as a commonly used constant related to timers. I'm going to start there and see what the search results turn up. Hopefully, that constant will be clearly defined and lead me to the relevant timer configuration or usage details. This will either quickly yield the definition or give me a solid point to pivot from if the name is used in a more complex context.
|
| 48/2 |
2026/05/16 03:29 |
tool |
grepper |
0m
Args: map[Expression:#define BCM_TIMER_SEC_MAX]
Results: map[Output:net/can/bcm.c-73-/* limit timers to 400 days for sending/timeouts */
net/can/bcm.c:74:#define BCM_TIMER_SEC_MAX (400 * 24 * 60 * 60)
net/can/bcm.c-75-
]
|
| 49/2 |
2026/05/16 03:29 |
llm |
expert |
0m
Model:
Tokens:
input: 51590
output: 66
thoughts: 271
Thoughts: **My Analysis of the Data Race**
Okay, so I've been looking at this KCSAN report and the situation with `BCM_TIMER_SEC_MAX`. Let me break down my thinking. `BCM_TIMER_SEC_MAX` is defined as `34560000`. Converting that to nanoseconds, we're talking about `34560000 * 1000000000`, which equals `34560000000000000` or `0x7AD22A000000000` in hexadecimal.
Crucially, this value fits within 64 bits and, importantly, it's a positive number. That's key. When we look at how `ktime_t` values are generated by `bcm_timeval_to_ktime`, they are *always* positive. This means that, no matter what, the sign bit in the upper 32 bits of any `ktime_t` value derived this way will *always* be zero.
Therefore, a "torn read" scenario β even if the data is read in a partially updated state β cannot result in a negative `ktime_t` value. The variable `ival` will *always* be positive.
Thus, this leads me to conclude that this data race is completely benign. It's a race condition on the CAN frame's payload and some timer intervals, yes, but neither of those areas can lead to any form of memory corruption, out-of-bounds access, or a Denial of Service. The KCSAN report is technically a "true positive" in that it detected a data race, but it's not a security vulnerability. It's safe.
I'm now ready to call `set-results` and provide the final response.
|
| 50/2 |
2026/05/16 03:29 |
tool |
set-results |
0m
Args: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 51/2 |
2026/05/16 03:29 |
llm |
expert |
0m
Model:
Tokens:
input: 52397
output: 977
thoughts: 115
Thoughts: **Final Assessment: Data Race Analysis**
Alright, the deep dive is done. Time to craft the definitive response. My focus will be sharp: I need to explicitly state that the core of the issue is the data race, and it's happening specifically on the CAN frame payload and the timer intervals.
The crucial part is going to be explaining *why* this isn't a showstopper. No exploitable vulnerabilities, thankfully. The explanation has to be air-tight. No out-of-bounds memory access, and `can_send` validating the copied payload length. That's key.
Then, I'll dismantle any concerns about Denial of Service. No crashing, no hanging, absolutely no memory leaks. The fact that the torn read of timer intervals can't even result in a negative value is actually quite reassuring.
Finally, I need to flag the accessibility from unprivileged processes. CAN BCM sockets can be created without requiring elevated capabilities, which is something the audience will need to keep in mind for mitigation.
|
| 52/1 |
2026/05/16 03:29 |
action |
format-explanation |
0m
Results: map[Explanation:Based on the analysis of the provided KCSAN bug report and the kernel source
code, here is the assessment of the security impact:
### Bug Mechanism
The KCSAN report identifies a data race between two execution contexts:
1. **CPU 0 (Interrupt/Softirq context):** The `bcm_tx_timeout_handler` timer
callback fires and calls `bcm_can_tx()`, which reads the CAN frame payload
(`cf`) and copies it into a newly allocated socket buffer (`skb`) using
`skb_put_data()`.
2. **CPU 1 (Process context):** A user-space process calls `sendmsg()` on a CAN
BCM socket, which reaches `bcm_tx_setup()`. This function updates the CAN frame
payload (`cf`) by copying data from user-space using `memcpy_from_msg()`.
Because `bcm_tx_setup()` does not hold the `op->bcm_tx_lock` while copying the
payload from user-space, the timer callback can concurrently read the payload
while it is being updated. This results in a data race on the CAN frame payload.
### Security Impact Assessment
**1. Exploitable: False**
This data race is strictly on the payload of the CAN frame being sent and does
not lead to memory corruption or privilege escalation.
- **No Out-of-Bounds Access:** The size of the copy (`op->cfsiz`) is fixed at
the creation of the BCM operation and cannot be changed concurrently. The `skb`
is allocated with exactly this size, and `skb_put_data()` copies exactly this
size. Thus, there is no out-of-bounds read from the `op->frames` array or
out-of-bounds write to the `skb`.
- **No TOCTOU on Payload Length:** The user-space process could attempt to write
an invalid payload length (`cf->len`) during the race. However, `bcm_can_tx()`
copies the entire frame into the `skb` first, and then passes the `skb` to
`can_send()`. `can_send()` validates the `cf->len` field from the *copied* data
in the `skb` (via `can_is_can_skb()` or `can_is_canfd_skb()`). If the copied
length is invalid, `can_send()` safely drops the packet. Since the validation
happens on the copied data, there is no Time-of-Check to Time-of-Use (TOCTOU)
vulnerability.
- The worst-case consequence is that a corrupted CAN frame (containing a mix of
old and new data) is transmitted on the CAN bus, which is a functional issue but
not a kernel security vulnerability.
**2. Denial Of Service: False**
The bug does not cause kernel crashes, hangs, deadlocks, or resource leaks.
- There are other minor data races on timer intervals (`op->kt_ival1`,
`op->kt_ival2`) which are updated without a lock. A torn read of these 64-bit
`ktime_t` values on 32-bit systems could result in a corrupted timer expiry.
However, because the maximum allowed timer value (`BCM_TIMER_SEC_MAX`) ensures
the sign bit is always 0, a torn read cannot result in a negative time value.
The timer will simply fire at an unexpected positive interval, which does not
cause a softirq storm or system hang.
**3. Accessible From Unprivileged Processes: True**
Unprivileged users can create CAN sockets (`PF_CAN`) and use the BCM protocol
(`CAN_BCM`). The kernel automatically loads the `can-bcm` module if it is not
already loaded, and there are no `capable()` checks restricting the creation of
these sockets or the use of `sendmsg()`.
**4. Accessible From User Namespaces: True**
Since the bug is reachable by unprivileged users in the initial namespace, it is
also reachable from within user namespaces.
**5. Other Triggers (Network, Remote, Peripheral, Filesystem, VM): False**
The bug is triggered locally via the `sendmsg()` syscall on a CAN socket. It is
not triggered by processing ingress network traffic, remote connections,
plugging in malicious peripherals, mounting filesystems, or specific VM
interactions.
### Conclusion
The KCSAN report highlights a true positive data race, but it is benign from a
kernel security perspective. It does not compromise kernel memory safety, cannot
be exploited for privilege escalation, and does not cause a Denial of Service.]
|