Skip to content

Commit 67f028a

Browse files
Eric Dumazetgregkh
Eric Dumazet
authored andcommitted
tcp: annotate tp->rcv_nxt lockless reads
[ Upstream commit dba7d9b ] There are few places where we fetch tp->rcv_nxt while this field can change from IRQ or other cpu. We need to add READ_ONCE() annotations, and also make sure write sides use corresponding WRITE_ONCE() to avoid store-tearing. Note that tcp_inq_hint() was already using READ_ONCE(tp->rcv_nxt) syzbot reported : BUG: KCSAN: data-race in tcp_poll / tcp_queue_rcv write to 0xffff888120425770 of 4 bytes by interrupt on cpu 0: tcp_rcv_nxt_update net/ipv4/tcp_input.c:3365 [inline] tcp_queue_rcv+0x180/0x380 net/ipv4/tcp_input.c:4638 tcp_rcv_established+0xbf1/0xf50 net/ipv4/tcp_input.c:5616 tcp_v4_do_rcv+0x381/0x4e0 net/ipv4/tcp_ipv4.c:1542 tcp_v4_rcv+0x1a03/0x1bf0 net/ipv4/tcp_ipv4.c:1923 ip_protocol_deliver_rcu+0x51/0x470 net/ipv4/ip_input.c:204 ip_local_deliver_finish+0x110/0x140 net/ipv4/ip_input.c:231 NF_HOOK include/linux/netfilter.h:305 [inline] NF_HOOK include/linux/netfilter.h:299 [inline] ip_local_deliver+0x133/0x210 net/ipv4/ip_input.c:252 dst_input include/net/dst.h:442 [inline] ip_rcv_finish+0x121/0x160 net/ipv4/ip_input.c:413 NF_HOOK include/linux/netfilter.h:305 [inline] NF_HOOK include/linux/netfilter.h:299 [inline] ip_rcv+0x18f/0x1a0 net/ipv4/ip_input.c:523 __netif_receive_skb_one_core+0xa7/0xe0 net/core/dev.c:5004 __netif_receive_skb+0x37/0xf0 net/core/dev.c:5118 netif_receive_skb_internal+0x59/0x190 net/core/dev.c:5208 napi_skb_finish net/core/dev.c:5671 [inline] napi_gro_receive+0x28f/0x330 net/core/dev.c:5704 receive_buf+0x284/0x30b0 drivers/net/virtio_net.c:1061 read to 0xffff888120425770 of 4 bytes by task 7254 on cpu 1: tcp_stream_is_readable net/ipv4/tcp.c:480 [inline] tcp_poll+0x204/0x6b0 net/ipv4/tcp.c:554 sock_poll+0xed/0x250 net/socket.c:1256 vfs_poll include/linux/poll.h:90 [inline] ep_item_poll.isra.0+0x90/0x190 fs/eventpoll.c:892 ep_send_events_proc+0x113/0x5c0 fs/eventpoll.c:1749 ep_scan_ready_list.constprop.0+0x189/0x500 fs/eventpoll.c:704 ep_send_events fs/eventpoll.c:1793 [inline] ep_poll+0xe3/0x900 fs/eventpoll.c:1930 do_epoll_wait+0x162/0x180 fs/eventpoll.c:2294 __do_sys_epoll_pwait fs/eventpoll.c:2325 [inline] __se_sys_epoll_pwait fs/eventpoll.c:2311 [inline] __x64_sys_epoll_pwait+0xcd/0x170 fs/eventpoll.c:2311 do_syscall_64+0xcf/0x2f0 arch/x86/entry/common.c:296 entry_SYSCALL_64_after_hwframe+0x44/0xa9 Reported by Kernel Concurrency Sanitizer on: CPU: 1 PID: 7254 Comm: syz-fuzzer Not tainted 5.3.0+ #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Signed-off-by: Eric Dumazet <[email protected]> Reported-by: syzbot <[email protected]> Signed-off-by: David S. Miller <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent d9f4d60 commit 67f028a

File tree

6 files changed

+15
-10
lines changed

6 files changed

+15
-10
lines changed

net/ipv4/tcp.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ static void tcp_tx_timestamp(struct sock *sk, u16 tsflags)
488488
static inline bool tcp_stream_is_readable(const struct tcp_sock *tp,
489489
int target, struct sock *sk)
490490
{
491-
return (tp->rcv_nxt - tp->copied_seq >= target) ||
491+
return (READ_ONCE(tp->rcv_nxt) - tp->copied_seq >= target) ||
492492
(sk->sk_prot->stream_memory_read ?
493493
sk->sk_prot->stream_memory_read(sk) : false);
494494
}
@@ -2866,7 +2866,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
28662866
else if (tp->repair_queue == TCP_SEND_QUEUE)
28672867
tp->write_seq = val;
28682868
else if (tp->repair_queue == TCP_RECV_QUEUE)
2869-
tp->rcv_nxt = val;
2869+
WRITE_ONCE(tp->rcv_nxt, val);
28702870
else
28712871
err = -EINVAL;
28722872
break;

net/ipv4/tcp_diag.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
3030
} else if (sk->sk_type == SOCK_STREAM) {
3131
const struct tcp_sock *tp = tcp_sk(sk);
3232

33-
r->idiag_rqueue = max_t(int, tp->rcv_nxt - tp->copied_seq, 0);
33+
r->idiag_rqueue = max_t(int, READ_ONCE(tp->rcv_nxt) - tp->copied_seq, 0);
3434
r->idiag_wqueue = tp->write_seq - tp->snd_una;
3535
}
3636
if (info)

net/ipv4/tcp_input.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3348,7 +3348,7 @@ static void tcp_rcv_nxt_update(struct tcp_sock *tp, u32 seq)
33483348

33493349
sock_owned_by_me((struct sock *)tp);
33503350
tp->bytes_received += delta;
3351-
tp->rcv_nxt = seq;
3351+
WRITE_ONCE(tp->rcv_nxt, seq);
33523352
}
33533353

33543354
/* Update our send window.
@@ -5829,7 +5829,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
58295829
/* Ok.. it's good. Set up sequence numbers and
58305830
* move to established.
58315831
*/
5832-
tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
5832+
WRITE_ONCE(tp->rcv_nxt, TCP_SKB_CB(skb)->seq + 1);
58335833
tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1;
58345834

58355835
/* RFC1323: The window in SYN & SYN/ACK segments is
@@ -5932,7 +5932,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
59325932
tp->tcp_header_len = sizeof(struct tcphdr);
59335933
}
59345934

5935-
tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
5935+
WRITE_ONCE(tp->rcv_nxt, TCP_SKB_CB(skb)->seq + 1);
59365936
tp->copied_seq = tp->rcv_nxt;
59375937
tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1;
59385938

net/ipv4/tcp_ipv4.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2330,7 +2330,8 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
23302330
/* Because we don't lock the socket,
23312331
* we might find a transient negative value.
23322332
*/
2333-
rx_queue = max_t(int, tp->rcv_nxt - tp->copied_seq, 0);
2333+
rx_queue = max_t(int, READ_ONCE(tp->rcv_nxt) -
2334+
tp->copied_seq, 0);
23342335

23352336
seq_printf(f, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08lX "
23362337
"%08X %5u %8d %lu %d %pK %lu %lu %u %u %d",

net/ipv4/tcp_minisocks.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,
454454
struct tcp_request_sock *treq = tcp_rsk(req);
455455
struct inet_connection_sock *newicsk;
456456
struct tcp_sock *oldtp, *newtp;
457+
u32 seq;
457458

458459
if (!newsk)
459460
return NULL;
@@ -467,8 +468,10 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,
467468
/* Now setup tcp_sock */
468469
newtp->pred_flags = 0;
469470

470-
newtp->rcv_wup = newtp->copied_seq =
471-
newtp->rcv_nxt = treq->rcv_isn + 1;
471+
seq = treq->rcv_isn + 1;
472+
newtp->rcv_wup = seq;
473+
newtp->copied_seq = seq;
474+
WRITE_ONCE(newtp->rcv_nxt, seq);
472475
newtp->segs_in = 1;
473476

474477
newtp->snd_sml = newtp->snd_una =

net/ipv6/tcp_ipv6.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1839,7 +1839,8 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
18391839
/* Because we don't lock the socket,
18401840
* we might find a transient negative value.
18411841
*/
1842-
rx_queue = max_t(int, tp->rcv_nxt - tp->copied_seq, 0);
1842+
rx_queue = max_t(int, READ_ONCE(tp->rcv_nxt) -
1843+
tp->copied_seq, 0);
18431844

18441845
seq_printf(seq,
18451846
"%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "

0 commit comments

Comments
 (0)