mirror of https://gitee.com/openkylin/linux.git
tcp: fix potential huge kmalloc() calls in TCP_REPAIR
tcp_send_rcvq() is used for re-injecting data into tcp receive queue. Problems : - No check against size is performed, allowed user to fool kernel in attempting very large memory allocations, eventually triggering OOM when memory is fragmented. - In case of fault during the copy we do not return correct errno. Lets use alloc_skb_with_frags() to cook optimal skbs. Fixes:292e8d8c85
("tcp: Move rcvq sending to tcp_input.c") Fixes:c0e88ff0f2
("tcp: Repair socket queues") Signed-off-by: Eric Dumazet <edumazet@google.com> Cc: Pavel Emelyanov <xemul@parallels.com> Acked-by: Pavel Emelyanov <xemul@parallels.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
dd52bc2b4e
commit
5d4c9bfbab
|
@ -4481,19 +4481,34 @@ static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int
|
||||||
int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
|
int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
|
int err = -ENOMEM;
|
||||||
|
int data_len = 0;
|
||||||
bool fragstolen;
|
bool fragstolen;
|
||||||
|
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
skb = alloc_skb(size, sk->sk_allocation);
|
if (size > PAGE_SIZE) {
|
||||||
|
int npages = min_t(size_t, size >> PAGE_SHIFT, MAX_SKB_FRAGS);
|
||||||
|
|
||||||
|
data_len = npages << PAGE_SHIFT;
|
||||||
|
size = data_len + (size & ~PAGE_MASK);
|
||||||
|
}
|
||||||
|
skb = alloc_skb_with_frags(size - data_len, data_len,
|
||||||
|
PAGE_ALLOC_COSTLY_ORDER,
|
||||||
|
&err, sk->sk_allocation);
|
||||||
if (!skb)
|
if (!skb)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
skb_put(skb, size - data_len);
|
||||||
|
skb->data_len = data_len;
|
||||||
|
skb->len = size;
|
||||||
|
|
||||||
if (tcp_try_rmem_schedule(sk, skb, skb->truesize))
|
if (tcp_try_rmem_schedule(sk, skb, skb->truesize))
|
||||||
goto err_free;
|
goto err_free;
|
||||||
|
|
||||||
if (memcpy_from_msg(skb_put(skb, size), msg, size))
|
err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, size);
|
||||||
|
if (err)
|
||||||
goto err_free;
|
goto err_free;
|
||||||
|
|
||||||
TCP_SKB_CB(skb)->seq = tcp_sk(sk)->rcv_nxt;
|
TCP_SKB_CB(skb)->seq = tcp_sk(sk)->rcv_nxt;
|
||||||
|
@ -4509,7 +4524,8 @@ int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
|
||||||
err_free:
|
err_free:
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
err:
|
err:
|
||||||
return -ENOMEM;
|
return err;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
|
static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
|
||||||
|
|
Loading…
Reference in New Issue