mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
net: restore refcount ordering for lockless socket access
Before commitfb041bb7c0("locking/refcount: Consolidate implementations of refcount_t"), default builds implemented refcount_inc_not_zero() using atomic_add_unless(), which provided full memory ordering on success. The commit removed that ordering on weakly ordered architectures, allowing accesses after a successful reference acquisition to be reordered before it. Use refcount_inc_not_zero_acquire() in the IPv4 and IPv6 established lookups and in inet_twsk_purge(), where the socket is validated again after taking a reference. bpf_sk_assign() can receive a socket pointer obtained through BTF without a reference. Use acquire ordering when it takes its own reference for a refcounted socket. Use refcount_set_release() when setting refcounts in the SYN cookie, TCP Fast Open, request migration, and TIME_WAIT paths. Keep the existing TIME_WAIT smp_wmb(), which separately orders the refcount store before ehash publication. Fixes:fb041bb7c0("locking/refcount: Consolidate implementations of refcount_t") Fixes:27e5203bd9("bpf: Change bpf_sk_assign to accept ARG_PTR_TO_BTF_ID_SOCK_COMMON") Cc: stable@vger.kernel.org Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com> Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> Link: https://patch.msgid.link/20261029214219.1035176-3-imv4bel@gmail.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
4e2b3ef19e
commit
5b5d8f351d
|
|
@ -8022,7 +8022,7 @@ BPF_CALL_3(bpf_sk_assign, struct sk_buff *, skb, struct sock *, sk, u64, flags)
|
|||
if (sk_unhashed(sk))
|
||||
return -EOPNOTSUPP;
|
||||
if (sk_is_refcounted(sk) &&
|
||||
unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
|
||||
unlikely(!refcount_inc_not_zero_acquire(&sk->sk_refcnt)))
|
||||
return -ENOENT;
|
||||
|
||||
skb_orphan(skb);
|
||||
|
|
|
|||
|
|
@ -1061,8 +1061,8 @@ static void reqsk_timer_handler(struct timer_list *t)
|
|||
* hold another count to prevent use-after-free and
|
||||
* call reqsk_put() just before return.
|
||||
*/
|
||||
refcount_set(&nreq->rsk_refcnt, 2 + 1);
|
||||
timer_setup(&nreq->rsk_timer, reqsk_timer_handler, TIMER_PINNED);
|
||||
refcount_set_release(&nreq->rsk_refcnt, 2 + 1);
|
||||
reqsk_queue_migrated(&inet_csk(nsk)->icsk_accept_queue, req);
|
||||
|
||||
req = nreq;
|
||||
|
|
@ -1447,7 +1447,7 @@ struct sock *inet_csk_complete_hashdance(struct sock *sk, struct sock *child,
|
|||
goto child_put;
|
||||
}
|
||||
|
||||
refcount_set(&nreq->rsk_refcnt, 1);
|
||||
refcount_set_release(&nreq->rsk_refcnt, 1);
|
||||
if (inet_csk_reqsk_queue_add(sk, nreq, child)) {
|
||||
__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMIGRATEREQSUCCESS);
|
||||
reqsk_migrate_reset(req);
|
||||
|
|
@ -1500,7 +1500,7 @@ void inet_csk_listen_stop(struct sock *sk)
|
|||
if (nsk) {
|
||||
nreq = inet_reqsk_clone(req, nsk);
|
||||
if (nreq) {
|
||||
refcount_set(&nreq->rsk_refcnt, 1);
|
||||
refcount_set_release(&nreq->rsk_refcnt, 1);
|
||||
|
||||
rcu_read_lock();
|
||||
if (inet_csk_reqsk_queue_add(nsk, nreq, child)) {
|
||||
|
|
|
|||
|
|
@ -550,7 +550,7 @@ struct sock *__inet_lookup_established(const struct net *net,
|
|||
if (sk->sk_hash != hash)
|
||||
continue;
|
||||
if (likely(inet_match(net, sk, acookie, ports, dif, sdif))) {
|
||||
if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
|
||||
if (unlikely(!refcount_inc_not_zero_acquire(&sk->sk_refcnt)))
|
||||
goto out;
|
||||
if (unlikely(hlist_nulls_unhashed_lockless(&sk->sk_nulls_node) ||
|
||||
!inet_match(net, sk, acookie, ports, dif,
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ void inet_twsk_hashdance_schedule(struct inet_timewait_sock *tw,
|
|||
* Also note that after this point, we lost our implicit reference
|
||||
* so we are not allowed to use tw anymore.
|
||||
*/
|
||||
refcount_set(&tw->tw_refcnt, 3);
|
||||
refcount_set_release(&tw->tw_refcnt, 3);
|
||||
|
||||
/* Ensure tw_refcnt has been set before tw is published.
|
||||
* smp_wmb() provides the necessary memory barrier to enforce this
|
||||
|
|
@ -330,7 +330,7 @@ void inet_twsk_purge(struct inet_hashinfo *hashinfo)
|
|||
if (check_net(sock_net(sk)))
|
||||
continue;
|
||||
|
||||
if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
|
||||
if (unlikely(!refcount_inc_not_zero_acquire(&sk->sk_refcnt)))
|
||||
continue;
|
||||
|
||||
if (check_net(sock_net(sk))) {
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb,
|
|||
child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst,
|
||||
NULL, &own_req, NULL);
|
||||
if (child) {
|
||||
refcount_set(&req->rsk_refcnt, 1);
|
||||
refcount_set_release(&req->rsk_refcnt, 1);
|
||||
sock_rps_save_rxhash(child, skb);
|
||||
|
||||
if (rsk_drop_req(req)) {
|
||||
|
|
|
|||
|
|
@ -366,7 +366,7 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk,
|
|||
tcp_reset_xmit_timer(child, ICSK_TIME_RETRANS,
|
||||
req->timeout, false);
|
||||
|
||||
refcount_set(&req->rsk_refcnt, 2);
|
||||
refcount_set_release(&req->rsk_refcnt, 2);
|
||||
|
||||
sk_mark_napi_id_set(child, skb);
|
||||
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ struct sock *__inet6_lookup_established(const struct net *net,
|
|||
continue;
|
||||
if (!inet6_match(net, sk, saddr, daddr, ports, dif, sdif))
|
||||
continue;
|
||||
if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
|
||||
if (unlikely(!refcount_inc_not_zero_acquire(&sk->sk_refcnt)))
|
||||
goto out;
|
||||
|
||||
if (unlikely(hlist_nulls_unhashed_lockless(&sk->sk_nulls_node) ||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user