net: Drop the lock in skb_may_tx_timestamp() [ Upstream commit 983512f3a87fd8dc4c94dfa6b596b6e57df5aad7 ] skb_may_tx_timestamp() may acquire sock::sk_callback_lock. The lock must not be taken in IRQ context, only softirq is okay. A few drivers receive the timestamp via a dedicated interrupt and complete the TX timestamp from that handler. This will lead to a deadlock if the lock is already write-locked on the same CPU. Taking the lock can be avoided. The socket (pointed by the skb) will remain valid until the skb is released. The ->sk_socket and ->file member will be set to NULL once the user closes the socket which may happen before the timestamp arrives. If we happen to observe the pointer while the socket is closing but before the pointer is set to NULL then we may use it because both pointer (and the file's cred member) are RCU freed. Drop the lock. Use READ_ONCE() to obtain the individual pointer. Add a matching WRITE_ONCE() where the pointer are cleared. BUG=b/510709973 TEST=presubmit RELEASE_NOTE=Fixed CVE-2026-43216 in the Linux kernel. cos-patch: security-moderate Link: https://lore.kernel.org/all/20260205145104.iWinkXHv@linutronix.de Fixes: b245be1f4db1a ("net-timestamp: no-payload only sysctl") Change-Id: I587f7d3562942b1f084ab160e243eb11b85f243e Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Reviewed-by: Willem de Bruijn <willemb@google.com> Reviewed-by: Jason Xing <kerneljasonxing@gmail.com> Reviewed-by: Eric Dumazet <edumazet@google.com> Link: https://patch.msgid.link/20260220183858.N4ERjFW6@linutronix.de Signed-off-by: Paolo Abeni <pabeni@redhat.com> [adapted sk_set_socket() in include/net/sock.h to fix the conflict from not having commit 5d6b58c932ec ("net: lockless sock_i_ino()") and the additional previous changes required by it. It comes down to just now having the lines of if (sock) { WRITE_ONCE(sk->sk_uid, SOCK_INODE(sock)->i_uid); WRITE_ONCE(sk->sk_ino, SOCK_INODE(sock)->i_ino); } below the changed line. I've tested this on a device running an nfs-root and did some additional network stress-testing.] Signed-off-by: Heiko Stuebner <heiko.stuebner@cherry.de> Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Kernel CVE Triage Automation <cloud-image-kernel-cve-triage-automation@prod.google.com> Reviewed-on: https://cos-review.googlesource.com/c/third_party/kernel/+/169324 Reviewed-by: Robert Kolchmeyer <rkolchmeyer@google.com> Reviewed-by: Miri Amarilio <mirilio@google.com> Tested-by: Cusky Presubmit Bot <presubmit@cos-infra-prod.iam.gserviceaccount.com>
diff --git a/include/net/sock.h b/include/net/sock.h index 1bc8109..a460885 100644 --- a/include/net/sock.h +++ b/include/net/sock.h
@@ -2041,7 +2041,7 @@ static inline int sk_rx_queue_get(const struct sock *sk) static inline void sk_set_socket(struct sock *sk, struct socket *sock) { - sk->sk_socket = sock; + WRITE_ONCE(sk->sk_socket, sock); } static inline wait_queue_head_t *sk_sleep(struct sock *sk)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 834dbaf..a768d4f 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c
@@ -5530,15 +5530,28 @@ static void __skb_complete_tx_timestamp(struct sk_buff *skb, static bool skb_may_tx_timestamp(struct sock *sk, bool tsonly) { - bool ret; + struct socket *sock; + struct file *file; + bool ret = false; if (likely(READ_ONCE(sysctl_tstamp_allow_data) || tsonly)) return true; - read_lock_bh(&sk->sk_callback_lock); - ret = sk->sk_socket && sk->sk_socket->file && - file_ns_capable(sk->sk_socket->file, &init_user_ns, CAP_NET_RAW); - read_unlock_bh(&sk->sk_callback_lock); + /* The sk pointer remains valid as long as the skb is. The sk_socket and + * file pointer may become NULL if the socket is closed. Both structures + * (including file->cred) are RCU freed which means they can be accessed + * within a RCU read section. + */ + rcu_read_lock(); + sock = READ_ONCE(sk->sk_socket); + if (!sock) + goto out; + file = READ_ONCE(sock->file); + if (!file) + goto out; + ret = file_ns_capable(file, &init_user_ns, CAP_NET_RAW); +out: + rcu_read_unlock(); return ret; }
diff --git a/net/socket.c b/net/socket.c index 5c5dd9f..723bc3a 100644 --- a/net/socket.c +++ b/net/socket.c
@@ -652,7 +652,7 @@ static void __sock_release(struct socket *sock, struct inode *inode) iput(SOCK_INODE(sock)); return; } - sock->file = NULL; + WRITE_ONCE(sock->file, NULL); } /**