tcp: fix crashes in do_tcp_sendpages()

Recent network changes allowed high order pages being used
for skb fragments.

This uncovered a bug in do_tcp_sendpages() which was assuming its caller
provided an array of order-0 page pointers.

We only have to deal with a single page in this function, and its order
is irrelevant.

Reported-by: Willy Tarreau <w@1wt.eu>
Tested-by: Willy Tarreau <w@1wt.eu>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Eric Dumazet 2012-12-01 13:07:02 +00:00 committed by David S. Miller
parent 9f8933e960
commit 64022d0b4e

View file

@ -830,8 +830,8 @@ static int tcp_send_mss(struct sock *sk, int *size_goal, int flags)
return mss_now; return mss_now;
} }
static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, static ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset,
size_t psize, int flags) size_t size, int flags)
{ {
struct tcp_sock *tp = tcp_sk(sk); struct tcp_sock *tp = tcp_sk(sk);
int mss_now, size_goal; int mss_now, size_goal;
@ -858,12 +858,9 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffse
if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN)) if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
goto out_err; goto out_err;
while (psize > 0) { while (size > 0) {
struct sk_buff *skb = tcp_write_queue_tail(sk); struct sk_buff *skb = tcp_write_queue_tail(sk);
struct page *page = pages[poffset / PAGE_SIZE];
int copy, i; int copy, i;
int offset = poffset % PAGE_SIZE;
int size = min_t(size_t, psize, PAGE_SIZE - offset);
bool can_coalesce; bool can_coalesce;
if (!tcp_send_head(sk) || (copy = size_goal - skb->len) <= 0) { if (!tcp_send_head(sk) || (copy = size_goal - skb->len) <= 0) {
@ -912,8 +909,8 @@ new_segment:
TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_PSH; TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_PSH;
copied += copy; copied += copy;
poffset += copy; offset += copy;
if (!(psize -= copy)) if (!(size -= copy))
goto out; goto out;
if (skb->len < size_goal || (flags & MSG_OOB)) if (skb->len < size_goal || (flags & MSG_OOB))
@ -960,7 +957,7 @@ int tcp_sendpage(struct sock *sk, struct page *page, int offset,
flags); flags);
lock_sock(sk); lock_sock(sk);
res = do_tcp_sendpages(sk, &page, offset, size, flags); res = do_tcp_sendpages(sk, page, offset, size, flags);
release_sock(sk); release_sock(sk);
return res; return res;
} }