mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
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: ba4e58eca8
("[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>
This commit is contained in:
parent
729549aa35
commit
882af43a0f
@ -40,6 +40,8 @@ enum {
|
|||||||
UDP_FLAGS_ACCEPT_FRAGLIST,
|
UDP_FLAGS_ACCEPT_FRAGLIST,
|
||||||
UDP_FLAGS_ACCEPT_L4,
|
UDP_FLAGS_ACCEPT_L4,
|
||||||
UDP_FLAGS_ENCAP_ENABLED, /* This socket enabled encap */
|
UDP_FLAGS_ENCAP_ENABLED, /* This socket enabled encap */
|
||||||
|
UDP_FLAGS_UDPLITE_SEND_CC, /* set via udplite setsockopt */
|
||||||
|
UDP_FLAGS_UDPLITE_RECV_CC, /* set via udplite setsockopt */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct udp_sock {
|
struct udp_sock {
|
||||||
@ -54,10 +56,6 @@ struct udp_sock {
|
|||||||
int pending; /* Any pending frames ? */
|
int pending; /* Any pending frames ? */
|
||||||
__u8 encap_type; /* Is this an Encapsulation socket? */
|
__u8 encap_type; /* Is this an Encapsulation socket? */
|
||||||
|
|
||||||
/* indicator bits used by pcflag: */
|
|
||||||
#define UDPLITE_SEND_CC 0x1 /* set via udplite setsockopt */
|
|
||||||
#define UDPLITE_RECV_CC 0x2 /* set via udplite setsocktopt */
|
|
||||||
__u8 pcflag; /* marks socket as UDP-Lite if > 0 */
|
|
||||||
/*
|
/*
|
||||||
* Following member retains the information to create a UDP header
|
* Following member retains the information to create a UDP header
|
||||||
* when the socket is uncorked.
|
* when the socket is uncorked.
|
||||||
|
@ -66,14 +66,18 @@ static inline int udplite_checksum_init(struct sk_buff *skb, struct udphdr *uh)
|
|||||||
/* Fast-path computation of checksum. Socket may not be locked. */
|
/* Fast-path computation of checksum. Socket may not be locked. */
|
||||||
static inline __wsum udplite_csum(struct sk_buff *skb)
|
static inline __wsum udplite_csum(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
const struct udp_sock *up = udp_sk(skb->sk);
|
|
||||||
const int off = skb_transport_offset(skb);
|
const int off = skb_transport_offset(skb);
|
||||||
|
const struct sock *sk = skb->sk;
|
||||||
int len = skb->len - off;
|
int len = skb->len - off;
|
||||||
|
|
||||||
if ((up->pcflag & UDPLITE_SEND_CC) && up->pcslen < len) {
|
if (udp_test_bit(UDPLITE_SEND_CC, sk)) {
|
||||||
if (0 < up->pcslen)
|
u16 pcslen = READ_ONCE(udp_sk(sk)->pcslen);
|
||||||
len = up->pcslen;
|
|
||||||
udp_hdr(skb)->len = htons(up->pcslen);
|
if (pcslen < len) {
|
||||||
|
if (pcslen > 0)
|
||||||
|
len = pcslen;
|
||||||
|
udp_hdr(skb)->len = htons(pcslen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
skb->ip_summed = CHECKSUM_NONE; /* no HW support for checksumming */
|
skb->ip_summed = CHECKSUM_NONE; /* no HW support for checksumming */
|
||||||
|
|
||||||
|
@ -2120,7 +2120,8 @@ static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb)
|
|||||||
/*
|
/*
|
||||||
* UDP-Lite specific tests, ignored on UDP sockets
|
* UDP-Lite specific tests, ignored on UDP sockets
|
||||||
*/
|
*/
|
||||||
if ((up->pcflag & UDPLITE_RECV_CC) && UDP_SKB_CB(skb)->partial_cov) {
|
if (udp_test_bit(UDPLITE_RECV_CC, sk) && UDP_SKB_CB(skb)->partial_cov) {
|
||||||
|
u16 pcrlen = READ_ONCE(up->pcrlen);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MIB statistics other than incrementing the error count are
|
* MIB statistics other than incrementing the error count are
|
||||||
@ -2133,7 +2134,7 @@ static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb)
|
|||||||
* delivery of packets with coverage values less than a value
|
* delivery of packets with coverage values less than a value
|
||||||
* provided by the application."
|
* provided by the application."
|
||||||
*/
|
*/
|
||||||
if (up->pcrlen == 0) { /* full coverage was set */
|
if (pcrlen == 0) { /* full coverage was set */
|
||||||
net_dbg_ratelimited("UDPLite: partial coverage %d while full coverage %d requested\n",
|
net_dbg_ratelimited("UDPLite: partial coverage %d while full coverage %d requested\n",
|
||||||
UDP_SKB_CB(skb)->cscov, skb->len);
|
UDP_SKB_CB(skb)->cscov, skb->len);
|
||||||
goto drop;
|
goto drop;
|
||||||
@ -2144,9 +2145,9 @@ static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb)
|
|||||||
* that it wants x while sender emits packets of smaller size y.
|
* that it wants x while sender emits packets of smaller size y.
|
||||||
* Therefore the above ...()->partial_cov statement is essential.
|
* Therefore the above ...()->partial_cov statement is essential.
|
||||||
*/
|
*/
|
||||||
if (UDP_SKB_CB(skb)->cscov < up->pcrlen) {
|
if (UDP_SKB_CB(skb)->cscov < pcrlen) {
|
||||||
net_dbg_ratelimited("UDPLite: coverage %d too small, need min %d\n",
|
net_dbg_ratelimited("UDPLite: coverage %d too small, need min %d\n",
|
||||||
UDP_SKB_CB(skb)->cscov, up->pcrlen);
|
UDP_SKB_CB(skb)->cscov, pcrlen);
|
||||||
goto drop;
|
goto drop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2729,8 +2730,8 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
|
|||||||
val = 8;
|
val = 8;
|
||||||
else if (val > USHRT_MAX)
|
else if (val > USHRT_MAX)
|
||||||
val = USHRT_MAX;
|
val = USHRT_MAX;
|
||||||
up->pcslen = val;
|
WRITE_ONCE(up->pcslen, val);
|
||||||
up->pcflag |= UDPLITE_SEND_CC;
|
udp_set_bit(UDPLITE_SEND_CC, sk);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* The receiver specifies a minimum checksum coverage value. To make
|
/* The receiver specifies a minimum checksum coverage value. To make
|
||||||
@ -2743,8 +2744,8 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
|
|||||||
val = 8;
|
val = 8;
|
||||||
else if (val > USHRT_MAX)
|
else if (val > USHRT_MAX)
|
||||||
val = USHRT_MAX;
|
val = USHRT_MAX;
|
||||||
up->pcrlen = val;
|
WRITE_ONCE(up->pcrlen, val);
|
||||||
up->pcflag |= UDPLITE_RECV_CC;
|
udp_set_bit(UDPLITE_RECV_CC, sk);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -2808,11 +2809,11 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
|
|||||||
/* The following two cannot be changed on UDP sockets, the return is
|
/* The following two cannot be changed on UDP sockets, the return is
|
||||||
* always 0 (which corresponds to the full checksum coverage of UDP). */
|
* always 0 (which corresponds to the full checksum coverage of UDP). */
|
||||||
case UDPLITE_SEND_CSCOV:
|
case UDPLITE_SEND_CSCOV:
|
||||||
val = up->pcslen;
|
val = READ_ONCE(up->pcslen);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UDPLITE_RECV_CSCOV:
|
case UDPLITE_RECV_CSCOV:
|
||||||
val = up->pcrlen;
|
val = READ_ONCE(up->pcrlen);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -727,16 +727,17 @@ static int udpv6_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb)
|
|||||||
/*
|
/*
|
||||||
* UDP-Lite specific tests, ignored on UDP sockets (see net/ipv4/udp.c).
|
* UDP-Lite specific tests, ignored on UDP sockets (see net/ipv4/udp.c).
|
||||||
*/
|
*/
|
||||||
if ((up->pcflag & UDPLITE_RECV_CC) && UDP_SKB_CB(skb)->partial_cov) {
|
if (udp_test_bit(UDPLITE_RECV_CC, sk) && UDP_SKB_CB(skb)->partial_cov) {
|
||||||
|
u16 pcrlen = READ_ONCE(up->pcrlen);
|
||||||
|
|
||||||
if (up->pcrlen == 0) { /* full coverage was set */
|
if (pcrlen == 0) { /* full coverage was set */
|
||||||
net_dbg_ratelimited("UDPLITE6: partial coverage %d while full coverage %d requested\n",
|
net_dbg_ratelimited("UDPLITE6: partial coverage %d while full coverage %d requested\n",
|
||||||
UDP_SKB_CB(skb)->cscov, skb->len);
|
UDP_SKB_CB(skb)->cscov, skb->len);
|
||||||
goto drop;
|
goto drop;
|
||||||
}
|
}
|
||||||
if (UDP_SKB_CB(skb)->cscov < up->pcrlen) {
|
if (UDP_SKB_CB(skb)->cscov < pcrlen) {
|
||||||
net_dbg_ratelimited("UDPLITE6: coverage %d too small, need min %d\n",
|
net_dbg_ratelimited("UDPLITE6: coverage %d too small, need min %d\n",
|
||||||
UDP_SKB_CB(skb)->cscov, up->pcrlen);
|
UDP_SKB_CB(skb)->cscov, pcrlen);
|
||||||
goto drop;
|
goto drop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user