Skip to content

Commit

Permalink
Merge branch 'net-sk_err-lockless-annotate'
Browse files Browse the repository at this point in the history
Eric Dumazet says:

====================
net: annotate lockless accesses to sk_err[_soft]

This patch series is inspired by yet another syzbot report.

Most poll() handlers are lockless and read sk->sk_err
while other cpus can change it.

Add READ_ONCE/WRITE_ONCE() to major/usual offenders.

More to come later.
====================

Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
davem330 committed Mar 17, 2023
2 parents 731b73d + cc04410 commit ec4040a
Show file tree
Hide file tree
Showing 20 changed files with 63 additions and 56 deletions.
7 changes: 4 additions & 3 deletions fs/dlm/lowcomms.c
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ static void lowcomms_error_report(struct sock *sk)
"sk_err=%d/%d\n", dlm_our_nodeid(),
con->nodeid, &inet->inet_daddr,
ntohs(inet->inet_dport), sk->sk_err,
sk->sk_err_soft);
READ_ONCE(sk->sk_err_soft));
break;
#if IS_ENABLED(CONFIG_IPV6)
case AF_INET6:
Expand All @@ -610,14 +610,15 @@ static void lowcomms_error_report(struct sock *sk)
"dport %d, sk_err=%d/%d\n", dlm_our_nodeid(),
con->nodeid, &sk->sk_v6_daddr,
ntohs(inet->inet_dport), sk->sk_err,
sk->sk_err_soft);
READ_ONCE(sk->sk_err_soft));
break;
#endif
default:
printk_ratelimited(KERN_ERR "dlm: node %d: socket error "
"invalid socket family %d set, "
"sk_err=%d/%d\n", dlm_our_nodeid(),
sk->sk_family, sk->sk_err, sk->sk_err_soft);
sk->sk_family, sk->sk_err,
READ_ONCE(sk->sk_err_soft));
break;
}

Expand Down
2 changes: 1 addition & 1 deletion net/atm/signaling.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb)
break;
case as_addparty:
case as_dropparty:
sk->sk_err_soft = -msg->reply;
WRITE_ONCE(sk->sk_err_soft, -msg->reply);
/* < 0 failure, otherwise ep_ref */
clear_bit(ATM_VF_WAITING, &vcc->flags);
break;
Expand Down
12 changes: 7 additions & 5 deletions net/dccp/ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ static inline void dccp_do_pmtu_discovery(struct sock *sk,
* for the case, if this connection will not able to recover.
*/
if (mtu < dst_mtu(dst) && ip_dont_fragment(sk, dst))
sk->sk_err_soft = EMSGSIZE;
WRITE_ONCE(sk->sk_err_soft, EMSGSIZE);

mtu = dst_mtu(dst);

Expand Down Expand Up @@ -339,8 +339,9 @@ static int dccp_v4_err(struct sk_buff *skb, u32 info)
sk_error_report(sk);

dccp_done(sk);
} else
sk->sk_err_soft = err;
} else {
WRITE_ONCE(sk->sk_err_soft, err);
}
goto out;
}

Expand All @@ -364,8 +365,9 @@ static int dccp_v4_err(struct sk_buff *skb, u32 info)
if (!sock_owned_by_user(sk) && inet->recverr) {
sk->sk_err = err;
sk_error_report(sk);
} else /* Only an error on timeout */
sk->sk_err_soft = err;
} else { /* Only an error on timeout */
WRITE_ONCE(sk->sk_err_soft, err);
}
out:
bh_unlock_sock(sk);
sock_put(sk);
Expand Down
11 changes: 6 additions & 5 deletions net/dccp/ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,17 +174,18 @@ static int dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
*/
sk_error_report(sk);
dccp_done(sk);
} else
sk->sk_err_soft = err;
} else {
WRITE_ONCE(sk->sk_err_soft, err);
}
goto out;
}

if (!sock_owned_by_user(sk) && np->recverr) {
sk->sk_err = err;
sk_error_report(sk);
} else
sk->sk_err_soft = err;

} else {
WRITE_ONCE(sk->sk_err_soft, err);
}
out:
bh_unlock_sock(sk);
sock_put(sk);
Expand Down
2 changes: 1 addition & 1 deletion net/dccp/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ int sysctl_dccp_retries2 __read_mostly = TCP_RETR2;

static void dccp_write_err(struct sock *sk)
{
sk->sk_err = sk->sk_err_soft ? : ETIMEDOUT;
sk->sk_err = READ_ONCE(sk->sk_err_soft) ? : ETIMEDOUT;
sk_error_report(sk);

dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/af_inet.c
Original file line number Diff line number Diff line change
Expand Up @@ -1322,7 +1322,7 @@ int inet_sk_rebuild_header(struct sock *sk)
sk->sk_state != TCP_SYN_SENT ||
(sk->sk_userlocks & SOCK_BINDADDR_LOCK) ||
(err = inet_sk_reselect_saddr(sk)) != 0)
sk->sk_err_soft = -err;
WRITE_ONCE(sk->sk_err_soft, -err);
}

return err;
Expand Down
11 changes: 6 additions & 5 deletions net/ipv4/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,8 @@ __poll_t tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
}
/* This barrier is coupled with smp_wmb() in tcp_reset() */
smp_rmb();
if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue))
if (READ_ONCE(sk->sk_err) ||
!skb_queue_empty_lockless(&sk->sk_error_queue))
mask |= EPOLLERR;

return mask;
Expand Down Expand Up @@ -3094,17 +3095,17 @@ int tcp_disconnect(struct sock *sk, int flags)
if (old_state == TCP_LISTEN) {
inet_csk_listen_stop(sk);
} else if (unlikely(tp->repair)) {
sk->sk_err = ECONNABORTED;
WRITE_ONCE(sk->sk_err, ECONNABORTED);
} else if (tcp_need_reset(old_state) ||
(tp->snd_nxt != tp->write_seq &&
(1 << old_state) & (TCPF_CLOSING | TCPF_LAST_ACK))) {
/* The last check adjusts for discrepancy of Linux wrt. RFC
* states
*/
tcp_send_active_reset(sk, gfp_any());
sk->sk_err = ECONNRESET;
WRITE_ONCE(sk->sk_err, ECONNRESET);
} else if (old_state == TCP_SYN_SENT)
sk->sk_err = ECONNRESET;
WRITE_ONCE(sk->sk_err, ECONNRESET);

tcp_clear_xmit_timers(sk);
__skb_queue_purge(&sk->sk_receive_queue);
Expand Down Expand Up @@ -4692,7 +4693,7 @@ int tcp_abort(struct sock *sk, int err)
bh_lock_sock(sk);

if (!sock_flag(sk, SOCK_DEAD)) {
sk->sk_err = err;
WRITE_ONCE(sk->sk_err, err);
/* This barrier is coupled with smp_rmb() in tcp_poll() */
smp_wmb();
sk_error_report(sk);
Expand Down
8 changes: 4 additions & 4 deletions net/ipv4/tcp_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -3874,7 +3874,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
/* We passed data and got it acked, remove any soft error
* log. Something worked...
*/
sk->sk_err_soft = 0;
WRITE_ONCE(sk->sk_err_soft, 0);
icsk->icsk_probes_out = 0;
tp->rcv_tstamp = tcp_jiffies32;
if (!prior_packets)
Expand Down Expand Up @@ -4322,15 +4322,15 @@ void tcp_reset(struct sock *sk, struct sk_buff *skb)
/* We want the right error as BSD sees it (and indeed as we do). */
switch (sk->sk_state) {
case TCP_SYN_SENT:
sk->sk_err = ECONNREFUSED;
WRITE_ONCE(sk->sk_err, ECONNREFUSED);
break;
case TCP_CLOSE_WAIT:
sk->sk_err = EPIPE;
WRITE_ONCE(sk->sk_err, EPIPE);
break;
case TCP_CLOSE:
return;
default:
sk->sk_err = ECONNRESET;
WRITE_ONCE(sk->sk_err, ECONNRESET);
}
/* This barrier is coupled with smp_rmb() in tcp_poll() */
smp_wmb();
Expand Down
10 changes: 5 additions & 5 deletions net/ipv4/tcp_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ void tcp_v4_mtu_reduced(struct sock *sk)
* for the case, if this connection will not able to recover.
*/
if (mtu < dst_mtu(dst) && ip_dont_fragment(sk, dst))
sk->sk_err_soft = EMSGSIZE;
WRITE_ONCE(sk->sk_err_soft, EMSGSIZE);

mtu = dst_mtu(dst);

Expand Down Expand Up @@ -596,13 +596,13 @@ int tcp_v4_err(struct sk_buff *skb, u32 info)
ip_icmp_error(sk, skb, err, th->dest, info, (u8 *)th);

if (!sock_owned_by_user(sk)) {
sk->sk_err = err;
WRITE_ONCE(sk->sk_err, err);

sk_error_report(sk);

tcp_done(sk);
} else {
sk->sk_err_soft = err;
WRITE_ONCE(sk->sk_err_soft, err);
}
goto out;
}
Expand All @@ -625,10 +625,10 @@ int tcp_v4_err(struct sk_buff *skb, u32 info)

inet = inet_sk(sk);
if (!sock_owned_by_user(sk) && inet->recverr) {
sk->sk_err = err;
WRITE_ONCE(sk->sk_err, err);
sk_error_report(sk);
} else { /* Only an error on timeout */
sk->sk_err_soft = err;
WRITE_ONCE(sk->sk_err_soft, err);
}

out:
Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/tcp_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -3699,7 +3699,7 @@ static void tcp_connect_init(struct sock *sk)
tp->rx_opt.rcv_wscale = rcv_wscale;
tp->rcv_ssthresh = tp->rcv_wnd;

sk->sk_err = 0;
WRITE_ONCE(sk->sk_err, 0);
sock_reset_flag(sk, SOCK_DONE);
tp->snd_wnd = 0;
tcp_init_wl(tp, 0);
Expand Down
6 changes: 3 additions & 3 deletions net/ipv4/tcp_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ u32 tcp_clamp_probe0_to_user_timeout(const struct sock *sk, u32 when)

static void tcp_write_err(struct sock *sk)
{
sk->sk_err = sk->sk_err_soft ? : ETIMEDOUT;
WRITE_ONCE(sk->sk_err, READ_ONCE(sk->sk_err_soft) ? : ETIMEDOUT);
sk_error_report(sk);

tcp_write_queue_purge(sk);
Expand Down Expand Up @@ -110,7 +110,7 @@ static int tcp_out_of_resources(struct sock *sk, bool do_reset)
shift++;

/* If some dubious ICMP arrived, penalize even more. */
if (sk->sk_err_soft)
if (READ_ONCE(sk->sk_err_soft))
shift++;

if (tcp_check_oom(sk, shift)) {
Expand Down Expand Up @@ -146,7 +146,7 @@ static int tcp_orphan_retries(struct sock *sk, bool alive)
int retries = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_orphan_retries); /* May be zero. */

/* We know from an ICMP that something is wrong. */
if (sk->sk_err_soft && !alive)
if (READ_ONCE(sk->sk_err_soft) && !alive)
retries = 0;

/* However, if socket sent something recently, select some safe
Expand Down
2 changes: 1 addition & 1 deletion net/ipv6/af_inet6.c
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ int inet6_sk_rebuild_header(struct sock *sk)
dst = ip6_dst_lookup_flow(sock_net(sk), sk, &fl6, final_p);
if (IS_ERR(dst)) {
sk->sk_route_caps = 0;
sk->sk_err_soft = -PTR_ERR(dst);
WRITE_ONCE(sk->sk_err_soft, -PTR_ERR(dst));
return PTR_ERR(dst);
}

Expand Down
2 changes: 1 addition & 1 deletion net/ipv6/inet6_connection_sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ int inet6_csk_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl_unused

dst = inet6_csk_route_socket(sk, &fl6);
if (IS_ERR(dst)) {
sk->sk_err_soft = -PTR_ERR(dst);
WRITE_ONCE(sk->sk_err_soft, -PTR_ERR(dst));
sk->sk_route_caps = 0;
kfree_skb(skb);
return PTR_ERR(dst);
Expand Down
15 changes: 8 additions & 7 deletions net/ipv6/tcp_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -493,12 +493,13 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
ipv6_icmp_error(sk, skb, err, th->dest, ntohl(info), (u8 *)th);

if (!sock_owned_by_user(sk)) {
sk->sk_err = err;
WRITE_ONCE(sk->sk_err, err);
sk_error_report(sk); /* Wake people up to see the error (see connect in sock.c) */

tcp_done(sk);
} else
sk->sk_err_soft = err;
} else {
WRITE_ONCE(sk->sk_err_soft, err);
}
goto out;
case TCP_LISTEN:
break;
Expand All @@ -512,11 +513,11 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
}

if (!sock_owned_by_user(sk) && np->recverr) {
sk->sk_err = err;
WRITE_ONCE(sk->sk_err, err);
sk_error_report(sk);
} else
sk->sk_err_soft = err;

} else {
WRITE_ONCE(sk->sk_err_soft, err);
}
out:
bh_unlock_sock(sk);
sock_put(sk);
Expand Down
2 changes: 1 addition & 1 deletion net/mptcp/pm_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -2019,7 +2019,7 @@ static int mptcp_event_put_token_and_ssk(struct sk_buff *skb,
nla_put_s32(skb, MPTCP_ATTR_IF_IDX, ssk->sk_bound_dev_if))
return -EMSGSIZE;

sk_err = ssk->sk_err;
sk_err = READ_ONCE(ssk->sk_err);
if (sk_err && sk->sk_state == TCP_ESTABLISHED &&
nla_put_u8(skb, MPTCP_ATTR_ERROR, sk_err))
return -EMSGSIZE;
Expand Down
8 changes: 4 additions & 4 deletions net/mptcp/protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -2463,15 +2463,15 @@ static void mptcp_check_fastclose(struct mptcp_sock *msk)
/* Mirror the tcp_reset() error propagation */
switch (sk->sk_state) {
case TCP_SYN_SENT:
sk->sk_err = ECONNREFUSED;
WRITE_ONCE(sk->sk_err, ECONNREFUSED);
break;
case TCP_CLOSE_WAIT:
sk->sk_err = EPIPE;
WRITE_ONCE(sk->sk_err, EPIPE);
break;
case TCP_CLOSE:
return;
default:
sk->sk_err = ECONNRESET;
WRITE_ONCE(sk->sk_err, ECONNRESET);
}

inet_sk_state_store(sk, TCP_CLOSE);
Expand Down Expand Up @@ -3791,7 +3791,7 @@ static __poll_t mptcp_poll(struct file *file, struct socket *sock,

/* This barrier is coupled with smp_wmb() in __mptcp_error_report() */
smp_rmb();
if (sk->sk_err)
if (READ_ONCE(sk->sk_err))
mask |= EPOLLERR;

return mask;
Expand Down
4 changes: 2 additions & 2 deletions net/mptcp/subflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -1335,7 +1335,7 @@ static bool subflow_check_data_avail(struct sock *ssk)
subflow->reset_reason = MPTCP_RST_EMPTCP;

reset:
ssk->sk_err = EBADMSG;
WRITE_ONCE(ssk->sk_err, EBADMSG);
tcp_set_state(ssk, TCP_CLOSE);
while ((skb = skb_peek(&ssk->sk_receive_queue)))
sk_eat_skb(ssk, skb);
Expand Down Expand Up @@ -1419,7 +1419,7 @@ void __mptcp_error_report(struct sock *sk)
ssk_state = inet_sk_state_load(ssk);
if (ssk_state == TCP_CLOSE && !sock_flag(sk, SOCK_DEAD))
inet_sk_state_store(sk, ssk_state);
sk->sk_err = -err;
WRITE_ONCE(sk->sk_err, -err);

/* This barrier is coupled with smp_rmb() in mptcp_poll() */
smp_wmb();
Expand Down
2 changes: 1 addition & 1 deletion net/sctp/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ static void sctp_v4_err_handle(struct sctp_transport *t, struct sk_buff *skb,
sk->sk_err = err;
sk_error_report(sk);
} else { /* Only an error on timeout */
sk->sk_err_soft = err;
WRITE_ONCE(sk->sk_err_soft, err);
}
}

Expand Down
2 changes: 1 addition & 1 deletion net/sctp/ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ static void sctp_v6_err_handle(struct sctp_transport *t, struct sk_buff *skb,
sk->sk_err = err;
sk_error_report(sk);
} else {
sk->sk_err_soft = err;
WRITE_ONCE(sk->sk_err_soft, err);
}
}

Expand Down
Loading

0 comments on commit ec4040a

Please sign in to comment.