Thread (15 messages) 15 messages, 3 authors, 6d ago

[RFC PATCH 04/41] net: sock: read sk_uid through a seqcount

flat view
COOLING6d

From: Jan Koester <hidden>
Date: 2026-10-18 18:01:49
Also in: keyrings, linux-api, linux-arch, linux-cifs, linux-ext4, linux-fsdevel, linux-nfs, linux-security-module, lkml
Subsystem: networking [general], networking [sockets], the rest · Maintainers: "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Kuniyuki Iwashima, Willem de Bruijn, Linus Torvalds

sk_uid is read without a lock, with READ_ONCE(), by the routing lookups
and the reuseport checks, while sockfs_setattr() may change it. That
only works for as long as a kuid_t is a single word, which it won't be
once ids can be UUIDs and GUIDs.

Add sk_uid_seq next to it: writers bump it around the store and hold
sk_callback_lock (sock_graft(), sockfs_setattr()) or own the socket
alone (sock_init_data_uid()); sk_uid() retries until it sees a whole
id. sk_clone() reads the parent's id the same way rather than relying
on the copy made by sock_copy().

Assisted-by: Claude:claude-opus-5-5

Signed-off-by: Jan Koester <redacted>
---
 include/net/sock.h | 28 +++++++++++++++++++++++++---
 net/core/sock.c    |  6 +++++-
 net/socket.c       |  5 +++--
 3 files changed, 33 insertions(+), 6 deletions(-)
diff --git a/include/net/sock.h b/include/net/sock.h
index 60ea55dc18..0986d94665 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -294,6 +294,8 @@ struct sk_filter;
   *	@sk_ack_backlog: current listen backlog
   *	@sk_max_ack_backlog: listen backlog set in listen()
   *	@sk_uid: user id of owner
+  *	@sk_uid_seq: makes lockless reads of @sk_uid see a whole id; writers
+  *		     hold @sk_callback_lock
   *	@sk_ino: inode number (zero if orphaned)
   *	@sk_prefer_busy_poll: prefer busypolling over softirq processing
   *	@sk_busy_poll_budget: napi processing budget when busypolling
@@ -509,6 +511,7 @@ struct sock {
 	u32			sk_priority;
 	u32			sk_mark;
 	kuid_t			sk_uid;
+	seqcount_t		sk_uid_seq;
 	u16			sk_protocol;
 	u16			sk_type;
 	struct dst_entry __rcu	*sk_dst_cache;
@@ -2109,11 +2112,23 @@ static inline int sk_rx_queue_get(const struct sock *sk)
 	return -1;
 }
 
+/*
+ * sk_uid is too big for WRITE_ONCE(): writers hold sk_callback_lock (or own
+ * the socket alone, before it is published) and bump sk_uid_seq around the
+ * store, so sk_uid() can read it without a lock.
+ */
+static inline void __sk_set_uid(struct sock *sk, kuid_t uid)
+{
+	write_seqcount_begin(&sk->sk_uid_seq);
+	sk->sk_uid = uid;
+	write_seqcount_end(&sk->sk_uid_seq);
+}
+
 static inline void sk_set_socket(struct sock *sk, struct socket *sock)
 {
 	WRITE_ONCE(sk->sk_socket, sock);
 	if (sock) {
-		WRITE_ONCE(sk->sk_uid, SOCK_INODE(sock)->i_uid);
+		__sk_set_uid(sk, SOCK_INODE(sock)->i_uid);
 		WRITE_ONCE(sk->sk_ino, SOCK_INODE(sock)->i_ino);
 	} else {
 		/* Note: sk_uid is unchanged. */
@@ -2161,8 +2176,15 @@ static inline u64 sock_i_ino(const struct sock *sk)
 
 static inline kuid_t sk_uid(const struct sock *sk)
 {
-	/* Paired with WRITE_ONCE() in sockfs_setattr() */
-	return READ_ONCE(sk->sk_uid);
+	unsigned int seq;
+	kuid_t uid;
+
+	/* Paired with __sk_set_uid() */
+	do {
+		seq = read_seqcount_begin(&sk->sk_uid_seq);
+		uid = sk->sk_uid;
+	} while (read_seqcount_retry(&sk->sk_uid_seq, seq));
+	return uid;
 }
 
 static inline kuid_t sock_net_uid(const struct net *net, const struct sock *sk)
diff --git a/net/core/sock.c b/net/core/sock.c
index e8551df833..300bc75ce1 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -2449,6 +2449,7 @@ static void sk_init_common(struct sock *sk)
 	skb_queue_head_init(&sk->sk_error_queue);
 
 	rwlock_init(&sk->sk_callback_lock);
+	seqcount_init(&sk->sk_uid_seq);
 	lockdep_set_class_and_name(&sk->sk_receive_queue.lock,
 			af_rlock_keys + sk->sk_family,
 			af_family_rlock_key_strings[sk->sk_family]);
@@ -2529,6 +2530,8 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
 
 	atomic_set(&newsk->sk_omem_alloc, 0);
 	sk_init_common(newsk);
+	/* sock_copy() may have caught a half written sk_uid */
+	newsk->sk_uid = sk_uid(sk);
 
 	newsk->sk_dst_cache	= NULL;
 	newsk->sk_dst_pending_confirm = 0;
@@ -3773,7 +3776,7 @@ void sock_init_data_uid(struct socket *sock, struct sock *sk, kuid_t uid)
 	} else {
 		RCU_INIT_POINTER(sk->sk_wq, NULL);
 	}
-	sk->sk_uid	=	uid;
+	__sk_set_uid(sk, uid);
 
 	sk->sk_state_change	=	sock_def_wakeup;
 	sk->sk_data_ready	=	sock_def_readable;
@@ -4614,6 +4617,7 @@ static int __init sock_struct_check(void)
 	CACHELINE_ASSERT_GROUP_MEMBER(struct sock, sock_read_tx, sk_priority);
 	CACHELINE_ASSERT_GROUP_MEMBER(struct sock, sock_read_tx, sk_mark);
 	CACHELINE_ASSERT_GROUP_MEMBER(struct sock, sock_read_tx, sk_uid);
+	CACHELINE_ASSERT_GROUP_MEMBER(struct sock, sock_read_tx, sk_uid_seq);
 	CACHELINE_ASSERT_GROUP_MEMBER(struct sock, sock_read_tx, sk_protocol);
 	CACHELINE_ASSERT_GROUP_MEMBER(struct sock, sock_read_tx, sk_dst_cache);
 	CACHELINE_ASSERT_GROUP_MEMBER(struct sock, sock_read_tx, sk_route_caps);
diff --git a/net/socket.c b/net/socket.c
index c05d86e63a..09658f3958 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -679,8 +679,9 @@ static int sockfs_setattr(struct mnt_idmap *idmap,
 		struct socket *sock = SOCKET_I(d_inode(dentry));
 
 		if (sock->sk) {
-			/* Paired with READ_ONCE() in sk_uid() */
-			WRITE_ONCE(sock->sk->sk_uid, iattr->ia_uid);
+			write_lock_bh(&sock->sk->sk_callback_lock);
+			__sk_set_uid(sock->sk, iattr->ia_uid);
+			write_unlock_bh(&sock->sk->sk_callback_lock);
 		} else {
 			err = -ENOENT;
 		}
-- 
2.56.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help