mirror of https://gitee.com/openkylin/linux.git
udp: properly cope with csum errors
Dmitry reported that UDP sockets being destroyed would trigger the
WARN_ON(atomic_read(&sk->sk_rmem_alloc)); in inet_sock_destruct()
It turns out we do not properly destroy skb(s) that have wrong UDP
checksum.
Thanks again to syzkaller team.
Fixes : 7c13f97ffd
("udp: do fwd memory scheduling on dequeue")
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Hannes Frederic Sowa <hannes@stressinduktion.org>
Acked-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
6a413e269b
commit
69629464e0
|
@ -2006,7 +2006,9 @@ void sk_reset_timer(struct sock *sk, struct timer_list *timer,
|
||||||
void sk_stop_timer(struct sock *sk, struct timer_list *timer);
|
void sk_stop_timer(struct sock *sk, struct timer_list *timer);
|
||||||
|
|
||||||
int __sk_queue_drop_skb(struct sock *sk, struct sk_buff *skb,
|
int __sk_queue_drop_skb(struct sock *sk, struct sk_buff *skb,
|
||||||
unsigned int flags);
|
unsigned int flags,
|
||||||
|
void (*destructor)(struct sock *sk,
|
||||||
|
struct sk_buff *skb));
|
||||||
int __sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
|
int __sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
|
||||||
int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
|
int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
|
||||||
|
|
||||||
|
|
|
@ -332,7 +332,9 @@ void __skb_free_datagram_locked(struct sock *sk, struct sk_buff *skb, int len)
|
||||||
EXPORT_SYMBOL(__skb_free_datagram_locked);
|
EXPORT_SYMBOL(__skb_free_datagram_locked);
|
||||||
|
|
||||||
int __sk_queue_drop_skb(struct sock *sk, struct sk_buff *skb,
|
int __sk_queue_drop_skb(struct sock *sk, struct sk_buff *skb,
|
||||||
unsigned int flags)
|
unsigned int flags,
|
||||||
|
void (*destructor)(struct sock *sk,
|
||||||
|
struct sk_buff *skb))
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
@ -342,6 +344,8 @@ int __sk_queue_drop_skb(struct sock *sk, struct sk_buff *skb,
|
||||||
if (skb == skb_peek(&sk->sk_receive_queue)) {
|
if (skb == skb_peek(&sk->sk_receive_queue)) {
|
||||||
__skb_unlink(skb, &sk->sk_receive_queue);
|
__skb_unlink(skb, &sk->sk_receive_queue);
|
||||||
atomic_dec(&skb->users);
|
atomic_dec(&skb->users);
|
||||||
|
if (destructor)
|
||||||
|
destructor(sk, skb);
|
||||||
err = 0;
|
err = 0;
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&sk->sk_receive_queue.lock);
|
spin_unlock_bh(&sk->sk_receive_queue.lock);
|
||||||
|
@ -375,7 +379,7 @@ EXPORT_SYMBOL(__sk_queue_drop_skb);
|
||||||
|
|
||||||
int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags)
|
int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags)
|
||||||
{
|
{
|
||||||
int err = __sk_queue_drop_skb(sk, skb, flags);
|
int err = __sk_queue_drop_skb(sk, skb, flags, NULL);
|
||||||
|
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
sk_mem_reclaim_partial(sk);
|
sk_mem_reclaim_partial(sk);
|
||||||
|
|
|
@ -1501,7 +1501,7 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock,
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
csum_copy_err:
|
csum_copy_err:
|
||||||
if (!__sk_queue_drop_skb(sk, skb, flags)) {
|
if (!__sk_queue_drop_skb(sk, skb, flags, udp_skb_destructor)) {
|
||||||
UDP_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite);
|
UDP_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite);
|
||||||
UDP_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite);
|
UDP_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite);
|
||||||
}
|
}
|
||||||
|
|
|
@ -441,7 +441,7 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
csum_copy_err:
|
csum_copy_err:
|
||||||
if (!__sk_queue_drop_skb(sk, skb, flags)) {
|
if (!__sk_queue_drop_skb(sk, skb, flags, udp_skb_destructor)) {
|
||||||
if (is_udp4) {
|
if (is_udp4) {
|
||||||
UDP_INC_STATS(sock_net(sk),
|
UDP_INC_STATS(sock_net(sk),
|
||||||
UDP_MIB_CSUMERRORS, is_udplite);
|
UDP_MIB_CSUMERRORS, is_udplite);
|
||||||
|
|
Loading…
Reference in New Issue