syzbot


ID Workflow Result Correct Bug Created Started Finished Revision Error
e49baaa6-b0f3-4727-a66a-7733f47ee47a assessment-kcsan Benign: ❌  Confident: ✅  KCSAN: data-race in smc_switch_to_fallback / sock_poll (2) 2026/02/04 14:09 2026/02/04 14:09 2026/02/04 14:21 9b618abc0cd923b36c7a5ebc58a21174112d8f70
Decision History:
Time User Decision
2026/02/06 06:15 dvyukov@google.com

Benign: ❌    Confident: ✅   

Explanation:
The data race occurs on the `file->private_data` field of a socket file descriptor. This field is being updated in `smc_switch_to_fallback()` (to point to the underlying TCP/CLC socket instead of the SMC socket) while concurrently being read in `sock_poll()`.

### Analysis of the Race

1.  **Nature of the Access**: `smc_switch_to_fallback()` is performing a plain write to `file->private_data`, and `sock_poll()` is performing a plain read. There is no mutual exclusion (like a lock) or memory barrier protecting this transition.
2.  **Type Confusion Risk**: In `sock_poll()`, the code first reads `file->private_data` into a local variable `sock`, then reads `sock->ops`, and finally calls `ops->poll(file, sock, wait)`. If the compiler reloads `sock` from `file->private_data` between these steps (which is permitted under the C memory model for non-volatile accesses), it could fetch the `ops` from the SMC socket but then call that `ops->poll` function (i.e., `smc_poll`) passing the TCP socket as the `sock` argument.
3.  **Consequences of Type Confusion**: `smc_poll()` casts the `struct socket *sock` to a `struct smc_sock *`. A TCP socket (`struct tcp_sock`) is not compatible with `struct smc_sock`. Accessing SMC-specific fields (like `smc->use_fallback` or `smc->conn`) on a TCP socket object would result in reading random memory, leading to undefined behavior, logic errors, or a kernel crash.
4.  **Inconsistent State**: Even if the compiler does not reload the pointer, the race between setting `smc->use_fallback = true` and updating `file->private_data` means that `sock_poll()` might see an inconsistent state where it enters `smc_poll()` but the fallback is already partially complete, potentially accessing uninitialized or transitioning connection state.
5.  **Violation of Invariants**: In the Linux kernel, `file->private_data` for a socket is generally expected to be constant for the lifetime of the `file` object. SMC's "fallback" mechanism violates this invariant. While the mechanism is intended to be a performance optimization, doing so without proper synchronization (like `READ_ONCE`/`WRITE_ONCE` or a lock) makes it unsafe.

### Conclusion
This data race is **harmful** because it can lead to type confusion and memory corruption. It is not a simple statistics counter or a benign flag race; it involves the fundamental identity of the socket object being operated on.

The fix for this would typically involve using `READ_ONCE` and `WRITE_ONCE` to prevent compiler reloads and ensure atomicity, or better yet, avoiding the mid-flight change of `file->private_data` altogether.

Crash report:
==================================================================
BUG: KCSAN: data-race in smc_switch_to_fallback / sock_poll

write to 0xffff888127398c18 of 8 bytes by task 14369 on cpu 1:
 smc_switch_to_fallback+0x4ea/0x7e0 net/smc/af_smc.c:933
 smc_sendmsg+0xce/0x2f0 net/smc/af_smc.c:2797
 sock_sendmsg_nosec net/socket.c:727 [inline]
 __sock_sendmsg net/socket.c:742 [inline]
 ____sys_sendmsg+0x5af/0x600 net/socket.c:2592
 ___sys_sendmsg+0x195/0x1e0 net/socket.c:2646
 __sys_sendmsg net/socket.c:2678 [inline]
 __do_sys_sendmsg net/socket.c:2683 [inline]
 __se_sys_sendmsg net/socket.c:2681 [inline]
 __x64_sys_sendmsg+0xd4/0x160 net/socket.c:2681
 x64_sys_call+0x17ba/0x3000 arch/x86/include/generated/asm/syscalls_64.h:47
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0xc0/0x2a0 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

read to 0xffff888127398c18 of 8 bytes by task 14367 on cpu 0:
 sock_poll+0x27/0x240 net/socket.c:1427
 vfs_poll include/linux/poll.h:82 [inline]
 __io_arm_poll_handler+0x1ee/0xb80 io_uring/poll.c:581
 io_poll_add+0x69/0xf0 io_uring/poll.c:899
 __io_issue_sqe+0xfd/0x2d0 io_uring/io_uring.c:1793
 io_issue_sqe+0x20b/0xc20 io_uring/io_uring.c:1816
 io_queue_sqe io_uring/io_uring.c:2043 [inline]
 io_submit_sqe io_uring/io_uring.c:2321 [inline]
 io_submit_sqes+0x78a/0x11b0 io_uring/io_uring.c:2435
 __do_sys_io_uring_enter io_uring/io_uring.c:3285 [inline]
 __se_sys_io_uring_enter+0x1bf/0x1c70 io_uring/io_uring.c:3224
 __x64_sys_io_uring_enter+0x78/0x90 io_uring/io_uring.c:3224
 x64_sys_call+0x27e4/0x3000 arch/x86/include/generated/asm/syscalls_64.h:427
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0xc0/0x2a0 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

value changed: 0xffff88811a6056c0 -> 0xffff88811a606080

Reported by Kernel Concurrency Sanitizer on:
CPU: 0 UID: 0 PID: 14367 Comm: syz.8.3658 Not tainted syzkaller #0 PREEMPT(voluntary) 
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/25/2025
==================================================================

Trajectory:
Seq Timestamp Type Name Duration
0/0 2026/02/04 14:09 flow assessment-kcsan
12m Results:
map[Benign:false Confident:true Explanation:The data race occurs on the `file->private_data` field of a socket file descriptor. This field is being updated in `smc_switch_to_fallback()` (to point to the underlying TCP/CLC socket instead of the SMC socket) while concurrently being read in `sock_poll()`.

### Analysis of the Race

1.  **Nature of the Access**: `smc_switch_to_fallback()` is performing a plain write to `file->private_data`, and `sock_poll()` is performing a plain read. There is no mutual exclusion (like a lock) or memory barrier protecting this transition.
2.  **Type Confusion Risk**: In `sock_poll()`, the code first reads `file->private_data` into a local variable `sock`, then reads `sock->ops`, and finally calls `ops->poll(file, sock, wait)`. If the compiler reloads `sock` from `file->private_data` between these steps (which is permitted under the C memory model for non-volatile accesses), it could fetch the `ops` from the SMC socket but then call that `ops->poll` function (i.e., `smc_poll`) passing the TCP socket as the `sock` argument.
3.  **Consequences of Type Confusion**: `smc_poll()` casts the `struct socket *sock` to a `struct smc_sock *`. A TCP socket (`struct tcp_sock`) is not compatible with `struct smc_sock`. Accessing SMC-specific fields (like `smc->use_fallback` or `smc->conn`) on a TCP socket object would result in reading random memory, leading to undefined behavior, logic errors, or a kernel crash.
4.  **Inconsistent State**: Even if the compiler does not reload the pointer, the race between setting `smc->use_fallback = true` and updating `file->private_data` means that `sock_poll()` might see an inconsistent state where it enters `smc_poll()` but the fallback is already partially complete, potentially accessing uninitialized or transitioning connection state.
5.  **Violation of Invariants**: In the Linux kernel, `file->private_data` for a socket is generally expected to be constant for the lifetime of the `file` object. SMC's "fallback" mechanism violates this invariant. While the mechanism is intended to be a performance optimization, doing so without proper synchronization (like `READ_ONCE`/`WRITE_ONCE` or a lock) makes it unsafe.

### Conclusion
This data race is **harmful** because it can lead to type confusion and memory corruption. It is not a simple statistics counter or a benign flag race; it involves the fundamental identity of the socket object being operated on.

The fix for this would typically involve using `READ_ONCE` and `WRITE_ONCE` to prevent compiler reloads and ensure atomicity, or better yet, avoiding the mid-flight change of `file->private_data` altogether.]

1/1 2026/02/04 14:09 action kernel-checkouter
0m Results:
map[KernelSrc:/usr/local/google/home/dvyukov/syzkaller/agent/workdir/cache/src/abb886c0c39beb4d1e7f5feabbfdd329d87de199]

2/1 2026/02/04 14:10 action kernel-builder
4m Results:
map[KernelObj:/usr/local/google/home/dvyukov/syzkaller/agent/workdir/cache/build/aa9548292bbb3c057fae0996445ca122c1280056]

3/1 2026/02/04 14:14 action codesearch-prepare
2m Results:
map[Index:codesearch-index]

4/1 2026/02/04 14:17 agent expert
4m Model:
gemini-3-flash-preview

Results:
map[Benign:false Confident:true]

Instruction:
You are an experienced Linux kernel developer tasked with determining if the given kernel
data race is benign or not. The data race report is from KCSAN tool.
It contains 2 stack traces of the memory accesses that constitute a data race.

A "benign" data races are on a simple int/bool variable or similar field,
and the accesses are not supposed to be protected by any mutual exclusion primitives.
Common examples of such "benign" data races are accesses to various flags fields,
statistics counters, and similar. A "benign" data race does not lead to memory corruption/crash
with a conservative compiler that compiles memory accesses to primitive types
effectively as atomic.

A non-benign (or "harmful" data race) can lead to corruption/crash even with
a conservative compiler that compiles memory accesses to primitive types
effectively as atomic. A common example of a "harmful" data races is race on
a complex container (list/hashmap/etc), where accesses are supposed to be protected
by a mutual exclusion primitive.

Also consider races that happen at the same time with the given one.
If there is no synchronization in between, other memory accesses in the involved threads
race with each other if they access the same memory. For example, if both threads execute:

	some_struct->have_elements = true;
	list_add(new_node, &some_struct->list_head);

the race on some_struct->have_elements may appear benign, however it also implies there
is a race on some_struct->list_head which is not benign, since the list is not thread-safe.

Take into account that on 32-bit systems 64-bit memory accesses may be split into two accesses,
and thus even with a conservative compiler may not be fully atomic. However, such races may
still be benign depending on how writes are done, and how read data is used.

In the final reply explain why you think the given data race is benign or is harmful.

Use the provided tools to confirm any assumptions, variables/fields being accessed, etc.
In particular, don't make assumptions about the kernel source code,
use codesearch tools to read the actual source code.

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 data race report is:

==================================================================
BUG: KCSAN: data-race in smc_switch_to_fallback / sock_poll

write to 0xffff888127398c18 of 8 bytes by task 14369 on cpu 1:
 smc_switch_to_fallback+0x4ea/0x7e0 net/smc/af_smc.c:933
 smc_sendmsg+0xce/0x2f0 net/smc/af_smc.c:2797
 sock_sendmsg_nosec net/socket.c:727 [inline]
 __sock_sendmsg net/socket.c:742 [inline]
 ____sys_sendmsg+0x5af/0x600 net/socket.c:2592
 ___sys_sendmsg+0x195/0x1e0 net/socket.c:2646
 __sys_sendmsg net/socket.c:2678 [inline]
 __do_sys_sendmsg net/socket.c:2683 [inline]
 __se_sys_sendmsg net/socket.c:2681 [inline]
 __x64_sys_sendmsg+0xd4/0x160 net/socket.c:2681
 x64_sys_call+0x17ba/0x3000 arch/x86/include/generated/asm/syscalls_64.h:47
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0xc0/0x2a0 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

read to 0xffff888127398c18 of 8 bytes by task 14367 on cpu 0:
 sock_poll+0x27/0x240 net/socket.c:1427
 vfs_poll include/linux/poll.h:82 [inline]
 __io_arm_poll_handler+0x1ee/0xb80 io_uring/poll.c:581
 io_poll_add+0x69/0xf0 io_uring/poll.c:899
 __io_issue_sqe+0xfd/0x2d0 io_uring/io_uring.c:1793
 io_issue_sqe+0x20b/0xc20 io_uring/io_uring.c:1816
 io_queue_sqe io_uring/io_uring.c:2043 [inline]
 io_submit_sqe io_uring/io_uring.c:2321 [inline]
 io_submit_sqes+0x78a/0x11b0 io_uring/io_uring.c:2435
 __do_sys_io_uring_enter io_uring/io_uring.c:3285 [inline]
 __se_sys_io_uring_enter+0x1bf/0x1c70 io_uring/io_uring.c:3224
 __x64_sys_io_uring_enter+0x78/0x90 io_uring/io_uring.c:3224
 x64_sys_call+0x27e4/0x3000 arch/x86/include/generated/asm/syscalls_64.h:427
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0xc0/0x2a0 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

value changed: 0xffff88811a6056c0 -> 0xffff88811a606080

Reported by Kernel Concurrency Sanitizer on:
CPU: 0 UID: 0 PID: 14367 Comm: syz.8.3658 Not tainted syzkaller #0 PREEMPT(voluntary) 
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/25/2025
==================================================================


Reply:
The data race occurs on the `file->private_data` field of a socket file descriptor. This field is being updated in `smc_switch_to_fallback()` (to point to the underlying TCP/CLC socket instead of the SMC socket) while concurrently being read in `sock_poll()`.

### Analysis of the Race

1.  **Nature of the Access**: `smc_switch_to_fallback()` is performing a plain write to `file->private_data`, and `sock_poll()` is performing a plain read. There is no mutual exclusion (like a lock) or memory barrier protecting this transition.
2.  **Type Confusion Risk**: In `sock_poll()`, the code first reads `file->private_data` into a local variable `sock`, then reads `sock->ops`, and finally calls `ops->poll(file, sock, wait)`. If the compiler reloads `sock` from `file->private_data` between these steps (which is permitted under the C memory model for non-volatile accesses), it could fetch the `ops` from the SMC socket but then call that `ops->poll` function (i.e., `smc_poll`) passing the TCP socket as the `sock` argument.
3.  **Consequences of Type Confusion**: `smc_poll()` casts the `struct socket *sock` to a `struct smc_sock *`. A TCP socket (`struct tcp_sock`) is not compatible with `struct smc_sock`. Accessing SMC-specific fields (like `smc->use_fallback` or `smc->conn`) on a TCP socket object would result in reading random memory, leading to undefined behavior, logic errors, or a kernel crash.
4.  **Inconsistent State**: Even if the compiler does not reload the pointer, the race between setting `smc->use_fallback = true` and updating `file->private_data` means that `sock_poll()` might see an inconsistent state where it enters `smc_poll()` but the fallback is already partially complete, potentially accessing uninitialized or transitioning connection state.
5.  **Violation of Invariants**: In the Linux kernel, `file->private_data` for a socket is generally expected to be constant for the lifetime of the `file` object. SMC's "fallback" mechanism violates this invariant. While the mechanism is intended to be a performance optimization, doing so without proper synchronization (like `READ_ONCE`/`WRITE_ONCE` or a lock) makes it unsafe.

### Conclusion
This data race is **harmful** because it can lead to type confusion and memory corruption. It is not a simple statistics counter or a benign flag race; it involves the fundamental identity of the socket object being operated on.

The fix for this would typically involve using `READ_ONCE` and `WRITE_ONCE` to prevent compiler reloads and ensure atomicity, or better yet, avoiding the mid-flight change of `file->private_data` altogether.

5/2 2026/02/04 14:17 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 4140
							output: 85
							thoughts: 223
						

Thoughts:
**Analyzing a Data Race in the SMC Subsystem**

Okay, so the report flags a nasty data race – a classic concurrency issue – between `smc_switch_to_fallback` (the writer) and `sock_poll` (the reader). My expert spidey-sense tingles; these often indicate subtle but critical flaws in synchronization. The report points to an access at address `0xffff888127398c18`, and the value shifted from `0xffff88811a6056c0` to `0xffff88811a606080`. Those addresses scream kernel pointer, meaning we're likely dealing with something kernel-internal, and potentially sensitive.

Now to dig into the details. Time to examine the stack traces. The write path is coming from within the SMC subsystem, specifically: `smc_switch_to_fallback+0x4ea/0x7e0` in `net/smc/af_smc.c:933` which is getting called from `smc_sendmsg+0xce/0x2f0` in `net/smc/af_smc.c:2797`. So, `smc_sendmsg` seems to be the trigger, and `smc_switch_to_fallback` is where the problematic write is happening. The read side, on the other hand, originates in `sock_poll+0x27/0x240` in `net/socket.c:1427`.

Alright, I'll start poring over the source code at those specific locations. The first step will be to thoroughly understand the data being accessed at that address, and what the write and read operations are actually doing. Let's see if we can expose the underlying synchronization issues.



6/2 2026/02/04 14:17 tool codesearch-definition-source
0m Args:
map[ContextFile:net/smc/af_smc.c IncludeLines:true Name:smc_switch_to_fallback]

Results:
map[SourceCode: 917:	static int smc_switch_to_fallback(struct smc_sock *smc, int reason_code)
 918:	{
 919:		int rc = 0;
 920:	
 921:		mutex_lock(&smc->clcsock_release_lock);
 922:		if (!smc->clcsock) {
 923:			rc = -EBADF;
 924:			goto out;
 925:		}
 926:	
 927:		smc->use_fallback = true;
 928:		smc->fallback_rsn = reason_code;
 929:		smc_stat_fallback(smc);
 930:		trace_smc_switch_to_fallback(smc, reason_code);
 931:		if (smc->sk.sk_socket && smc->sk.sk_socket->file) {
 932:			smc->clcsock->file = smc->sk.sk_socket->file;
 933:			smc->clcsock->file->private_data = smc->clcsock;
 934:			smc->clcsock->wq.fasync_list =
 935:				smc->sk.sk_socket->wq.fasync_list;
 936:			smc->sk.sk_socket->wq.fasync_list = NULL;
 937:	
 938:			/* There might be some wait entries remaining
 939:			 * in smc sk->sk_wq and they should be woken up
 940:			 * as clcsock's wait queue is woken up.
 941:			 */
 942:			smc_fback_replace_callbacks(smc);
 943:		}
 944:	out:
 945:		mutex_unlock(&smc->clcsock_release_lock);
 946:		return rc;
 947:	}
 SourceFile:net/smc/af_smc.c]

7/2 2026/02/04 14:17 tool codesearch-definition-source
0m Args:
map[ContextFile:net/socket.c IncludeLines:true Name:sock_poll]

Results:
map[SourceCode:1424:	/* No kernel lock held - perfect */
1425:	static __poll_t sock_poll(struct file *file, poll_table *wait)
1426:	{
1427:		struct socket *sock = file->private_data;
1428:		const struct proto_ops *ops = READ_ONCE(sock->ops);
1429:		__poll_t events = poll_requested_events(wait), flag = 0;
1430:	
1431:		if (!ops->poll)
1432:			return 0;
1433:	
1434:		if (sk_can_busy_loop(sock->sk)) {
1435:			/* poll once if requested by the syscall */
1436:			if (events & POLL_BUSY_LOOP)
1437:				sk_busy_loop(sock->sk, 1);
1438:	
1439:			/* if this socket can poll_ll, tell the system call */
1440:			flag = POLL_BUSY_LOOP;
1441:		}
1442:	
1443:		return ops->poll(file, sock, wait) | flag;
1444:	}
 SourceFile:net/socket.c]

8/2 2026/02/04 14:17 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 5742
							output: 44
							thoughts: 574
						

Thoughts:
**Analyzing the Socket File Pointer Update in SMC Fallback**

Okay, here's what's running through my head. The core issue seems to revolve around a race condition concerning `file->private_data` when SMC is switching to a fallback CLC (TCP) socket. The report highlights that `file->private_data` is changing. Specifically, it's going from what seems to be the SMC socket (`0xffff88811a6056c0`) to the CLC socket (`0xffff88811a606080`).

I'm focusing on these key code snippets:

*   In `smc_switch_to_fallback`, the `file->private_data` is updated to point to the `clcsock`.
*   And in `sock_poll`, `file->private_data` is used to get the socket object. This function seems to be reading the value that `smc_switch_to_fallback` is changing.

So, the SMC socket's `sk_socket` and the underlying CLC socket (`clcsock`) are involved. When falling back to TCP, the idea is to have the `file` associated with the SMC socket point to the CLC socket.

Line 933 in `smc_switch_to_fallback` is the crucial update, making `file->private_data` point to the `clcsock`. But line 932 makes the `file` for the SMC socket the same `file` as the `clcsock` one. Meaning line 933 can be interpreted as setting `file->private_data = smc->clcsock`.

The concern is the timing. `sock_poll` reads `file->private_data`. If the read happens concurrently with the update in `smc_switch_to_fallback`, there is a data race. `sock_poll` could read either the old value (SMC socket) or the new value (CLC socket) depending on where it's interrupted.

If `sock_poll` gets the SMC socket, it calls `smc_poll`. If it gets the CLC socket, it'll call `tcp_poll` (or whatever the CLC socket's operations are). This could lead to inconsistent behavior depending on what the poll implementation expects. I need to understand what `smc_poll` does in this situation.



9/2 2026/02/04 14:17 tool codesearch-definition-source
0m Args:
map[ContextFile:net/smc/af_smc.c IncludeLines:true Name:smc_poll]

Results:
map[SourceCode:2872:	__poll_t smc_poll(struct file *file, struct socket *sock,
2873:			  poll_table *wait)
2874:	{
2875:		struct sock *sk = sock->sk;
2876:		struct smc_sock *smc;
2877:		__poll_t mask = 0;
2878:	
2879:		if (!sk)
2880:			return EPOLLNVAL;
2881:	
2882:		smc = smc_sk(sock->sk);
2883:		if (smc->use_fallback) {
2884:			/* delegate to CLC child sock */
2885:			mask = smc->clcsock->ops->poll(file, smc->clcsock, wait);
2886:			sk->sk_err = smc->clcsock->sk->sk_err;
2887:		} else {
2888:			if (sk->sk_state != SMC_CLOSED)
2889:				sock_poll_wait(file, sock, wait);
2890:			if (sk->sk_err)
2891:				mask |= EPOLLERR;
2892:			if ((sk->sk_shutdown == SHUTDOWN_MASK) ||
2893:			    (sk->sk_state == SMC_CLOSED))
2894:				mask |= EPOLLHUP;
2895:			if (sk->sk_state == SMC_LISTEN) {
2896:				/* woken up by sk_data_ready in smc_listen_work() */
2897:				mask |= smc_accept_poll(sk);
2898:			} else if (smc->use_fallback) { /* as result of connect_work()*/
2899:				mask |= smc->clcsock->ops->poll(file, smc->clcsock,
2900:								   wait);
2901:				sk->sk_err = smc->clcsock->sk->sk_err;
2902:			} else {
2903:				if ((sk->sk_state != SMC_INIT &&
2904:				     atomic_read(&smc->conn.sndbuf_space)) ||
2905:				    sk->sk_shutdown & SEND_SHUTDOWN) {
2906:					mask |= EPOLLOUT | EPOLLWRNORM;
2907:				} else {
2908:					sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
2909:					set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
2910:	
2911:					if (sk->sk_state != SMC_INIT) {
2912:						/* Race breaker the same way as tcp_poll(). */
2913:						smp_mb__after_atomic();
2914:						if (atomic_read(&smc->conn.sndbuf_space))
2915:							mask |= EPOLLOUT | EPOLLWRNORM;
2916:					}
2917:				}
2918:				if (atomic_read(&smc->conn.bytes_to_rcv))
2919:					mask |= EPOLLIN | EPOLLRDNORM;
2920:				if (sk->sk_shutdown & RCV_SHUTDOWN)
2921:					mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP;
2922:				if (sk->sk_state == SMC_APPCLOSEWAIT1)
2923:					mask |= EPOLLIN;
2924:				if (smc->conn.urg_state == SMC_URG_VALID)
2925:					mask |= EPOLLPRI;
2926:			}
2927:		}
2928:	
2929:		return mask;
2930:	}
 SourceFile:net/smc/af_smc.c]

10/2 2026/02/04 14:17 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 7914
							output: 49
							thoughts: 763
						

Thoughts:
**Analyzing the SMC/CLC Fallback Mechanism in the Socket Polling Context**

Okay, let me break this down. My initial thought process is centered around how the system handles the transition from an SMC (Shared Memory Communication) socket to a CLC (Client-Level Connection, likely TCP) fallback socket, particularly within the socket polling mechanism. It seems there's a race condition I need to understand, focusing on `file->private_data`.

First, I see that `smc_poll` checks `smc->use_fallback`. If this flag is set to true, as `smc_switch_to_fallback` sets it, the polling shifts from the SMC socket to the CLC socket. Specifically, I note this line: `smc->clcsock->file->private_data = smc->clcsock;`.

The key here is `sock_poll`.  `sock_poll` fetches a `file->private_data` which may be an old SMC socket or the new CLC socket, and then gets the socket operations via `sock->ops`. If `sock_poll` reads the SMC socket from `file->private_data`, it calls `smc_poll`. If the fallback is triggered, this `smc_poll` will correctly call `clcsock->ops->poll` due to the use of `smc->use_fallback`. If `sock_poll` reads the new CLC socket, it calls `clcsock->ops->poll` directly, which uses `tcp_poll` in the case of a TCP CLC socket.

The crucial race is here: `sock_poll` reads `file->private_data` and *then* `smc_switch_to_fallback` occurs, updating `file->private_data` to the CLC socket.  In this scenario, `sock_poll` calls `smc_poll` using the old SMC socket, but `smc_poll` then uses `smc->clcsock->ops->poll` because of `smc->use_fallback`.  In short, in both situations, the correct `poll` function appears to get called eventually.

However, a potential problem emerges with `sock->ops`.  `sock_poll` relies on `sock->ops`, so, even if `file->private_data` correctly points to the CLC socket, is the right operation being called?  I note `smc_sock_ops` for the SMC and `inet_stream_ops` (TCP) for the CLC. So, `smc_switch_to_fallback` must also ensure the correct socket operations are used after the fallback.

Ah, I see. `smc_switch_to_fallback` also transfers the `fasync_list` and calls `smc_fback_replace_callbacks`. This is important, as it suggests the system is replacing callback pointers. I'll need to dig into `smc_fback_replace_callbacks` to fully understand if these callback changes align correctly with both the `file->private_data` change AND the `sock->ops` the `sock_poll` function utilizes.



11/2 2026/02/04 14:17 tool codesearch-definition-source
0m Args:
map[ContextFile:net/smc/af_smc.c IncludeLines:true Name:smc_fback_replace_callbacks]

Results:
map[SourceCode: 898:	static void smc_fback_replace_callbacks(struct smc_sock *smc)
 899:	{
 900:		struct sock *clcsk = smc->clcsock->sk;
 901:	
 902:		write_lock_bh(&clcsk->sk_callback_lock);
 903:		clcsk->sk_user_data = (void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY);
 904:	
 905:		smc_clcsock_replace_cb(&clcsk->sk_state_change, smc_fback_state_change,
 906:				       &smc->clcsk_state_change);
 907:		smc_clcsock_replace_cb(&clcsk->sk_data_ready, smc_fback_data_ready,
 908:				       &smc->clcsk_data_ready);
 909:		smc_clcsock_replace_cb(&clcsk->sk_write_space, smc_fback_write_space,
 910:				       &smc->clcsk_write_space);
 911:		smc_clcsock_replace_cb(&clcsk->sk_error_report, smc_fback_error_report,
 912:				       &smc->clcsk_error_report);
 913:	
 914:		write_unlock_bh(&clcsk->sk_callback_lock);
 915:	}
 SourceFile:net/smc/af_smc.c]

12/2 2026/02/04 14:17 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 9837
							output: 48
							thoughts: 1923
						

Thoughts:
**Analyzing the Data Race in `file->private_data` During SMC Fallback**

Okay, so I'm digging into this KCSAN report. The core issue seems to be a potential data race on `file->private_data`. Specifically, this is happening during the SMC fallback mechanism, where an SMC socket transitions to a TCP (CLC) socket. The race is between `sock_poll`, which reads this field, and `smc_switch_to_fallback`, which writes to it.

`sock_poll` is pretty straightforward: it fetches the socket from `file->private_data` and then uses the socket's operations (`ops`). If `smc_switch_to_fallback` is running concurrently, there's a chance `sock_poll` might read a stale or "torn" pointer. But the write is atomic since we're dealing with an 8-byte pointer on a 64-bit system, right? The KCSAN report does confirm it sees two different, valid-looking pointers though.

If `sock_poll` reads the old pointer (SMC socket), it uses `smc_sock_ops`. If it reads the new pointer (CLC socket), it uses `clcsock->ops`. The question is, does it cause issues if we use the old pointer when the fallback is happening? `smc_poll` *should* handle the `use_fallback` flag, so it seems designed to account for this.

`smc_switch_to_fallback` is changing `file->private_data`. Usually, that field in a socket file is set once at open time. SMC is special because it can dynamically fall back to a TCP socket. `smc_switch_to_fallback` is called under `smc->clcsock_release_lock`, but `sock_poll` doesn't hold any locks, which is the crux of the issue.

Let's trace: if `sock_poll` reads the SMC socket, it calls `smc_poll`. `smc_poll` then checks `smc->use_fallback`. If `true`, it calls the CLC socket's poll function via `smc->clcsock->ops->poll`. Wait, `smc_poll` takes the `struct socket *sock` argument, which it gets from `sock_poll` which read it from `file->private_data`.. If it reads the CLC socket, it simply calls `clcsock->ops->poll`

No risk of `smc->clcsock` being `NULL`, that's checked. I also checked that `smc->clcsock->file` is set to the same `file` as the SMC socket, so no confusion there. And, if `tcp_poll` is called, the `file` argument is used for `sock_poll_wait`, but `sock_poll_wait` uses the `socket` argument passed to it, so that's fine. `clcsock->wq.wait` will have the correct waiters added no matter which path is taken.

So, from a purely functional perspective, it *looks* okay.

The KCSAN report is flagging a data race because of the lack of synchronization on the read and write of `file->private_data`. Since the read is atomic, it will always see a valid socket pointer, either the SMC or the CLC. And, in the case it sees the SMC socket, `smc->use_fallback` has been set, right?

But, if `sock_poll` read the SMC socket and *then* the SMC socket is destroyed? No, the SMC socket is the one the user has a file descriptor for. It won't be destroyed until the file is closed. `smc->clcsock` is initialized when the SMC socket is created.

Thinking it through again, `smc_switch_to_fallback` can be called from `connect` or `sendmsg`, and in the report, it is called from `smc_sendmsg`.

So, the sequence looks like:
1.  `sock_poll` reads `file->private_data`.
2.  `smc_switch_to_fallback` writes `file->private_data = smc->clcsock`.

If `sock_poll` sees the old value (SMC socket), `smc_poll` will be called, which will correctly check the `use_fallback` flag and use the `clcsock`. If it sees the new value (CLC socket), it calls `clcsock->ops->poll` directly, which is correct. There is no case where `sock_poll` should see a partially written pointer, because the `file->private_data` is an 8-byte pointer and is


13/2 2026/02/04 14:17 tool codesearch-definition-source
0m Error:
requested entity does not exist

Args:
map[ContextFile:net/smc/af_smc.c IncludeLines:true Name:smc_clcsock_alloc]

Results:
map[SourceCode: SourceFile:]

14/2 2026/02/04 14:17 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 12854
							output: 18
							thoughts: 39
						

Thoughts:
Okay, here's my take on those thoughts, framed as an expert's internal monologue:

**Tracking Down a Socket Allocation**

Alright, right, where *is* `clcsock` being allocated? Need to quickly pinpoint that. Forget the theory for a moment; it's hands-on time. Okay, I'll go straight to `net/smc/af_smc.c`. That's where I'd expect to find the core SMC socket implementation, so that's the logical starting point. Let's see what we've got in that file...



15/2 2026/02/04 14:18 tool grepper
0m Args:
map[Expression:clcsock =]

Results:
map[Output:net/smc/af_smc.c=1726=static int smc_clcsock_accept(struct smc_sock *lsmc, struct smc_sock **new_smc)
net/smc/af_smc.c-1727-{
net/smc/af_smc.c:1728:	struct socket *new_clcsock = NULL;
net/smc/af_smc.c-1729-	struct sock *lsk = &lsmc->sk;
--
net/smc/af_smc.c-1778-
net/smc/af_smc.c:1779:	(*new_smc)->clcsock = new_clcsock;
net/smc/af_smc.c-1780-out:
--
net/smc/af_smc.c=1813=struct sock *smc_accept_dequeue(struct sock *parent,
--
net/smc/af_smc.c-1826-				sock_release(isk->clcsock);
net/smc/af_smc.c:1827:				isk->clcsock = NULL;
net/smc/af_smc.c-1828-			}
--
net/smc/af_smc.c=2045=static int smc_listen_prfx_check(struct smc_sock *new_smc,
--
net/smc/af_smc.c-2048-	struct smc_clc_msg_proposal_prefix *pclc_prfx;
net/smc/af_smc.c:2049:	struct socket *newclcsock = new_smc->clcsock;
net/smc/af_smc.c-2050-
--
net/smc/af_smc.c=2438=static void smc_listen_work(struct work_struct *work)
--
net/smc/af_smc.c-2441-						smc_listen_work);
net/smc/af_smc.c:2442:	struct socket *newclcsock = new_smc->clcsock;
net/smc/af_smc.c-2443-	struct smc_clc_msg_accept_confirm *cclc;
--
net/smc/af_smc.c=3360=static int __smc_create(struct net *net, struct socket *sock, int protocol,
--
net/smc/af_smc.c-3387-	if (clcsock)
net/smc/af_smc.c:3388:		smc->clcsock = clcsock;
net/smc/af_smc.c-3389-	else
--
net/smc/smc_close.c=24=void smc_clcsock_release(struct smc_sock *smc)
--
net/smc/smc_close.c-32-		tcp = smc->clcsock;
net/smc/smc_close.c:33:		smc->clcsock = NULL;
net/smc/smc_close.c-34-		sock_release(tcp);
--
net/smc/smc_close.c=128=void smc_close_active_abort(struct smc_sock *smc)
--
net/smc/smc_close.c-130-	struct sock *sk = &smc->sk;
net/smc/smc_close.c:131:	bool release_clcsock = false;
net/smc/smc_close.c-132-
--
net/smc/smc_close.c-157-		smc_conn_free(&smc->conn);
net/smc/smc_close.c:158:		release_clcsock = true;
net/smc/smc_close.c-159-		sock_put(sk); /* passive closing */
--
net/smc/smc_close.c-168-		smc_conn_free(&smc->conn);
net/smc/smc_close.c:169:		release_clcsock = true;
net/smc/smc_close.c-170-		break;
--
net/smc/smc_close.c=356=static void smc_close_passive_work(struct work_struct *work)
--
net/smc/smc_close.c-362-	struct smc_cdc_conn_state_flags *rxflags;
net/smc/smc_close.c:363:	bool release_clcsock = false;
net/smc/smc_close.c-364-	struct sock *sk = &smc->sk;
--
net/smc/smc_close.c-437-			if (smc->clcsock)
net/smc/smc_close.c:438:				release_clcsock = true;
net/smc/smc_close.c-439-		}
]

16/2 2026/02/04 14:18 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 14351
							output: 45
							thoughts: 486
						

Thoughts:
**Analyzing a Potential Race Condition in the SMC Fallback Mechanism**

Okay, so I'm looking at this SMC code, specifically the fallback mechanism. It looks like `clcsock`, the underlying TCP socket, is initialized early on, specifically in `__smc_create`. The interesting part is that `smc_switch_to_fallback` is responsible for transitioning from SMC to using the `clcsock` socket directly.

My concern is this race condition: Thread 1 is calling `smc_switch_to_fallback`, and Thread 2 is calling `sock_poll`.  `smc_switch_to_fallback` updates `clcsock->file->private_data` with the `clcsock` pointer. Meanwhile, `sock_poll` is reading `file->private_data` to determine the socket type.  If the read in `sock_poll` happens *during* the write in `smc_switch_to_fallback`, there's a problem.

Here's how it breaks down:  If `sock_poll` gets the *old* value, it uses the SMC-specific poll routine `smc_poll`. If it gets the *new* value, it uses the TCP routine `tcp_poll`. The inconsistency here is the problem.

`smc_switch_to_fallback` is touching other fields in the file structure too, specifically `fasync_list`. However, `sock_poll` isn't using `fasync_list`, so that doesn't seem to be an immediate concern for this specific function.

But, I'm thinking... are there *other* syscalls potentially being called while `smc_switch_to_fallback` is in progress? Specifically, what about `sendmsg`? `smc_sendmsg` has a fallback mechanism that *also* calls `smc_switch_to_fallback` if the SMC send fails.

So, I need to look at `smc_sendmsg` more closely. I'll need to understand how the locking works, to determine if we have a race condition or not. Let me dive into that function now.



17/2 2026/02/04 14:18 tool codesearch-definition-source
0m Args:
map[ContextFile:net/smc/af_smc.c IncludeLines:true Name:smc_sendmsg]

Results:
map[SourceCode:2784:	int smc_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
2785:	{
2786:		struct sock *sk = sock->sk;
2787:		struct smc_sock *smc;
2788:		int rc;
2789:	
2790:		smc = smc_sk(sk);
2791:		lock_sock(sk);
2792:	
2793:		/* SMC does not support connect with fastopen */
2794:		if (msg->msg_flags & MSG_FASTOPEN) {
2795:			/* not connected yet, fallback */
2796:			if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) {
2797:				rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
2798:				if (rc)
2799:					goto out;
2800:			} else {
2801:				rc = -EINVAL;
2802:				goto out;
2803:			}
2804:		} else if ((sk->sk_state != SMC_ACTIVE) &&
2805:			   (sk->sk_state != SMC_APPCLOSEWAIT1) &&
2806:			   (sk->sk_state != SMC_INIT)) {
2807:			rc = -EPIPE;
2808:			goto out;
2809:		}
2810:	
2811:		if (smc->use_fallback) {
2812:			rc = smc->clcsock->ops->sendmsg(smc->clcsock, msg, len);
2813:		} else {
2814:			rc = smc_tx_sendmsg(smc, msg, len);
2815:			SMC_STAT_TX_PAYLOAD(smc, len, rc);
2816:		}
2817:	out:
2818:		release_sock(sk);
2819:		return rc;
2820:	}
 SourceFile:net/smc/af_smc.c]

18/2 2026/02/04 14:18 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 15932
							output: 37
							thoughts: 1628
						

Thoughts:
**Analyzing the SMC Fallback Mechanism and Potential Race Conditions**

Okay, here's what I'm thinking, walking through this code. In `smc_sendmsg`, the call to `smc_switch_to_fallback` is the first thing to trip this off. Right, so if that fallback is triggered, `smc->use_fallback` becomes true, and it then calls `smc->clcsock->ops->sendmsg`. The reported race is between the `smc_switch_to_fallback` function, which is doing a write on `file->private_data`, and `sock_poll`, which is *reading* from `file->private_data`.

If `sock_poll` is called on the same file descriptor, this could lead to the race condition.

Can the following happen? Can `sock_poll` read `file->private_data` and get the SMC socket, then `smc_switch_to_fallback` changes it to CLC socket, and then the SMC socket is freed? No, that's not possible, because the SMC socket's socket pointer is a parent socket to `sock->sk`. It is held by the file descriptor.

So `smc_switch_to_fallback` is changing `file->private_data` from one socket to another. The file descriptor itself still exists.

`sock_poll` gets the file from the file descriptor. It reads `file->private_data`. If it gets the SMC socket, it calls `smc_poll`. If it gets the CLC socket, it calls `tcp_poll`.

But is there a problem with `tcp_poll` being called on a `file` whose `private_data` *has* already been changed? No, `tcp_poll` doesn't even look at `file->private_data`. It gets the `sock` from what's passed to it.

```c
__poll_t tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
```

Right, `sock_poll` passes `sock`, which it just read from `file->private_data`.

```c
1427:		struct socket *sock = file->private_data;
1428:		const struct proto_ops *ops = READ_ONCE(sock->ops);
...
1443:		return ops->poll(file, sock, wait) | flag;
```

If `sock` is the CLC socket, `ops` becomes `inet_stream_ops`. `inet_stream_ops.poll` is `tcp_poll`, so that's correct: `tcp_poll(file, clcsock, wait)`.

If `sock` is the SMC socket, `ops` becomes `smc_sock_ops`, and `smc_sock_ops.poll` is `smc_poll`, so that calls `smc_poll(file, smc_sock, wait)`. Inside `smc_poll`:

```c
2883:		if (smc->use_fallback) {
2884:			/* delegate to CLC child sock */
2885:			mask = smc->clcsock->ops->poll(file, smc->clcsock, wait);
```

This then *also* calls `tcp_poll(file, clcsock, wait)`. So, it seems either path, the end result is the same.

Is there a race condition with `ops = READ_ONCE(sock->ops)` and `sock` changing? No, `sock` is a local variable in `sock_poll`. But what if `sock` changes between line 1427 and 1428? It can't, because `sock` is local. It's `file->private_data` that's changing.

If `sock_poll` does the following:
1.  `sock = file->private_data;` (reads SMC socket)
2.  `smc_switch_to_fallback` writes `file->private_data = clcsock`.
3.  `ops = READ_ONCE(sock->ops);` (reads `smc_sock_ops` from SMC socket)
4.  `ops->poll(file, sock, wait);` (calls `smc_poll(file, smc_sock, wait)`)

This is fine.

If `sock_poll` does this:
1.  `smc_switch_to_fallback` writes `file->private_data = clcsock`.
2.  `sock = file->private_data;` (reads


19/2 2026/02/04 14:18 tool codesearch-definition-source
0m Args:
map[ContextFile:net/socket.c IncludeLines:true Name:sock_close]

Results:
map[SourceCode:1453:	static int sock_close(struct inode *inode, struct file *filp)
1454:	{
1455:		__sock_release(SOCKET_I(inode), inode);
1456:		return 0;
1457:	}
 SourceFile:net/socket.c]

20/2 2026/02/04 14:18 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 18721
							output: 32
							thoughts: 2062
						

Thoughts:
**Analyzing a Potential Race Condition in SMC Fallback**

Okay, so I'm digging into this `sock_close` function, and it seems to rely on `SOCKET_I` which gets the socket from the inode. In Linux, the inode and socket are intrinsically linked, which is generally fine. But, hold on... if SMC's fallback mechanism replaces `file->private_data`, does it also replace the associated inode? No, `file->f_inode` remains untouched, but `file->private_data` now points to an entirely *different* socket, which has *its own* distinct inode.

That immediately sets off alarm bells!  `file->f_inode` pointing to SMC's inode (Inode A), while `file->private_data` now references a CLC socket (Socket B) with its own Inode B... This feels like it has the potential to lead to issues later. `sock_close` will operate on the SMC socket because it uses `file->f_inode`, and that should be fine.  SMC, in its normal course of action, would release its own CLC socket using its `release` function.

However, if `smc_switch_to_fallback` happens, we're in a situation where `file->private_data` now points to `clcsock`. But, `file->f_inode` still links back to the SMC socket's inode. So, from then on, the CLC socket is now in use for everything, which is the intention.

So, how do other system calls handle this change? `sock_sendmsg` calls `sock_from_file(file)`. `sockfd_lookup_light` calls `sock_from_file(f.file)`. `sock_from_file` then simply returns `file->private_data`. So, in a switch to fallback, we'll now be using the CLC socket. This appears to be the expected behavior, the `smc_switch_to_fallback` is doing what it is supposed to.

But the question in my mind is, is it safe to change `file->private_data` while other threads might still be using it? I remember KCSAN found a race with `sock_poll` specifically.

If `sock_poll` reads `file->private_data` and it's the SMC socket, it calls `smc_poll`, which eventually checks `smc->use_fallback` and if that is true, calls the CLC socket's poll function.

If it reads `file->private_data` and gets the CLC socket, it simply calls the CLC socket's poll function.

So, either way, there's no inherent problem so long as the value is correct. The pointer is 64-bit aligned, so it shouldn't be torn during the read/write.

But wait, what if `smc->clcsock` itself is changing while we're doing this?  `smc_switch_to_fallback` modifies `smc->clcsock->file->private_data`. Now, I need to check if another thread could be modifying `smc->clcsock`. Looking at the code, it's set on creation and only cleared on destruction.

So, the race is *specifically* on `file->private_data`. Since it's a pointer, being switched between two valid socket structures, and both sockets are kept alive (one by the file descriptor, one by the SMC socket), this seems relatively benign.

Still, kernel code usually uses `READ_ONCE`/`WRITE_ONCE` for such pointer modifications to avoid compiler optimizations that could break atomicity.

In `sock_poll`, we simply read `file->private_data`.  In `smc_switch_to_fallback`, there's a simple write.

If the compiler decides to reload `file->private_data` in `sock_poll`, we get different values. The compiler could load `sock->ops` using `READ_ONCE`, but is there a compiler barrier for the read of `file->private_data`? No, there isn't!

Wait... `sock` is a local variable, so the compiler should keep it in a register. But under register pressure, or if the compiler thinks it's cheaper to reload from `file->private_data` (assuming it doesn't change), it *might* do so. But here, `file->private_data` *can* change, and the compiler isn't aware of this unless it's `volatile` or accessed with `READ_ONCE`.

If the compiler reloads `file->private_data`, it's making an incorrect assumption. This is exactly the kind of thing we want to avoid


21/2 2026/02/04 14:18 tool codesearch-struct-layout
0m Args:
map[ContextFile:include/linux/fs.h Name:file]

Results:
map[Fields:[map[Name:f_lock OffsetBits:0 SizeBits:32] map[Name:f_mode OffsetBits:32 SizeBits:32] map[Name:f_op OffsetBits:64 SizeBits:64] map[Name:f_mapping OffsetBits:128 SizeBits:64] map[Name:private_data OffsetBits:192 SizeBits:64] map[Name:f_inode OffsetBits:256 SizeBits:64] map[Name:f_flags OffsetBits:320 SizeBits:32] map[Name:f_iocb_flags OffsetBits:352 SizeBits:32] map[Name:f_cred OffsetBits:384 SizeBits:64] map[Name:f_owner OffsetBits:448 SizeBits:64] map[Name: OffsetBits:512 SizeBits:128] map[Name: OffsetBits:640 SizeBits:256] map[Name:f_pos OffsetBits:896 SizeBits:64] map[Name:f_security OffsetBits:960 SizeBits:64] map[Name:f_wb_err OffsetBits:1024 SizeBits:32] map[Name:f_sb_err OffsetBits:1056 SizeBits:32] map[Name:f_ep OffsetBits:1088 SizeBits:64] map[Name: OffsetBits:1152 SizeBits:256] map[Name:f_ref OffsetBits:1408 SizeBits:64]]]

22/2 2026/02/04 14:18 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 22255
							output: 49
							thoughts: 2108
						

Thoughts:
**Analyzing the SMC Fallback Mechanism and Potential Races**

Okay, so I'm digging into this SMC fallback implementation and trying to wrap my head around potential data races, especially concerning this `file->private_data` field and the switching of socket identities. Here's what I'm seeing:

Initially, `private_data` at offset 192 bits (24 bytes) holds the SMC socket pointer. The `smc_switch_to_fallback` function is the point of interest, as it's changing the value of this field from the SMC socket to a CLC (TCP) socket. The question is: when and how could this change cause a problem?

The core concern is this: if a compiler reloads `file->private_data` at the wrong time, we might have type confusion. But even without that, there's another concern if the field isn't reloaded: the `sock_poll` function could read the SMC socket, then `smc_switch_to_fallback` completes, and then the file is closed.

I've traced the logic in `sock_poll`.  It retrieves the `struct socket *` using `file->private_data`. If the fallback has already occurred, we correctly get the TCP socket, and everything works. It calls `sock_from_file`, which seems to be the right way to retrieve the socket regardless of whether it's the original SMC or the fallback.

If `sock_poll` is running and reads the SMC socket before the fallback, the `sock_poll` implementation goes through `smc_poll`. `smc_poll` then uses the `smc->use_fallback` flag, which is set *before* the `file->private_data` is changed, delegating to the TCP socket when it is true. This all looks safe on the surface.

KCSAN flagged this with a data race. Is it benign? It *seems* the code is designed to handle this, but the data race is still concerning. The potential for a "type confusion" if the compiler reloads the pointer remains a concern. The code needs either `data_race()` calls or atomic primitives to eliminate this risk.

Looking closer, I'm analyzing the order of operations in `smc_switch_to_fallback`. It sets `smc->use_fallback = true` *before* it updates `file->private_data`.  This ensures that any calls to `smc_poll` will correctly delegate to the TCP socket.  If `sock_poll` reads the SMC socket, it later calls `smc_poll` which should see `smc->use_fallback == true`. The logic flows to `tcp_poll`. This is a clean transition. It seems robust *so far*.

However, I'm now realizing `smc_switch_to_fallback` isn't just changing `private_data`; it's also setting `smc->clcsock->file`.  This is where I'm concerned. Normally, the `file` pointer is fixed.  Now, the internal CLC socket gets its `file` pointer set to the `file` for the SMC socket. Could this lead to problems when other parts of the kernel use `sock->file`?

My analysis suggests that the `file` pointer is passed *down* to functions like `tcp_poll`, and it's that *passed* `file`, not `sock->file`, that is consistently used in the call chain. Therefore, I don't see any immediate issue.

The thing is, I'm also looking at `smc_fback_replace_callbacks`. This makes sure that events on the CLC socket are re-routed to the right code. `smc_fback_data_ready` is what I really need to examine in detail.

So, in summary, the fallback logic *seems* sound, specifically, because `use_fallback` is set *before* `private_data` is updated, and the code consistently passes the file pointer down. I think the KCSAN report is correct that there is a data race. It's likely benign, but needs to be evaluated. It needs to be explicitly marked as such to avoid future confusion or potential issues from compiler optimizations or future changes.



23/2 2026/02/04 14:19 tool codesearch-definition-source
0m Args:
map[ContextFile:net/smc/af_smc.c IncludeLines:true Name:smc_fback_data_ready]

Results:
map[SourceCode: 862:	static void smc_fback_data_ready(struct sock *clcsk)
 863:	{
 864:		struct smc_sock *smc;
 865:	
 866:		read_lock_bh(&clcsk->sk_callback_lock);
 867:		smc = smc_clcsock_user_data(clcsk);
 868:		if (smc)
 869:			smc_fback_forward_wakeup(smc, clcsk,
 870:						 smc->clcsk_data_ready);
 871:		read_unlock_bh(&clcsk->sk_callback_lock);
 872:	}
 SourceFile:net/smc/af_smc.c]

24/2 2026/02/04 14:19 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 25549
							output: 49
							thoughts: 12
						

Thoughts:
Okay, here's my summary, tailored for an expert audience, as if it's a direct representation of my thought process:

**Analyzing the `smc_fback_forward_wakeup` Function**

Alright, let's dissect this `smc_fback_forward_wakeup` function. Immediately, I'm thinking about the implications of "forward wakeup." This suggests we're dealing with a system that's been dormant or in a low-power state and needs to be roused proactively, likely driven by some external trigger or a scheduled event, as opposed to a purely reactive interrupt. My initial question is: what is the context? Is this related to a specific hardware component (like a sensor, a particular communication protocol, or a system timer)? Understanding the architectural dependency is the key.

I'm mentally mapping the function's potential roles.  Could it be a part of the System Management Controller (SMC) – this immediately flags the usual suspects: power management, clock control, thermal management. Perhaps its job is to signal the main CPU, a co-processor, or even a specific hardware block that a particular condition has been met, and thus initiate a state transition. I'm focusing on "forward" – which means it's not a response. This also suggests the function is controlling the states of multiple things at once; otherwise it wouldn't need to be so specific. It feels like a multi-level state change might be occurring here.

I'm mentally running through potential scenarios: Is it a low-power interface between a sensor and a more power-hungry core? Is it responsible for coordinating the wake-up of multiple devices in a controlled sequence to minimize inrush current or power spikes? Is it doing something with an interrupt controller? How is it implemented? Is it handling hardware interrupts, memory-mapped registers, or a dedicated communication channel within the SMC?

I need to understand what 'fback' refers to here. Is it a feedback mechanism? Does it involve an internal loop, perhaps monitoring a specific register or condition before initiating the wakeup sequence? This is the core of the function and the most important question for me, because it shows the dependencies. This function would need a lot of testing, and careful design because any issues with the feedback could result in unexpected outcomes. I want to look for an associated datasheet for the specific hardware, because that provides the clearest information.



25/2 2026/02/04 14:19 tool codesearch-definition-source
0m Args:
map[ContextFile:net/smc/af_smc.c IncludeLines:true Name:smc_fback_forward_wakeup]

Results:
map[SourceCode: 828:	static void smc_fback_forward_wakeup(struct smc_sock *smc, struct sock *clcsk,
 829:					     void (*clcsock_callback)(struct sock *sk))
 830:	{
 831:		struct smc_mark_woken mark = { .woken = false };
 832:		struct socket_wq *wq;
 833:	
 834:		init_waitqueue_func_entry(&mark.wait_entry,
 835:					  smc_fback_mark_woken);
 836:		rcu_read_lock();
 837:		wq = rcu_dereference(clcsk->sk_wq);
 838:		if (!wq)
 839:			goto out;
 840:		add_wait_queue(sk_sleep(clcsk), &mark.wait_entry);
 841:		clcsock_callback(clcsk);
 842:		remove_wait_queue(sk_sleep(clcsk), &mark.wait_entry);
 843:	
 844:		if (mark.woken)
 845:			smc_fback_wakeup_waitqueue(smc, mark.key);
 846:	out:
 847:		rcu_read_unlock();
 848:	}
 SourceFile:net/smc/af_smc.c]

26/2 2026/02/04 14:19 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 26501
							output: 22
							thoughts: 831
						

Thoughts:
**Analyzing the SMC Fallback Mechanism and Potential Data Races**

Okay, this is becoming a bit involved, but it seems like the designers of the SMC system are really trying to make the fallback process as seamless as possible. I'm revisiting the data race issue.

The core of it is this: `sock_poll` might read `file->private_data`, which could be in the middle of being changed.  It could be the old (SMC) value or the new (fallback) value. Initially, I thought that either value would be safe within `sock_poll`.

But there's another wrinkle. What if `smc_switch_to_fallback` is running concurrently with `sock_poll`? What if `sock_poll` reads the OLD SMC socket value because of CPU reordering or caching? Memory barriers or `READ_ONCE` are there for a reason... But even with the old value, it *should* be safe because `smc_poll` will check `smc->use_fallback`.

Ah, but now I see another problem! `smc->use_fallback` is just a regular field!  If `sock_poll` sees the old `file->private_data` (SMC socket) AND `smc_poll` sees the old `smc->use_fallback` (false), `smc_poll` will proceed as though it's still an SMC socket.  But `smc_switch_to_fallback` is *already* running and might be making changes that `smc_poll` relies on!

Let me look at what `smc_poll` does when `use_fallback` is false:
```c
2888:			if (sk->sk_state != SMC_CLOSED)
2889:				sock_poll_wait(file, sock, wait);
...
2903:				if ((sk->sk_state != SMC_INIT &&
2904:				     atomic_read(&smc->conn.sndbuf_space)) ||
```

It uses `smc->conn`! Does `smc_switch_to_fallback` destroy `smc->conn`? No, it doesn't look like it destroys it. It just stops using it for new data.

Wait, if `smc_switch_to_fallback` is called, it's usually because we *failed* to set up the SMC connection.

So, if `smc_poll` *thinks* it's still SMC and it's running, it'll wait on `smc->sk.sk_wq`. But `smc_switch_to_fallback` is moving everything to the `clcsock`.

If `smc_poll` adds a waiter to `smc->sk.sk_wq`, will it *ever* be woken up?

Aha! `smc_fback_replace_callbacks` makes sure that events on `clcsock` will wake up `smc->sk.sk_wq` via `smc_fback_wakeup_waitqueue`.
```c
 845:			smc_fback_wakeup_waitqueue(smc, mark.key);
```

So even if `smc_poll` uses the "old" path, it *should* still work correctly.

The data race on `file->private_data` is *definitely* present. The key is to determine if it's *harmful*.

The only real dangers with data races are:
1.  Torn reads/writes (not an issue here on x86_64).
2.  Compiler reloads (possible issue, but not likely to cause a crash because both sockets are valid).
3.  Logic errors due to an inconsistent view of the state.

In this case, it seems that the SMC system has been designed with this transition in mind.

However, I'm going to do a quick search for commits using "smc_switch_to_fallback" and "race" to see if there are any related issues that have been addressed.



27/2 2026/02/04 14:19 tool grepper
0m Args:
map[Expression:smc_switch_to_fallback]

Results:
map[Output:net/smc/af_smc.c=898=static void smc_fback_replace_callbacks(struct smc_sock *smc)
--
net/smc/af_smc.c-916-
net/smc/af_smc.c:917:static int smc_switch_to_fallback(struct smc_sock *smc, int reason_code)
net/smc/af_smc.c-918-{
--
net/smc/af_smc.c-929-	smc_stat_fallback(smc);
net/smc/af_smc.c:930:	trace_smc_switch_to_fallback(smc, reason_code);
net/smc/af_smc.c-931-	if (smc->sk.sk_socket && smc->sk.sk_socket->file) {
--
net/smc/af_smc.c=950=static int smc_connect_fallback(struct smc_sock *smc, int reason_code)
--
net/smc/af_smc.c-954-
net/smc/af_smc.c:955:	rc = smc_switch_to_fallback(smc, reason_code);
net/smc/af_smc.c-956-	if (rc) { /* fallback fails */
--
net/smc/af_smc.c=1962=static void smc_listen_decline(struct smc_sock *new_smc, int reason_code,
--
net/smc/af_smc.c-1967-	if (reason_code < 0 ||
net/smc/af_smc.c:1968:	    smc_switch_to_fallback(new_smc, reason_code)) {
net/smc/af_smc.c-1969-		/* error, no fallback possible */
--
net/smc/af_smc.c=2438=static void smc_listen_work(struct work_struct *work)
--
net/smc/af_smc.c-2461-	if (!tcp_sk(newclcsock->sk)->syn_smc) {
net/smc/af_smc.c:2462:		rc = smc_switch_to_fallback(new_smc, SMC_CLC_DECL_PEERNOSMC);
net/smc/af_smc.c-2463-		if (rc)
--
net/smc/af_smc.c=2784=int smc_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
--
net/smc/af_smc.c-2796-		if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) {
net/smc/af_smc.c:2797:			rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
net/smc/af_smc.c-2798-			if (rc)
--
net/smc/af_smc.c=3072=int smc_setsockopt(struct socket *sock, int level, int optname,
--
net/smc/af_smc.c-3119-		if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) {
net/smc/af_smc.c:3120:			rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
net/smc/af_smc.c-3121-		} else {
--
net/smc/smc_tracepoint.c-5-
net/smc/smc_tracepoint.c:6:EXPORT_TRACEPOINT_SYMBOL(smc_switch_to_fallback);
net/smc/smc_tracepoint.c-7-EXPORT_TRACEPOINT_SYMBOL(smc_tx_sendmsg);
--
net/smc/smc_tracepoint.h-15-
net/smc/smc_tracepoint.h:16:TRACE_EVENT(smc_switch_to_fallback,
net/smc/smc_tracepoint.h-17-
--
tools/testing/selftests/bpf/progs/bpf_smc.c=25=int BPF_PROG(bpf_smc_release, struct socket *sock)
--
tools/testing/selftests/bpf/progs/bpf_smc.c-33-
tools/testing/selftests/bpf/progs/bpf_smc.c:34:SEC("fentry/smc_switch_to_fallback")
tools/testing/selftests/bpf/progs/bpf_smc.c:35:int BPF_PROG(bpf_smc_switch_to_fallback, struct smc_sock___local *smc)
tools/testing/selftests/bpf/progs/bpf_smc.c-36-{
]

28/2 2026/02/04 14:19 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 29537
							output: 18
							thoughts: 1004
						

Thoughts:
**Analyzing the SMC `file->private_data` Race**

Okay, so the `file->private_data` field, specifically when dealing with SMC sockets, is the core of this issue.  The race condition centers around the changing of this pointer and how it interacts with `sock_poll`, especially when used with `io_uring`.

My immediate concern was that if `file->private_data` changed, `io_uring` might be holding onto a stale pointer. However, `io_uring` typically works directly with the `file` object itself, not the private data within, so it's not likely to cache the socket pointer directly.

The fact that `file->private_data` changes at all is unusual and rings alarm bells. The kernel relies on pointer stability. That's a classic source of problems.

Looking closer at `sock_poll`, I see the critical lines:

```c
struct socket *sock = file->private_data;
const struct proto_ops *ops = READ_ONCE(sock->ops);
...
return ops->poll(file, sock, wait) | flag;
```

It fetches the `sock` pointer, and crucially, it reads the `sock->ops` *before* the poll call.  This means that if `smc_switch_to_fallback` runs between getting the `sock` and making the call, `sock` is the `SMC_socket`.  The critical point here is in the `smc_poll` call:

```c
mask = smc->clcsock->ops->poll(file, smc->clcsock, wait);
```

It *correctly* delegates to `smc->clcsock`, which becomes the `CLC_socket`. So, even if the "old" SMC socket is used in the initial call to `sock_poll`, the actual poll operation correctly uses the fallback CLC socket.

However, where else is `file->private_data` being used? Places like `sock_read_iter` and `sock_write_iter` come to mind. I need to keep looking at where this pointer is used.

The same sort of delegation is at play with `sockfd_lookup_light` and `smc_sendmsg`.  If a fallback happens after the lookup but before the operation, `smc_sendmsg` handles it correctly. This indicates the SMC architecture is designed around this "delegation" of the fallback behavior.

Now, why change `file->private_data` in the first place?  Ah, right. It's a "fast path optimization." After a fallback to TCP, we want to bypass the SMC layer entirely for subsequent calls to avoid the overhead of the SMC layer.

So, this race on `file->private_data` is a deliberate optimization. The intention is that it *will* eventually point to the CLC socket. Until then, we go through the SMC layer, which correctly delegates the calls.

Is it benign? KCSAN is flagging it as a race because it's unsynchronized. To silence KCSAN, using `READ_ONCE` and `WRITE_ONCE` would be the way to go.

But is there actual harm? If the read of `file->private_data` is atomic, it shouldn't cause an issue. And on the supported architectures, pointer reads and writes are indeed atomic. I'll need to double-check this for edge cases, though.

Wait, I recall seeing a similar issue reported before. I'll look at the history of `net/smc/af_smc.c`. There was a patch for a similar race in `smc_switch_to_fallback`. I'll cross-reference what I'm seeing here with that earlier fix.



29/2 2026/02/04 14:19 tool grepper
0m Args:
map[Expression:private_data =]

Results:
map[Output:
Full output is too long, showing 500 out of 9796 lines.
Use more precise expression if possible.

[Documentation/driver-api/usb/writing_usb_driver.rst=171=of this is done with the following code::
 --
 Documentation/driver-api/usb/writing_usb_driver.rst-176-    /* save our object in the file's private structure */
 Documentation/driver-api/usb/writing_usb_driver.rst:177:    file->private_data = dev;
 Documentation/driver-api/usb/writing_usb_driver.rst-178-
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst=1129=shows only the skeleton, how to build up the PCM interfaces::
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-1314-                      return err;
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst:1315:              pcm->private_data = chip;
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-1316-              strcpy(pcm->name, "My Chip");
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst=1336=function. It would be better to create a constructor for the PCM, namely::
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-1345-                  return err;
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst:1346:          pcm->private_data = chip;
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-1347-          strcpy(pcm->name, "My Chip");
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst=1428=destructor function to ``pcm->private_free``::
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-1445-              /* set the destructor */
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst:1446:              pcm->private_data = chip;
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-1447-              pcm->private_free = mychip_pcm_free;
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst=1734=callback::
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-1740-          data = kmalloc(sizeof(*data), GFP_KERNEL);
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst:1741:          substream->runtime->private_data = data;
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-1742-          ....
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst=2729=Full Code Example
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-2770-              memset(&ac97, 0, sizeof(ac97));
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst:2771:              ac97.private_data = chip;
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-2772-              return snd_ac97_mixer(bus, &ac97, &chip->ac97);
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst=2793=record together with the bus pointer created above::
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-2798-  memset(&ac97, 0, sizeof(ac97));
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst:2799:  ac97.private_data = chip;
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-2800-  snd_ac97_mixer(bus, &ac97, &chip->ac97);
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst=3034=function::
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-3039-          return err;
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst:3040:  rmidi->private_data = chip;
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-3041-  strcpy(rmidi->name, "My MIDI");
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst=3302=destructor function is set in the ``private_free`` field::
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-3304-  struct mydata *p = kmalloc(sizeof(*p), GFP_KERNEL);
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst:3305:  hw->private_data = p;
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-3306-  hw->private_free = mydata_free;
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst=3666=For a raw-data proc-file, set the attributes as follows::
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-3672-  entry->content = SNDRV_INFO_CONTENT_DATA;
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst:3673:  entry->private_data = chip;
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-3674-  entry->c.ops = &my_file_io_ops;
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst=3823=chip data individually::
 --
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-3837-          ....
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst:3838:          card->private_data = chip;
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst-3839-          ....
 --
 arch/arm/kernel/bios32.c=427=static void pcibios_init_hw(struct device *parent, struct hw_pci *hw,
 --
 arch/arm/kernel/bios32.c-448-		if (hw->private_data)
 arch/arm/kernel/bios32.c:449:			sys->private_data = hw->private_data[nr];
 arch/arm/kernel/bios32.c-450-
 --
 arch/arm/mach-dove/pcie.c=35=static int __init dove_pcie_setup(int nr, struct pci_sys_data *sys)
 --
 arch/arm/mach-dove/pcie.c-43-	pp = &pcie_port[nr];
 arch/arm/mach-dove/pcie.c:44:	sys->private_data = pp;
 arch/arm/mach-dove/pcie.c-45-	pp->root_bus_nr = sys->busnr;
 --
 arch/arm/mach-mv78xx0/pcie.c=98=static int __init mv78xx0_pcie_setup(int nr, struct pci_sys_data *sys)
 --
 arch/arm/mach-mv78xx0/pcie.c-106-	pp = &pcie_port[nr];
 arch/arm/mach-mv78xx0/pcie.c:107:	sys->private_data = pp;
 arch/arm/mach-mv78xx0/pcie.c-108-	pp->root_bus_nr = sys->busnr;
 --
 arch/m68k/emu/nfblock.c=97=static int __init nfhd_init_one(int id, u32 blocks, u32 bsize)
 --
 arch/m68k/emu/nfblock.c-133-	dev->disk->fops = &nfhd_ops;
 arch/m68k/emu/nfblock.c:134:	dev->disk->private_data = dev;
 arch/m68k/emu/nfblock.c-135-	sprintf(dev->disk->disk_name, "nfhd%u", dev_id);
 --
 arch/powerpc/kvm/book3s_64_mmu_hv.c=1918=static int kvm_htab_release(struct inode *inode, struct file *filp)
 --
 arch/powerpc/kvm/book3s_64_mmu_hv.c-1921-
 arch/powerpc/kvm/book3s_64_mmu_hv.c:1922:	filp->private_data = NULL;
 arch/powerpc/kvm/book3s_64_mmu_hv.c-1923-	if (!(ctx->flags & KVM_GET_HTAB_WRITE))
 --
 arch/powerpc/kvm/book3s_64_mmu_hv.c=1983=static int debugfs_htab_open(struct inode *inode, struct file *file)
 --
 arch/powerpc/kvm/book3s_64_mmu_hv.c-1994-	mutex_init(&p->mutex);
 arch/powerpc/kvm/book3s_64_mmu_hv.c:1995:	file->private_data = p;
 arch/powerpc/kvm/book3s_64_mmu_hv.c-1996-
 --
 arch/powerpc/kvm/book3s_64_mmu_radix.c=1254=static int debugfs_radix_open(struct inode *inode, struct file *file)
 --
 arch/powerpc/kvm/book3s_64_mmu_radix.c-1265-	mutex_init(&p->mutex);
 arch/powerpc/kvm/book3s_64_mmu_radix.c:1266:	file->private_data = p;
 arch/powerpc/kvm/book3s_64_mmu_radix.c-1267-
 --
 arch/powerpc/kvm/book3s_hv.c=2840=static int debugfs_timings_open(struct inode *inode, struct file *file)
 --
 arch/powerpc/kvm/book3s_hv.c-2850-	p->vcpu = vcpu;
 arch/powerpc/kvm/book3s_hv.c:2851:	file->private_data = p;
 arch/powerpc/kvm/book3s_hv.c-2852-
 --
 arch/powerpc/platforms/52xx/mpc52xx_gpt.c=605=static int mpc52xx_wdt_open(struct inode *inode, struct file *file)
 --
 arch/powerpc/platforms/52xx/mpc52xx_gpt.c-624-
 arch/powerpc/platforms/52xx/mpc52xx_gpt.c:625:	file->private_data = mpc52xx_gpt_wdt;
 arch/powerpc/platforms/52xx/mpc52xx_gpt.c-626-	return stream_open(inode, file);
 --
 arch/powerpc/platforms/book3s/vas-api.c=265=static int coproc_open(struct inode *inode, struct file *fp)
 --
 arch/powerpc/platforms/book3s/vas-api.c-274-					cdev);
 arch/powerpc/platforms/book3s/vas-api.c:275:	fp->private_data = cp_inst;
 arch/powerpc/platforms/book3s/vas-api.c-276-
 --
 arch/powerpc/platforms/book3s/vas-api.c=326=static int coproc_release(struct inode *inode, struct file *fp)
 --
 arch/powerpc/platforms/book3s/vas-api.c-341-	kfree(cp_inst);
 arch/powerpc/platforms/book3s/vas-api.c:342:	fp->private_data = NULL;
 arch/powerpc/platforms/book3s/vas-api.c-343-
 --
 arch/powerpc/platforms/cell/spufs/file.c=44=static int spufs_attr_open(struct inode *inode, struct file *file,
 --
 arch/powerpc/platforms/cell/spufs/file.c-58-	mutex_init(&attr->mutex);
 arch/powerpc/platforms/cell/spufs/file.c:59:	file->private_data = attr;
 arch/powerpc/platforms/cell/spufs/file.c-60-
 --
 arch/powerpc/platforms/cell/spufs/file.c=157=spufs_mem_open(struct inode *inode, struct file *file)
 --
 arch/powerpc/platforms/cell/spufs/file.c-162-	mutex_lock(&ctx->mapping_lock);
 arch/powerpc/platforms/cell/spufs/file.c:163:	file->private_data = ctx;
 arch/powerpc/platforms/cell/spufs/file.c-164-	if (!i->i_openers++)
 --
 arch/powerpc/platforms/cell/spufs/file.c=422=static int spufs_cntl_open(struct inode *inode, struct file *file)
 --
 arch/powerpc/platforms/cell/spufs/file.c-427-	mutex_lock(&ctx->mapping_lock);
 arch/powerpc/platforms/cell/spufs/file.c:428:	file->private_data = ctx;
 arch/powerpc/platforms/cell/spufs/file.c-429-	if (!i->i_openers++)
 --
 arch/powerpc/platforms/cell/spufs/file.c=460=spufs_regs_open(struct inode *inode, struct file *file)
 --
 arch/powerpc/platforms/cell/spufs/file.c-462-	struct spufs_inode_info *i = SPUFS_I(inode);
 arch/powerpc/platforms/cell/spufs/file.c:463:	file->private_data = i->i_ctx;
 arch/powerpc/platforms/cell/spufs/file.c-464-	return 0;
 --
 arch/powerpc/platforms/cell/spufs/file.c=577=static int spufs_pipe_open(struct inode *inode, struct file *file)
 --
 arch/powerpc/platforms/cell/spufs/file.c-579-	struct spufs_inode_info *i = SPUFS_I(inode);
 arch/powerpc/platforms/cell/spufs/file.c:580:	file->private_data = i->i_ctx;
 arch/powerpc/platforms/cell/spufs/file.c-581-
 --
 arch/powerpc/platforms/cell/spufs/file.c=928=static int spufs_signal1_open(struct inode *inode, struct file *file)
 --
 arch/powerpc/platforms/cell/spufs/file.c-933-	mutex_lock(&ctx->mapping_lock);
 arch/powerpc/platforms/cell/spufs/file.c:934:	file->private_data = ctx;
 arch/powerpc/platforms/cell/spufs/file.c-935-	if (!i->i_openers++)
 --
 arch/powerpc/platforms/cell/spufs/file.c=1061=static int spufs_signal2_open(struct inode *inode, struct file *file)
 --
 arch/powerpc/platforms/cell/spufs/file.c-1066-	mutex_lock(&ctx->mapping_lock);
 arch/powerpc/platforms/cell/spufs/file.c:1067:	file->private_data = ctx;
 arch/powerpc/platforms/cell/spufs/file.c-1068-	if (!i->i_openers++)
 --
 arch/powerpc/platforms/cell/spufs/file.c=1304=static int spufs_mss_open(struct inode *inode, struct file *file)
 --
 arch/powerpc/platforms/cell/spufs/file.c-1308-
 arch/powerpc/platforms/cell/spufs/file.c:1309:	file->private_data = i->i_ctx;
 arch/powerpc/platforms/cell/spufs/file.c-1310-
 --
 arch/powerpc/platforms/cell/spufs/file.c=1362=static int spufs_psmap_open(struct inode *inode, struct file *file)
 --
 arch/powerpc/platforms/cell/spufs/file.c-1367-	mutex_lock(&ctx->mapping_lock);
 arch/powerpc/platforms/cell/spufs/file.c:1368:	file->private_data = i->i_ctx;
 arch/powerpc/platforms/cell/spufs/file.c-1369-	if (!i->i_openers++)
 --
 arch/powerpc/platforms/cell/spufs/file.c=1424=static int spufs_mfc_open(struct inode *inode, struct file *file)
 --
 arch/powerpc/platforms/cell/spufs/file.c-1436-	mutex_lock(&ctx->mapping_lock);
 arch/powerpc/platforms/cell/spufs/file.c:1437:	file->private_data = ctx;
 arch/powerpc/platforms/cell/spufs/file.c-1438-	if (!i->i_openers++)
 --
 arch/powerpc/platforms/cell/spufs/file.c=1894=static int spufs_info_open(struct inode *inode, struct file *file)
 --
 arch/powerpc/platforms/cell/spufs/file.c-1897-	struct spu_context *ctx = i->i_ctx;
 arch/powerpc/platforms/cell/spufs/file.c:1898:	file->private_data = ctx;
 arch/powerpc/platforms/cell/spufs/file.c-1899-	return 0;
 --
 arch/powerpc/platforms/powernv/pci-ioda.c=2496=static void __init pnv_pci_init_ioda_phb(struct device_node *np,
 --
 arch/powerpc/platforms/powernv/pci-ioda.c-2548-	}
 arch/powerpc/platforms/powernv/pci-ioda.c:2549:	hose->private_data = phb;
 arch/powerpc/platforms/powernv/pci-ioda.c-2550-	phb->hub_id = hub_id;
 --
 arch/powerpc/platforms/pseries/dtl.c=240=static int dtl_file_open(struct inode *inode, struct file *filp)
 --
 arch/powerpc/platforms/pseries/dtl.c-248-
 arch/powerpc/platforms/pseries/dtl.c:249:	filp->private_data = dtl;
 arch/powerpc/platforms/pseries/dtl.c-250-	return 0;
 --
 arch/powerpc/platforms/pseries/papr-hvpipe.c=443=static int papr_hvpipe_handle_release(struct inode *inode,
 --
 arch/powerpc/platforms/pseries/papr-hvpipe.c-455-	list_del(&src_info->list);
 arch/powerpc/platforms/pseries/papr-hvpipe.c:456:	file->private_data = NULL;
 arch/powerpc/platforms/pseries/papr-hvpipe.c-457-	/*
 --
 arch/powerpc/platforms/pseries/papr-platform-dump.c=211=static int papr_platform_dump_handle_release(struct inode *inode,
 --
 arch/powerpc/platforms/pseries/papr-platform-dump.c-223-	kfree(params);
 arch/powerpc/platforms/pseries/papr-platform-dump.c:224:	file->private_data = NULL;
 arch/powerpc/platforms/pseries/papr-platform-dump.c-225-	return 0;
 --
 arch/powerpc/sysdev/fsl_pci.c=522=static int fsl_add_bridge(struct platform_device *pdev, int is_primary)
 --
 arch/powerpc/sysdev/fsl_pci.c-568-
 arch/powerpc/sysdev/fsl_pci.c:569:	pci = hose->private_data = ioremap(rsrc.start, resource_size(&rsrc));
 arch/powerpc/sysdev/fsl_pci.c-570-	if (!hose->private_data)
 --
 arch/s390/hypfs/inode.c=101=static int hypfs_open(struct inode *inode, struct file *filp)
 --
 arch/s390/hypfs/inode.c-117-		mutex_lock(&fs_info->lock);
 arch/s390/hypfs/inode.c:118:		filp->private_data = kstrdup(data, GFP_KERNEL);
 arch/s390/hypfs/inode.c-119-		if (!filp->private_data) {
 --
 arch/s390/kernel/debug.c=658=static int debug_open(struct inode *inode, struct file *file)
 --
 arch/s390/kernel/debug.c-682-	}
 arch/s390/kernel/debug.c:683:	file->private_data = p_info;
 arch/s390/kernel/debug.c-684-	nonseekable_open(inode, file);
 --
 arch/s390/kernel/debug.c=703=static int debug_close(struct inode *inode, struct file *file)
 --
 arch/s390/kernel/debug.c-708-	debug_file_private_free(p_info);
 arch/s390/kernel/debug.c:709:	file->private_data = NULL;
 arch/s390/kernel/debug.c-710-	return 0; /* success */
 --
 arch/s390/kernel/perf_cpum_cf.c=1383=static int cfset_release(struct inode *inode, struct file *file)
 --
 arch/s390/kernel/perf_cpum_cf.c-1390-		kfree(file->private_data);
 arch/s390/kernel/perf_cpum_cf.c:1391:		file->private_data = NULL;
 arch/s390/kernel/perf_cpum_cf.c-1392-	}
 --
 arch/s390/kernel/perf_cpum_cf.c=1408=static int cfset_open(struct inode *inode, struct file *file)
 --
 arch/s390/kernel/perf_cpum_cf.c-1413-		return -EPERM;
 arch/s390/kernel/perf_cpum_cf.c:1414:	file->private_data = NULL;
 arch/s390/kernel/perf_cpum_cf.c-1415-
 --
 arch/s390/kernel/perf_cpum_cf.c=1578=static long cfset_ioctl_stop(struct file *file)
 --
 arch/s390/kernel/perf_cpum_cf.c-1586-		kfree(req);
 arch/s390/kernel/perf_cpum_cf.c:1587:		file->private_data = NULL;
 arch/s390/kernel/perf_cpum_cf.c-1588-		ret = 0;
 --
 arch/s390/kernel/perf_cpum_cf.c=1593=static long cfset_ioctl_start(unsigned long arg, struct file *file)
 --
 arch/s390/kernel/perf_cpum_cf.c-1641-		cfset_session_add(preq);
 arch/s390/kernel/perf_cpum_cf.c:1642:		file->private_data = preq;
 arch/s390/kernel/perf_cpum_cf.c-1643-	} else {
 --
 arch/s390/kernel/sysinfo.c=448=static int stsi_open_##fc##_##s1##_##s2(struct inode *inode, struct file *file)\
 arch/s390/kernel/sysinfo.c-449-{									       \
 arch/s390/kernel/sysinfo.c:450:	file->private_data = (void *) get_zeroed_page(GFP_KERNEL);	       \
 arch/s390/kernel/sysinfo.c-451-	if (!file->private_data)					       \
 --
 arch/s390/kernel/sysinfo.c-454-		free_page((unsigned long)file->private_data);		       \
 arch/s390/kernel/sysinfo.c:455:		file->private_data = NULL;				       \
 arch/s390/kernel/sysinfo.c-456-		return -EACCES;						       \
 --
 arch/sparc/kernel/mdesc.c=1252=static int mdesc_open(struct inode *inode, struct file *file)
 --
 arch/sparc/kernel/mdesc.c-1258-
 arch/sparc/kernel/mdesc.c:1259:	file->private_data = hp;
 arch/sparc/kernel/mdesc.c-1260-
 --
 arch/um/drivers/hostaudio_kern.c=177=static int hostaudio_open(struct inode *inode, struct file *file)
 --
 arch/um/drivers/hostaudio_kern.c-208-	state->fd = ret;
 arch/um/drivers/hostaudio_kern.c:209:	file->private_data = state;
 arch/um/drivers/hostaudio_kern.c-210-	return 0;
 --
 arch/um/drivers/hostaudio_kern.c=240=static int hostmixer_open_mixdev(struct inode *inode, struct file *file)
 --
 arch/um/drivers/hostaudio_kern.c-273-
 arch/um/drivers/hostaudio_kern.c:274:	file->private_data = state;
 arch/um/drivers/hostaudio_kern.c-275-	return 0;
 --
 arch/um/drivers/ubd_kern.c=829=static int ubd_add(int n, char **error_out)
 --
 arch/um/drivers/ubd_kern.c-886-	sprintf(disk->disk_name, "ubd%c", 'a' + n);
 arch/um/drivers/ubd_kern.c:887:	disk->private_data = ubd_dev;
 arch/um/drivers/ubd_kern.c-888-	set_disk_ro(disk, !ubd_dev->openflags.w);
 --
 arch/x86/kernel/apm_32.c=1538=static int do_release(struct inode *inode, struct file *filp)
 --
 arch/x86/kernel/apm_32.c-1544-		return 0;
 arch/x86/kernel/apm_32.c:1545:	filp->private_data = NULL;
 arch/x86/kernel/apm_32.c-1546-	if (as->standbys_pending > 0) {
 --
 arch/x86/kernel/apm_32.c=1576=static int do_open(struct inode *inode, struct file *filp)
 --
 arch/x86/kernel/apm_32.c-1601-	spin_unlock(&user_list_lock);
 arch/x86/kernel/apm_32.c:1602:	filp->private_data = as;
 arch/x86/kernel/apm_32.c-1603-	return 0;
 --
 arch/x86/kernel/cpu/sgx/driver.c=17=static int __sgx_open(struct inode *inode, struct file *file)
 --
 arch/x86/kernel/cpu/sgx/driver.c-38-
 arch/x86/kernel/cpu/sgx/driver.c:39:	file->private_data = encl;
 arch/x86/kernel/cpu/sgx/driver.c-40-
 --
 arch/x86/kernel/cpu/sgx/driver.c=101=static int sgx_mmap(struct file *file, struct vm_area_struct *vma)
 --
 arch/x86/kernel/cpu/sgx/driver.c-115-	vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP | VM_IO);
 arch/x86/kernel/cpu/sgx/driver.c:116:	vma->vm_private_data = encl;
 arch/x86/kernel/cpu/sgx/driver.c-117-
 --
 arch/x86/kernel/cpu/sgx/encl.c=487=static void sgx_vma_open(struct vm_area_struct *vma)
 --
 arch/x86/kernel/cpu/sgx/encl.c-499-	if (sgx_encl_mm_add(encl, vma->vm_mm))
 arch/x86/kernel/cpu/sgx/encl.c:500:		vma->vm_private_data = NULL;
 arch/x86/kernel/cpu/sgx/encl.c-501-}
 --
 arch/x86/kernel/cpu/sgx/virt.c=100=static int sgx_vepc_mmap(struct file *file, struct vm_area_struct *vma)
 --
 arch/x86/kernel/cpu/sgx/virt.c-109-	vm_flags_set(vma, VM_PFNMAP | VM_IO | VM_DONTDUMP | VM_DONTCOPY);
 arch/x86/kernel/cpu/sgx/virt.c:110:	vma->vm_private_data = vepc;
 arch/x86/kernel/cpu/sgx/virt.c-111-
 --
 arch/x86/kernel/cpu/sgx/virt.c=263=static int __sgx_vepc_open(struct inode *inode, struct file *file)
 --
 arch/x86/kernel/cpu/sgx/virt.c-272-
 arch/x86/kernel/cpu/sgx/virt.c:273:	file->private_data = vepc;
 arch/x86/kernel/cpu/sgx/virt.c-274-
 --
 arch/xtensa/platforms/iss/simdisk.c=268=static int __init simdisk_setup(struct simdisk *dev, int which,
 --
 arch/xtensa/platforms/iss/simdisk.c-290-	dev->gd->fops = &simdisk_ops;
 arch/xtensa/platforms/iss/simdisk.c:291:	dev->gd->private_data = dev;
 arch/xtensa/platforms/iss/simdisk.c-292-	snprintf(dev->gd->disk_name, 32, "simdisk%d", which);
 --
 block/bdev.c=903=static inline bool bdev_unclaimed(const struct file *bdev_file)
 block/bdev.c-904-{
 block/bdev.c:905:	return bdev_file->private_data == BDEV_I(bdev_file->f_mapping->host);
 block/bdev.c-906-}
 --
 block/bdev.c=943=int bdev_open(struct block_device *bdev, blk_mode_t mode, void *holder,
 --
 block/bdev.c-1007-	bdev_file->f_wb_err = filemap_sample_wb_err(bdev_file->f_mapping);
 block/bdev.c:1008:	bdev_file->private_data = holder;
 block/bdev.c-1009-
 --
 block/bdev.c=1051=struct file *bdev_file_open_by_dev(dev_t dev, blk_mode_t mode, void *holder,
 --
 block/bdev.c-1078-		/* We failed to open the block device. Let ->release() know. */
 block/bdev.c:1079:		bdev_file->private_data = ERR_PTR(ret);
 block/bdev.c-1080-		fput(bdev_file);
 --
 block/bdev.c=1177=void bdev_fput(struct file *bdev_file)
 --
 block/bdev.c-1193-		 */
 block/bdev.c:1194:		bdev_file->private_data = BDEV_I(bdev_file->f_mapping->host);
 block/bdev.c-1195-		mutex_unlock(&disk->open_mutex);
 --
 block/fops.c=675=static int blkdev_open(struct inode *inode, struct file *filp)
 --
 block/fops.c-683-	if (mode & BLK_OPEN_EXCL)
 block/fops.c:684:		filp->private_data = filp;
 block/fops.c-685-	ret = bdev_permission(inode->i_rdev, mode, filp->private_data);
 --
 drivers/accel/amdxdna/amdxdna_gem.c=271=static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo,
 --
 drivers/accel/amdxdna/amdxdna_gem.c-298-
 drivers/accel/amdxdna/amdxdna_gem.c:299:	vma->vm_private_data = NULL;
 drivers/accel/amdxdna/amdxdna_gem.c-300-	vma->vm_ops = NULL;
 --
 drivers/accel/amdxdna/amdxdna_gem.c=354=static int amdxdna_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)
 --
 drivers/accel/amdxdna/amdxdna_gem.c-361-	vma->vm_ops = &drm_gem_shmem_vm_ops;
 drivers/accel/amdxdna/amdxdna_gem.c:362:	vma->vm_private_data = gobj;
 drivers/accel/amdxdna/amdxdna_gem.c-363-
 --
 drivers/accel/amdxdna/amdxdna_ubuf.c=90=static int amdxdna_ubuf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma)
 --
 drivers/accel/amdxdna/amdxdna_ubuf.c-94-	vma->vm_ops = &amdxdna_ubuf_vm_ops;
 drivers/accel/amdxdna/amdxdna_ubuf.c:95:	vma->vm_private_data = ubuf;
 drivers/accel/amdxdna/amdxdna_ubuf.c-96-	vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP);
 --
 drivers/accel/habanalabs/common/device.c=627=static int hl_device_release_ctrl(struct inode *inode, struct file *filp)
 --
 drivers/accel/habanalabs/common/device.c-631-
 drivers/accel/habanalabs/common/device.c:632:	filp->private_data = NULL;
 drivers/accel/habanalabs/common/device.c-633-
 --
 drivers/accel/habanalabs/common/habanalabs_drv.c=278=int hl_device_open_ctrl(struct inode *inode, struct file *filp)
 --
 drivers/accel/habanalabs/common/habanalabs_drv.c-301-	hpriv->hdev = hdev;
 drivers/accel/habanalabs/common/habanalabs_drv.c:302:	filp->private_data = hpriv;
 drivers/accel/habanalabs/common/habanalabs_drv.c-303-
 --
 drivers/accel/habanalabs/common/habanalabs_drv.c-324-	mutex_unlock(&hdev->fpriv_ctrl_list_lock);
 drivers/accel/habanalabs/common/habanalabs_drv.c:325:	filp->private_data = NULL;
 drivers/accel/habanalabs/common/habanalabs_drv.c-326-	put_pid(hpriv->taskpid);
 --
 drivers/accel/habanalabs/common/memory.c=1427=static void hw_block_vm_close(struct vm_area_struct *vma)
 --
 drivers/accel/habanalabs/common/memory.c-1444-	kfree(lnode);
 drivers/accel/habanalabs/common/memory.c:1445:	vma->vm_private_data = NULL;
 drivers/accel/habanalabs/common/memory.c-1446-}
 --
 drivers/accel/habanalabs/common/memory.c=1460=int hl_hw_block_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma)
 --
 drivers/accel/habanalabs/common/memory.c-1502-
 drivers/accel/habanalabs/common/memory.c:1503:	vma->vm_private_data = lnode;
 drivers/accel/habanalabs/common/memory.c-1504-	vma->vm_ops = &hw_block_vm_ops;
 --
 drivers/accel/habanalabs/common/memory_mgr.c=199=static void hl_mmap_mem_buf_vm_close(struct vm_area_struct *vma)
 --
 drivers/accel/habanalabs/common/memory_mgr.c-213-	hl_mmap_mem_buf_put(buf);
 drivers/accel/habanalabs/common/memory_mgr.c:214:	vma->vm_private_data = NULL;
 drivers/accel/habanalabs/common/memory_mgr.c-215-}
 --
 drivers/accel/habanalabs/common/memory_mgr.c=230=int hl_mem_mgr_mmap(struct hl_mem_mgr *mmg, struct vm_area_struct *vma,
 --
 drivers/accel/habanalabs/common/memory_mgr.c-282-
 drivers/accel/habanalabs/common/memory_mgr.c:283:	vma->vm_private_data = buf;
 drivers/accel/habanalabs/common/memory_mgr.c-284-
 --
 drivers/android/binder.c=6014=static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
 --
 drivers/android/binder.c-6034-	vma->vm_ops = &binder_vm_ops;
 drivers/android/binder.c:6035:	vma->vm_private_data = proc;
 drivers/android/binder.c-6036-
 --
 drivers/android/binder.c=6040=static int binder_open(struct inode *nodp, struct file *filp)
 --
 drivers/android/binder.c-6081-	INIT_LIST_HEAD(&proc->waiting_threads);
 drivers/android/binder.c:6082:	filp->private_data = proc;
 drivers/android/binder.c-6083-
 --
 drivers/android/binder/rust_binder_main.rs=304=unsafe impl<T> Sync for AssertSync<T> {}
 --
 drivers/android/binder/rust_binder_main.rs-384-    // SAFETY: This file is associated with Rust binder, so we own the `private_data` field.
 drivers/android/binder/rust_binder_main.rs:385:    unsafe { (*file_ptr).private_data = process.into_foreign() };
 drivers/android/binder/rust_binder_main.rs-386-    0
 --
 drivers/android/tests/binder_alloc_kunit.c=478=static int binder_alloc_test_mmap_handler(struct file *filp,
 --
 drivers/android/tests/binder_alloc_kunit.c-485-	vma->vm_ops = &binder_alloc_test_vm_ops;
 drivers/android/tests/binder_alloc_kunit.c:486:	vma->vm_private_data = alloc;
 drivers/android/tests/binder_alloc_kunit.c-487-
 --
 drivers/ata/acard-ahci.c=273=static int acard_ahci_port_start(struct ata_port *ap)
 --
 drivers/ata/acard-ahci.c-344-
 drivers/ata/acard-ahci.c:345:	ap->private_data = pp;
 drivers/ata/acard-ahci.c-346-
 --
 drivers/ata/acard-ahci.c=351=static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 --
 drivers/ata/acard-ahci.c-414-		return -ENOMEM;
 drivers/ata/acard-ahci.c:415:	host->private_data = hpriv;
 drivers/ata/acard-ahci.c-416-
 --
 drivers/ata/ahci.c=1877=static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 --
 drivers/ata/ahci.c-2062-	}
 drivers/ata/ahci.c:2063:	host->private_data = hpriv;
 drivers/ata/ahci.c-2064-
 --
 drivers/ata/ata_piix.c=1643=static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 --
 drivers/ata/ata_piix.c-1706-		return rc;
 drivers/ata/ata_piix.c:1707:	host->private_data = hpriv;
 drivers/ata/ata_piix.c-1708-
 --
 drivers/ata/libahci.c=2482=static int ahci_port_start(struct ata_port *ap)
 --
 drivers/ata/libahci.c-2571-
 drivers/ata/libahci.c:2572:	ap->private_data = pp;
 drivers/ata/libahci.c-2573-
 --
 drivers/ata/libahci_platform.c=698=int ahci_platform_init_host(struct platform_device *pdev,
 --
 drivers/ata/libahci_platform.c-717-	/* prepare host */
 drivers/ata/libahci_platform.c:718:	pi.private_data = (void *)(unsigned long)hpriv->flags;
 drivers/ata/libahci_platform.c-719-
 --
 drivers/ata/libahci_platform.c-740-
 drivers/ata/libahci_platform.c:741:	host->private_data = hpriv;
]
]

30/2 2026/02/04 14:19 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 42619
							output: 22
							thoughts: 26
						

Thoughts:
Okay, here's my interpretation of those thoughts, crafted with the understanding that I'm speaking to a fellow expert:

**Refining the Search Strategy**

Alright, that initial `grep` for `private_data =` feels a bit too broad, doesn't it? We're going to get swamped with false positives otherwise. I need to narrow the scope. Thinking about it, the specific area I'm most interested in is the `net/smc/` directory. That's where I need to focus my attention for now. Let me see if I can find what I'm looking for by searching in that specific location. I'll focus my efforts there, which should yield more relevant results. Let's see what we can find.



31/2 2026/02/04 14:19 tool grepper
0m Args:
map[Expression:private_data =.*clcsock]

Results:
map[Output:net/smc/af_smc.c=917=static int smc_switch_to_fallback(struct smc_sock *smc, int reason_code)
--
net/smc/af_smc.c-932-		smc->clcsock->file = smc->sk.sk_socket->file;
net/smc/af_smc.c:933:		smc->clcsock->file->private_data = smc->clcsock;
net/smc/af_smc.c-934-		smc->clcsock->wq.fasync_list =
--
net/smc/af_smc.c=1813=struct sock *smc_accept_dequeue(struct sock *parent,
--
net/smc/af_smc.c-1836-				smc_sk(new_sk)->clcsock->file = new_sock->file;
net/smc/af_smc.c:1837:				isk->clcsock->file->private_data = isk->clcsock;
net/smc/af_smc.c-1838-			}
]

32/2 2026/02/04 14:19 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 43126
							output: 37
							thoughts: 2321
						

Thoughts:
**Analyzing a Potential Race Condition in the SMC Protocol**

Alright, so I'm looking into this KCSAN report flagged around `smc_switch_to_fallback` and `smc_accept_dequeue`, both of which seem to interact with socket polling. I recall seeing a related commit, `b6059638367a`, that might be relevant, so I'll check if it's present and what it addressed. Without a direct commit tool, I'll examine the current code.

The code snippet flagged by KCSAN shows:
```c
 932:		smc->clcsock->file = smc->sk.sk_socket->file;
 933:		smc->clcsock->file->private_data = smc->clcsock;
```
This looks like the root cause of the KCSAN report.  If there was a fix, it might involve `WRITE_ONCE`, but it's not present here.

Now, I'm reviewing `sock_poll` in `net/socket.c`:
```c
1427:		struct socket *sock = file->private_data;
```
Given the nature of SMC's protocol-specific interaction, it's very important to note that generic code should not need to be altered because of it, unless the modifications are permitted. In this case, altering `file->private_data` is generally a no-no, as it often leads to these race conditions.

If SMC is changing `file->private_data`, it needs to ensure it's either done atomically or accept the KCSAN flags.

Is this actually harmful?  Logically, the delegation in `smc_poll` seems safe, but I need to consider a few things.

Here's the scenario: if `sock_poll` reads `file->private_data` and gets the CLC socket and calls `tcp_poll(file, clcsock, wait)` then, `sock_poll_wait` is reached by `poll_wait(file, &clcsock->wq.wait, wait)`. This is fine. If `sock_poll` reads `file->private_data` and gets the SMC socket and calls `smc_poll(file, smc_sock, wait)` then, `sock_poll_wait` is reached by `poll_wait(file, &clcsock->wq.wait, wait)`. This also seems fine.

The potential issue is:
```c
1427:		struct socket *sock = file->private_data; // reads SMC
1428:		const struct proto_ops *ops = READ_ONCE(sock->ops); // reads smc_sock_ops
...
1443:		return ops->poll(file, sock, wait) | flag; // reloads sock, gets CLC
```
If the compiler reloads `sock` between lines 1427 and 1443, it could call `smc_sock_ops.poll(file, CLC_socket, wait)`. `smc_sock_ops.poll` is `smc_poll`, which eventually uses `smc_sk(sock->sk)`. But now `sock` is the CLC socket. Casting a `tcp_sock` (from the CLC socket) to `smc_sock` is a type confusion, and this would result in an access to arbitrary memory inside the `tcp_sock` or beyond. This is definitely a HARMFUL race.

Therefore, the race is harmful because the compiler *could* reload `file->private_data`, leading to type confusion, and this isn't guarded against.

Ideally, SMC shouldn't be changing `file->private_data` at all while the socket is active. If it has to, then `READ_ONCE` and `WRITE_ONCE` must be used. But since `sock_poll` is generic, we can't change it.

The best solution is for SMC to keep `file->private_data` pointing to the SMC socket all the time and delegate to the CLC socket. This would eliminate the race and the performance penalty of an extra function call is acceptable.

I need to make sure that the `file->private_data` is only a problem in `sock_poll`.  I'll look at the other socket syscalls. Almost every single socket syscall might have this issue.

For example, with `sendmsg`, if `sockfd_lookup_light` returns a socket, which is then stored in a local variable, and `sock_sendmsg` is inlined, then reloading the `file` could cause an issue.




33/2 2026/02/04 14:20 tool codesearch-definition-source
0m Args:
map[ContextFile:net/socket.c IncludeLines:true Name:sock_poll]

Results:
map[SourceCode:1424:	/* No kernel lock held - perfect */
1425:	static __poll_t sock_poll(struct file *file, poll_table *wait)
1426:	{
1427:		struct socket *sock = file->private_data;
1428:		const struct proto_ops *ops = READ_ONCE(sock->ops);
1429:		__poll_t events = poll_requested_events(wait), flag = 0;
1430:	
1431:		if (!ops->poll)
1432:			return 0;
1433:	
1434:		if (sk_can_busy_loop(sock->sk)) {
1435:			/* poll once if requested by the syscall */
1436:			if (events & POLL_BUSY_LOOP)
1437:				sk_busy_loop(sock->sk, 1);
1438:	
1439:			/* if this socket can poll_ll, tell the system call */
1440:			flag = POLL_BUSY_LOOP;
1441:		}
1442:	
1443:		return ops->poll(file, sock, wait) | flag;
1444:	}
 SourceFile:net/socket.c]

34/2 2026/02/04 14:20 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 46856
							output: 39
							thoughts: 2573
						

Thoughts:
**Analysis of a Potential Data Race in Linux Kernel SMC Implementation**

Okay, so I've been tracing through this code, and I've come across something potentially problematic in the SMC (Shared Memory Communications) code within the Linux kernel. Specifically, I'm focusing on how `file->private_data` is being handled. I see `READ_ONCE(sock->ops)` which is good, but I'm worried about `file->private_data`. It seems like there's a window where this pointer, representing the socket, could change between reads, potentially leading to a race condition.

My initial thought is that the compiler could optimize and keep a pointer to the socket in a register. If it *doesn't* reload it from `file->private_data` every time it's used, that's fine. However, if the compiler spills it to the stack or, worse, reloads from `file->private_data`, things get dicey. The code uses `sock` in several places, and if the socket pointer changes mid-execution due to the race, we could have a problem, potentially even a crash, because the old `ops` pointer is associated with the old socket data and the new one might be wildly different.

I've got a scenario where `smc_switch_to_fallback` is called, and this function *does* change `file->private_data`. The old value is an SMC socket, the new value is a CLC socket. If another thread is in the middle of a `sock_poll` operation on the *same* file descriptor, it might see the *new* value of `file->private_data` and call `tcp_poll` on what it *thinks* is a TCP socket, but is actually the CLC socket. This could lead to a crash because the `file` object is likely used in `tcp_poll`. And indeed, there's a race with `smc->clcsock->file`.

I found a similar issue discussed publicly a while back – a KCSAN report. The consensus was that it was a real race and needed fixing. That strengthens my suspicion.

Let me analyze how that crash might occur. I see that `smc_poll` is using a cast from `sock->sk` to an `smc_sock`. If the `use_fallback` flag is set (which it might be due to the switch), it then attempts to use `smc->clcsock->ops->poll`. But if, due to the race, the compiler reloads the `sock` pointer, and that `sock` is actually a `tcp_sock` (because the `file->private_data` has been changed by the race), the code will dereference a garbage pointer, leading to a crash.

My next line of investigation is about what could be making the compiler reload. KCSAN and KASAN enabled, and debugging kernels, can increase register pressure and make reloads more likely. But even in a production kernel, the race violates the memory model!

I need to understand *why* `smc_switch_to_fallback` changes `file->private_data`. It seems that it's for performance reasons - an optimization. But if an optimization causes a race, and that race can lead to a crash, that's a *bug*.

I must think whether the race is somehow impossible. Does another lock exist? No, the code does not. The `smc_switch_to_fallback` function holds `smc->clcsock_release_lock` but the code accessing this other socket is not locked in this way.

I'm checking if the change of `file->private_data` is safe.

I've looked at other uses of `file->private_data` via other functions such as `sock_ioctl`, and `sock_mmap`. They all read the same value so they are all safe, as long as the compiler does not reload `file->private_data`. Thus, the compiler's reloading of the value is the only risk.

Based on this, I have to conclude that this race is real and potentially harmful, given that it could lead to type confusion and crashes. While some might argue that the compiler is unlikely to reload the pointer in such a short function, I'd say "never" is a strong word, and we must assume the compiler *can* reload unless we protect with `READ_ONCE`.

I found that the kernel commit *does* still have that line of code that sets `file->private_data`. There is still no `WRITE_ONCE` or `READ_ONCE`.

Why is there a `READ_ONCE(sock->ops)` but not `READ_ONCE(sock)`? `sock->ops` can change. But `sock


35/2 2026/02/04 14:20 tool codesearch-struct-layout
0m Args:
map[ContextFile:net/smc/af_smc.c Name:smc_sock]

Results:
map[Fields:[map[Name: OffsetBits:0 SizeBits:8384] map[Name:clcsock OffsetBits:8384 SizeBits:64] map[Name:clcsk_state_change OffsetBits:8448 SizeBits:64] map[Name:clcsk_data_ready OffsetBits:8512 SizeBits:64] map[Name:clcsk_write_space OffsetBits:8576 SizeBits:64] map[Name:clcsk_error_report OffsetBits:8640 SizeBits:64] map[Name:conn OffsetBits:8704 SizeBits:4096] map[Name:listen_smc OffsetBits:12800 SizeBits:64] map[Name:connect_work OffsetBits:12864 SizeBits:256] map[Name:tcp_listen_work OffsetBits:13120 SizeBits:256] map[Name:smc_listen_work OffsetBits:13376 SizeBits:256] map[Name:accept_q OffsetBits:13632 SizeBits:128] map[Name:accept_q_lock OffsetBits:13760 SizeBits:32] map[Name:limit_smc_hs OffsetBits:13792 SizeBits:8] map[Name:use_fallback OffsetBits:13800 SizeBits:8] map[Name:fallback_rsn OffsetBits:13824 SizeBits:32] map[Name:peer_diagnosis OffsetBits:13856 SizeBits:32] map[Name:queued_smc_hs OffsetBits:13888 SizeBits:32] map[Name:af_ops OffsetBits:13952 SizeBits:640] map[Name:ori_af_ops OffsetBits:14592 SizeBits:64] map[Name:sockopt_defer_accept OffsetBits:14656 SizeBits:32] map[Name:wait_close_tx_prepared OffsetBits:14688 SizeBits:1] map[Name:connect_nonblock OffsetBits:14689 SizeBits:1] map[Name:clcsock_release_lock OffsetBits:14720 SizeBits:256]]]

36/2 2026/02/04 14:20 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 51095
							output: 35
							thoughts: 118
						

Thoughts:
**Analyzing the `use_fallback` Flag Within a TCP Socket**

Okay, so I'm looking at this `use_fallback` flag, and the information I have says it's at offset 13800 bits, which is the same as 1725 bytes. That gives me a starting point. Now, the question is, what does that offset correspond to within a `tcp_sock` structure?

I know that `tcp_sock` is complex. It's built on top of a `struct sock`, which is already a significant chunk – probably around 700 to 800 bytes, give or take. Then there are the layers on top of that: `struct inet_sock`, `struct inet_connection_sock`, and finally `struct tcp_sock` itself. Each of these adds more to the overall size. So I'll need to know the layout, specifically of `tcp_sock`, to understand where 1725 bytes lands in relation to that base structure. Time to delve into the `tcp_sock` layout to map this out.



37/2 2026/02/04 14:20 tool codesearch-struct-layout
0m Args:
map[ContextFile:net/ipv4/tcp.c Name:tcp_sock]

Results:
map[Fields:[map[Name:inet_conn OffsetBits:0 SizeBits:11456] map[Name:__cacheline_group_begin__tcp_sock_read_tx OffsetBits:11456 SizeBits:0] map[Name:max_window OffsetBits:11456 SizeBits:32] map[Name:rcv_ssthresh OffsetBits:11488 SizeBits:32] map[Name:reordering OffsetBits:11520 SizeBits:32] map[Name:notsent_lowat OffsetBits:11552 SizeBits:32] map[Name:gso_segs OffsetBits:11584 SizeBits:16] map[Name:retransmit_skb_hint OffsetBits:11648 SizeBits:64] map[Name:tcp_clean_acked OffsetBits:11712 SizeBits:64] map[Name:__cacheline_group_end__tcp_sock_read_tx OffsetBits:11776 SizeBits:0] map[Name:__cacheline_group_begin__tcp_sock_read_txrx OffsetBits:11776 SizeBits:0] map[Name:tsoffset OffsetBits:11776 SizeBits:32] map[Name:snd_wnd OffsetBits:11808 SizeBits:32] map[Name:mss_cache OffsetBits:11840 SizeBits:32] map[Name:snd_cwnd OffsetBits:11872 SizeBits:32] map[Name:prr_out OffsetBits:11904 SizeBits:32] map[Name:lost_out OffsetBits:11936 SizeBits:32] map[Name:sacked_out OffsetBits:11968 SizeBits:32] map[Name:tcp_header_len OffsetBits:12000 SizeBits:16] map[Name:scaling_ratio OffsetBits:12016 SizeBits:8] map[Name:chrono_type OffsetBits:12024 SizeBits:2] map[Name:repair OffsetBits:12026 SizeBits:1] map[Name:tcp_usec_ts OffsetBits:12027 SizeBits:1] map[Name:is_sack_reneg OffsetBits:12028 SizeBits:1] map[Name:is_cwnd_limited OffsetBits:12029 SizeBits:1] map[Name:recvmsg_inq OffsetBits:12030 SizeBits:1] map[Name:__cacheline_group_end__tcp_sock_read_txrx OffsetBits:12032 SizeBits:0] map[Name:__cacheline_group_begin__tcp_sock_read_rx OffsetBits:12032 SizeBits:0] map[Name:copied_seq OffsetBits:12032 SizeBits:32] map[Name:snd_wl1 OffsetBits:12064 SizeBits:32] map[Name:tlp_high_seq OffsetBits:12096 SizeBits:32] map[Name:rttvar_us OffsetBits:12128 SizeBits:32] map[Name:retrans_out OffsetBits:12160 SizeBits:32] map[Name:advmss OffsetBits:12192 SizeBits:16] map[Name:urg_data OffsetBits:12208 SizeBits:16] map[Name:lost OffsetBits:12224 SizeBits:32] map[Name:snd_ssthresh OffsetBits:12256 SizeBits:32] map[Name:rtt_min OffsetBits:12288 SizeBits:192] map[Name:out_of_order_queue OffsetBits:12480 SizeBits:64] map[Name:__cacheline_group_end__tcp_sock_read_rx OffsetBits:12544 SizeBits:0] map[Name:__cacheline_group_begin__tcp_sock_write_tx OffsetBits:12800 SizeBits:0] map[Name:segs_out OffsetBits:12800 SizeBits:32] map[Name:data_segs_out OffsetBits:12832 SizeBits:32] map[Name:bytes_sent OffsetBits:12864 SizeBits:64] map[Name:snd_sml OffsetBits:12928 SizeBits:32] map[Name:chrono_start OffsetBits:12960 SizeBits:32] map[Name:chrono_stat OffsetBits:12992 SizeBits:96] map[Name:write_seq OffsetBits:13088 SizeBits:32] map[Name:pushed_seq OffsetBits:13120 SizeBits:32] map[Name:lsndtime OffsetBits:13152 SizeBits:32] map[Name:mdev_us OffsetBits:13184 SizeBits:32] map[Name:rtt_seq OffsetBits:13216 SizeBits:32] map[Name:tcp_wstamp_ns OffsetBits:13248 SizeBits:64] map[Name:accecn_opt_tstamp OffsetBits:13312 SizeBits:64] map[Name:tsorted_sent_queue OffsetBits:13376 SizeBits:128] map[Name:highest_sack OffsetBits:13504 SizeBits:64] map[Name:ecn_flags OffsetBits:13568 SizeBits:8] map[Name:__cacheline_group_end__tcp_sock_write_tx OffsetBits:13576 SizeBits:0] map[Name:__cacheline_group_begin__tcp_sock_write_txrx OffsetBits:13576 SizeBits:0] map[Name:nonagle OffsetBits:13576 SizeBits:4] map[Name:rate_app_limited OffsetBits:13580 SizeBits:1] map[Name:received_ce_pending OffsetBits:13584 SizeBits:4] map[Name:unused2 OffsetBits:13588 SizeBits:4] map[Name:accecn_minlen OffsetBits:13592 SizeBits:2] map[Name:est_ecnfield OffsetBits:13594 SizeBits:2] map[Name:accecn_opt_demand OffsetBits:13596 SizeBits:2] map[Name:prev_ecnfield OffsetBits:13598 SizeBits:2] map[Name:pred_flags OffsetBits:13600 SizeBits:32] map[Name:tcp_clock_cache OffsetBits:13632 SizeBits:64] map[Name:tcp_mstamp OffsetBits:13696 SizeBits:64] map[Name:rcv_nxt OffsetBits:13760 SizeBits:32] map[Name:snd_nxt OffsetBits:13792 SizeBits:32] map[Name:snd_una OffsetBits:13824 SizeBits:32] map[Name:window_clamp OffsetBits:13856 SizeBits:32] map[Name:srtt_us OffsetBits:13888 SizeBits:32] map[Name:packets_out OffsetBits:13920 SizeBits:32] map[Name:snd_up OffsetBits:13952 SizeBits:32] map[Name:delivered OffsetBits:13984 SizeBits:32] map[Name:delivered_ce OffsetBits:14016 SizeBits:32] map[Name:received_ce OffsetBits:14048 SizeBits:32] map[Name:received_ecn_bytes OffsetBits:14080 SizeBits:96] map[Name:app_limited OffsetBits:14176 SizeBits:32] map[Name:rcv_wnd OffsetBits:14208 SizeBits:32] map[Name:rcv_tstamp OffsetBits:14240 SizeBits:32] map[Name:rx_opt OffsetBits:14272 SizeBits:192] map[Name:__cacheline_group_end__tcp_sock_write_txrx OffsetBits:14464 SizeBits:0] map[Name:__cacheline_group_begin__tcp_sock_write_rx OffsetBits:14464 SizeBits:0] map[Name:bytes_received OffsetBits:14464 SizeBits:64] map[Name:segs_in OffsetBits:14528 SizeBits:32] map[Name:data_segs_in OffsetBits:14560 SizeBits:32] map[Name:rcv_wup OffsetBits:14592 SizeBits:32] map[Name:max_packets_out OffsetBits:14624 SizeBits:32] map[Name:cwnd_usage_seq OffsetBits:14656 SizeBits:32] map[Name:rate_delivered OffsetBits:14688 SizeBits:32] map[Name:rate_interval_us OffsetBits:14720 SizeBits:32] map[Name:rcv_rtt_last_tsecr OffsetBits:14752 SizeBits:32] map[Name:delivered_ecn_bytes OffsetBits:14784 SizeBits:96] map[Name:first_tx_mstamp OffsetBits:14912 SizeBits:64] map[Name:delivered_mstamp OffsetBits:14976 SizeBits:64] map[Name:bytes_acked OffsetBits:15040 SizeBits:64] map[Name:rcv_rtt_est OffsetBits:15104 SizeBits:128] map[Name:rcvq_space OffsetBits:15232 SizeBits:128] map[Name:__cacheline_group_end__tcp_sock_write_rx OffsetBits:15360 SizeBits:0] map[Name:dsack_dups OffsetBits:15360 SizeBits:32] map[Name:compressed_ack_rcv_nxt OffsetBits:15392 SizeBits:32] map[Name:tsq_node OffsetBits:15424 SizeBits:128] map[Name:rack OffsetBits:15552 SizeBits:192] map[Name:compressed_ack OffsetBits:15744 SizeBits:8] map[Name:dup_ack_counter OffsetBits:15752 SizeBits:2] map[Name:tlp_retrans OffsetBits:15754 SizeBits:1] map[Name:syn_ect_snt OffsetBits:15755 SizeBits:2] map[Name:syn_ect_rcv OffsetBits:15757 SizeBits:2] map[Name:thin_lto OffsetBits:15759 SizeBits:1] map[Name:fastopen_connect OffsetBits:15760 SizeBits:1] map[Name:fastopen_no_cookie OffsetBits:15761 SizeBits:1] map[Name:fastopen_client_fail OffsetBits:15762 SizeBits:2] map[Name:frto OffsetBits:15764 SizeBits:1] map[Name:repair_queue OffsetBits:15768 SizeBits:8] map[Name:save_syn OffsetBits:15776 SizeBits:2] map[Name:syn_data OffsetBits:15778 SizeBits:1] map[Name:syn_fastopen OffsetBits:15779 SizeBits:1] map[Name:syn_fastopen_exp OffsetBits:15780 SizeBits:1] map[Name:syn_fastopen_ch OffsetBits:15781 SizeBits:1] map[Name:syn_data_acked OffsetBits:15782 SizeBits:1] map[Name:syn_fastopen_child OffsetBits:15783 SizeBits:1] map[Name:keepalive_probes OffsetBits:15784 SizeBits:8] map[Name:accecn_fail_mode OffsetBits:15792 SizeBits:4] map[Name:saw_accecn_opt OffsetBits:15796 SizeBits:2] map[Name:tcp_tx_delay OffsetBits:15808 SizeBits:32] map[Name:mdev_max_us OffsetBits:15840 SizeBits:32] map[Name:reord_seen OffsetBits:15872 SizeBits:32] map[Name:snd_cwnd_cnt OffsetBits:15904 SizeBits:32] map[Name:snd_cwnd_clamp OffsetBits:15936 SizeBits:32] map[Name:snd_cwnd_used OffsetBits:15968 SizeBits:32] map[Name:snd_cwnd_stamp OffsetBits:16000 SizeBits:32] map[Name:prior_cwnd OffsetBits:16032 SizeBits:32] map[Name:prr_delivered OffsetBits:16064 SizeBits:32] map[Name:last_oow_ack_time OffsetBits:16096 SizeBits:32] map[Name:pacing_timer OffsetBits:16128 SizeBits:512] map[Name:compressed_ack_timer OffsetBits:16640 SizeBits:512] map[Name:ooo_last_skb OffsetBits:17152 SizeBits:64] map[Name:duplicate_sack OffsetBits:17216 SizeBits:64] map[Name:selective_acks OffsetBits:17280 SizeBits:256] map[Name:recv_sack_cache OffsetBits:17536 SizeBits:256] map[Name:prior_ssthresh OffsetBits:17792 SizeBits:32] map[Name:high_seq OffsetBits:17824 SizeBits:32] map[Name:retrans_stamp OffsetBits:17856 SizeBits:32] map[Name:undo_marker OffsetBits:17888 SizeBits:32] map[Name:undo_retrans OffsetBits:17920 SizeBits:32] map[Name:mtu_info OffsetBits:17952 SizeBits:32] map[Name:bytes_retrans OffsetBits:17984 SizeBits:64] map[Name:total_retrans OffsetBits:18048 SizeBits:32] map[Name:rto_stamp OffsetBits:18080 SizeBits:32] map[Name:total_rto OffsetBits:18112 SizeBits:16] map[Name:total_rto_recoveries OffsetBits:18128 SizeBits:16] map[Name:total_rto_time OffsetBits:18144 SizeBits:32] map[Name:urg_seq OffsetBits:18176 SizeBits:32] map[Name:keepalive_time OffsetBits:18208 SizeBits:32] map[Name:keepalive_intvl OffsetBits:18240 SizeBits:32] map[Name:linger2 OffsetBits:18272 SizeBits:32] map[Name:bpf_sock_ops_cb_flags OffsetBits:18304 SizeBits:8] map[Name:bpf_chg_cc_inprogress OffsetBits:18312 SizeBits:1] map[Name:timeout_rehash OffsetBits:18320 SizeBits:16] map[Name:rcv_ooopack OffsetBits:18336 SizeBits:32] map[Name:mtu_probe OffsetBits:18368 SizeBits:64] map[Name:plb_rehash OffsetBits:18432 SizeBits:32] map[Name:is_mptcp OffsetBits:18464 SizeBits:8] map[Name:syn_smc OffsetBits:18472 SizeBits:8] map[Name:smc_hs_congested OffsetBits:18496 SizeBits:64] map[Name:af_specific OffsetBits:18560 SizeBits:64] map[Name:md5sig_info OffsetBits:18624 SizeBits:64] map[Name:fastopen_req OffsetBits:18688 SizeBits:64] map[Name:fastopen_rsk OffsetBits:18752 SizeBits:64] map[Name:saved_syn OffsetBits:18816 SizeBits:64]]]

38/2 2026/02/04 14:20 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 55833
							output: 19
							thoughts: 2331
						

Thoughts:
**Harmful Data Race in SMC Protocol: Analysis and Conclusion**

Okay, let's break this down. I've been looking at this KCSAN report and need to solidify my thoughts.

The core of the problem is this: there's a data race involving `file->private_data` within the SMC protocol, specifically between `smc_switch_to_fallback` (the writer) and `sock_poll` (the reader). My initial thought was, let's dissect where in `tcp_sock` this is happening, the memory offsets and bit counts were a starting point. It appears the relevant location within `tcp_sock` happens just before `snd_una`, overlapping with the `snd_nxt` member. This is a critical detail.

`smc_switch_to_fallback` updates `file->private_data` to point to a TCP socket when an SMC connection fails, to allow a switch to TCP. `sock_poll` reads that same pointer. If the compiler gets "clever" and reloads the value of `file->private_data` from memory between the assignment in `smc_switch_to_fallback` and the read in `sock_poll`, we get a type confusion. It's essentially reading a TCP socket's data and interpreting it as an SMC socket. Specifically, it can lead to `smc_poll` being called with a TCP socket as its input. That's *bad*. The offsets point to some memory within `inet_connection_sock`, and the subsequent attempt to dereference `clcsock->ops->poll` will almost certainly crash, as it is pointing to a TCP socket's struct.

I'm now thinking about the compiler behavior. The C standard *allows* the compiler to assume that non-volatile memory doesn't change unless there's an explicit side effect (i.e., sequence point). `sock_poll` doesn't make any calls that would invalidate this assumption, thus the compiler *might* not reload `file->private_data` at all. The compiler might try to optimize and re-use the register instead, assuming the value doesn't change. That might make the race benign, right? But the compiler might also reload it if it runs out of registers. Also, even if the compiler *doesn't* reload, the race is still present in the protocol's logic and the bug is present regardless. The race *must* be solved.

There was a fix! I need to see what the fix was about! I was looking for `WRITE_ONCE`. If `WRITE_ONCE` was present then the compiler would have been forced to reload the variable. It would look like `WRITE_ONCE(smc->clcsock->file->private_data, smc->clcsock);`. Unfortunately, I don't see any sign of the fix, so it's *still* present in the current code I'm analyzing. The fix was NOT applied.

It's definitely not benign. It *will* corrupt, and *could* crash.

I now remember that `sock_poll` is also used by `io_uring`. Meaning this race could be happening a lot.

The report highlights that the race happens at `smc_switch_to_fallback+0x4ea` writing and `sock_poll+0x27` reading. This is consistent with what I'm thinking. I'm now certain of the harmfulness of this race.

Now, let's consider the synchronization. `smc_sendmsg` uses `lock_sock`, but `sock_poll` does *not*. This difference is the problem.

I need to make sure this is actually a "harmful" race as defined by the prompt. I need to make sure I understand "benign" vs "harmful". The prompt specifically excludes flags and counters. This is a pointer to a core object and type confusion is possible.

I also need to make sure this is the *only* problem, and there are no other places where the race manifests.

I need to quickly analyze if `smc_switch_to_fallback` is called in the `connect` process. It is. This mean we can trigger the race with `poll` and `connect` happening concurrently.

Also, it looks like there's a race condition on `smc->use_fallback` as well. That is set to true in `smc_switch_to_fallback` as well, but can be checked in `smc_poll`. This means even before we get to the potential type confusion, we could be making wrong decision about which code path to take. Because `smc->conn` may be uninitialized, the problem could manifest at different points in the code.

The SMC devs do something non-


39/2 2026/02/04 14:21 tool set-results
0m Args:
map[Benign:false Confident:true]

Results:
map[Benign:false Confident:true]

40/2 2026/02/04 14:21 llm expert
0m Model:
gemini-3-flash-preview

Tokens:
							input: 59225
							output: 638
							thoughts: 269
						

Thoughts:
**My Analysis of the Data Race**

Okay, the results are in. Now comes the part I really enjoy – crafting the explanation. Here's how I see this data race unfolding:

First, the core issue revolves around `file->private_data`. It’s the battleground. I’ve pinpointed the conflict between `smc_switch_to_fallback()` and `sock_poll()`.

Essentially, `smc_switch_to_fallback()` has the job of swapping `file->private_data` from an SMC socket to a more fundamental TCP (CLC) socket when the transition to the fallback state happens. Meanwhile, `sock_poll()` is constantly checking that very same `file->private_data` to figure out which socket and its associated `proto_ops` to use.

The problem, as I see it, is the timing and the potential for compiler optimizations. If the compiler gets too clever and reloads `file->private_data` in the middle of `sock_poll()`, specifically between grabbing the `ops` pointer and actually calling `ops->poll()`, the race is on. We could end up calling a poll function tailored for SMC sockets, but on a TCP socket! That’s a recipe for type confusion and, inevitably, memory corruption.

But the race isn't limited to reloads. The concurrent access to `file->private_data`, coupled with that `smc->use_fallback` flag, could easily create inconsistent views of the socket state during this fallback transition. We're talking about a situation where one part of the code thinks it's dealing with SMC, and another part thinks TCP.

The key take-away is this: the whole notion of changing `file->private_data` mid-lifetime of a `file` object is just…weird. It breaks fundamental assumptions in the kernel, and frankly, I'm surprised it ever made it this far. It's inherently risky, and it really, *really* needs proper synchronization. As I see it, that’s the missing piece.

Finally, the fact that kernel developers have already identified this as a real issue strengthens my conclusions. I'm glad they've caught this, because it's a critical one.



LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)