Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

udplite: fix various data-races

udp->pcflag, udp->pcslen and udp->pcrlen reads/writes are racy.

Move udp->pcflag to udp->udp_flags for atomicity,
and add READ_ONCE()/WRITE_ONCE() annotations for pcslen and pcrlen.

Fixes: ba4e58eca8aa ("[NET]: Supporting UDP-Lite (RFC 3828) in Linux")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Eric Dumazet and committed by
Paolo Abeni
882af43a 729549aa

+27 -23
+2 -4
include/linux/udp.h
··· 40 40 UDP_FLAGS_ACCEPT_FRAGLIST, 41 41 UDP_FLAGS_ACCEPT_L4, 42 42 UDP_FLAGS_ENCAP_ENABLED, /* This socket enabled encap */ 43 + UDP_FLAGS_UDPLITE_SEND_CC, /* set via udplite setsockopt */ 44 + UDP_FLAGS_UDPLITE_RECV_CC, /* set via udplite setsockopt */ 43 45 }; 44 46 45 47 struct udp_sock { ··· 56 54 int pending; /* Any pending frames ? */ 57 55 __u8 encap_type; /* Is this an Encapsulation socket? */ 58 56 59 - /* indicator bits used by pcflag: */ 60 - #define UDPLITE_SEND_CC 0x1 /* set via udplite setsockopt */ 61 - #define UDPLITE_RECV_CC 0x2 /* set via udplite setsocktopt */ 62 - __u8 pcflag; /* marks socket as UDP-Lite if > 0 */ 63 57 /* 64 58 * Following member retains the information to create a UDP header 65 59 * when the socket is uncorked.
+9 -5
include/net/udplite.h
··· 66 66 /* Fast-path computation of checksum. Socket may not be locked. */ 67 67 static inline __wsum udplite_csum(struct sk_buff *skb) 68 68 { 69 - const struct udp_sock *up = udp_sk(skb->sk); 70 69 const int off = skb_transport_offset(skb); 70 + const struct sock *sk = skb->sk; 71 71 int len = skb->len - off; 72 72 73 - if ((up->pcflag & UDPLITE_SEND_CC) && up->pcslen < len) { 74 - if (0 < up->pcslen) 75 - len = up->pcslen; 76 - udp_hdr(skb)->len = htons(up->pcslen); 73 + if (udp_test_bit(UDPLITE_SEND_CC, sk)) { 74 + u16 pcslen = READ_ONCE(udp_sk(sk)->pcslen); 75 + 76 + if (pcslen < len) { 77 + if (pcslen > 0) 78 + len = pcslen; 79 + udp_hdr(skb)->len = htons(pcslen); 80 + } 77 81 } 78 82 skb->ip_summed = CHECKSUM_NONE; /* no HW support for checksumming */ 79 83
+11 -10
net/ipv4/udp.c
··· 2120 2120 /* 2121 2121 * UDP-Lite specific tests, ignored on UDP sockets 2122 2122 */ 2123 - if ((up->pcflag & UDPLITE_RECV_CC) && UDP_SKB_CB(skb)->partial_cov) { 2123 + if (udp_test_bit(UDPLITE_RECV_CC, sk) && UDP_SKB_CB(skb)->partial_cov) { 2124 + u16 pcrlen = READ_ONCE(up->pcrlen); 2124 2125 2125 2126 /* 2126 2127 * MIB statistics other than incrementing the error count are ··· 2134 2133 * delivery of packets with coverage values less than a value 2135 2134 * provided by the application." 2136 2135 */ 2137 - if (up->pcrlen == 0) { /* full coverage was set */ 2136 + if (pcrlen == 0) { /* full coverage was set */ 2138 2137 net_dbg_ratelimited("UDPLite: partial coverage %d while full coverage %d requested\n", 2139 2138 UDP_SKB_CB(skb)->cscov, skb->len); 2140 2139 goto drop; ··· 2145 2144 * that it wants x while sender emits packets of smaller size y. 2146 2145 * Therefore the above ...()->partial_cov statement is essential. 2147 2146 */ 2148 - if (UDP_SKB_CB(skb)->cscov < up->pcrlen) { 2147 + if (UDP_SKB_CB(skb)->cscov < pcrlen) { 2149 2148 net_dbg_ratelimited("UDPLite: coverage %d too small, need min %d\n", 2150 - UDP_SKB_CB(skb)->cscov, up->pcrlen); 2149 + UDP_SKB_CB(skb)->cscov, pcrlen); 2151 2150 goto drop; 2152 2151 } 2153 2152 } ··· 2730 2729 val = 8; 2731 2730 else if (val > USHRT_MAX) 2732 2731 val = USHRT_MAX; 2733 - up->pcslen = val; 2734 - up->pcflag |= UDPLITE_SEND_CC; 2732 + WRITE_ONCE(up->pcslen, val); 2733 + udp_set_bit(UDPLITE_SEND_CC, sk); 2735 2734 break; 2736 2735 2737 2736 /* The receiver specifies a minimum checksum coverage value. To make ··· 2744 2743 val = 8; 2745 2744 else if (val > USHRT_MAX) 2746 2745 val = USHRT_MAX; 2747 - up->pcrlen = val; 2748 - up->pcflag |= UDPLITE_RECV_CC; 2746 + WRITE_ONCE(up->pcrlen, val); 2747 + udp_set_bit(UDPLITE_RECV_CC, sk); 2749 2748 break; 2750 2749 2751 2750 default: ··· 2809 2808 /* The following two cannot be changed on UDP sockets, the return is 2810 2809 * always 0 (which corresponds to the full checksum coverage of UDP). */ 2811 2810 case UDPLITE_SEND_CSCOV: 2812 - val = up->pcslen; 2811 + val = READ_ONCE(up->pcslen); 2813 2812 break; 2814 2813 2815 2814 case UDPLITE_RECV_CSCOV: 2816 - val = up->pcrlen; 2815 + val = READ_ONCE(up->pcrlen); 2817 2816 break; 2818 2817 2819 2818 default:
+5 -4
net/ipv6/udp.c
··· 727 727 /* 728 728 * UDP-Lite specific tests, ignored on UDP sockets (see net/ipv4/udp.c). 729 729 */ 730 - if ((up->pcflag & UDPLITE_RECV_CC) && UDP_SKB_CB(skb)->partial_cov) { 730 + if (udp_test_bit(UDPLITE_RECV_CC, sk) && UDP_SKB_CB(skb)->partial_cov) { 731 + u16 pcrlen = READ_ONCE(up->pcrlen); 731 732 732 - if (up->pcrlen == 0) { /* full coverage was set */ 733 + if (pcrlen == 0) { /* full coverage was set */ 733 734 net_dbg_ratelimited("UDPLITE6: partial coverage %d while full coverage %d requested\n", 734 735 UDP_SKB_CB(skb)->cscov, skb->len); 735 736 goto drop; 736 737 } 737 - if (UDP_SKB_CB(skb)->cscov < up->pcrlen) { 738 + if (UDP_SKB_CB(skb)->cscov < pcrlen) { 738 739 net_dbg_ratelimited("UDPLITE6: coverage %d too small, need min %d\n", 739 - UDP_SKB_CB(skb)->cscov, up->pcrlen); 740 + UDP_SKB_CB(skb)->cscov, pcrlen); 740 741 goto drop; 741 742 } 742 743 }