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 <[email protected]> --- 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

