diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2009-10-30 01:03:53 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-10-30 15:25:12 -0400 |
commit | 9d410c796067686b1e032d54ce475b7055537138 (patch) | |
tree | 4442bdbe4a4be1cce024f2e83a12b7404ef935fa | |
parent | 63ca2d74ea4f9c7a7ac082c915609a7b224908e7 (diff) |
net: fix sk_forward_alloc corruption
On UDP sockets, we must call skb_free_datagram() with socket locked,
or risk sk_forward_alloc corruption. This requirement is not respected
in SUNRPC.
Add a convenient helper, skb_free_datagram_locked() and use it in SUNRPC
Reported-by: Francis Moreau <francis.moro@gmail.com>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/skbuff.h | 2 | ||||
-rw-r--r-- | net/core/datagram.c | 10 | ||||
-rw-r--r-- | net/ipv4/udp.c | 4 | ||||
-rw-r--r-- | net/ipv6/udp.c | 4 | ||||
-rw-r--r-- | net/sunrpc/svcsock.c | 10 |
5 files changed, 18 insertions, 12 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 6aebfceca3e..bcdd6606f46 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
@@ -1757,6 +1757,8 @@ extern int skb_copy_datagram_const_iovec(const struct sk_buff *from, | |||
1757 | int to_offset, | 1757 | int to_offset, |
1758 | int size); | 1758 | int size); |
1759 | extern void skb_free_datagram(struct sock *sk, struct sk_buff *skb); | 1759 | extern void skb_free_datagram(struct sock *sk, struct sk_buff *skb); |
1760 | extern void skb_free_datagram_locked(struct sock *sk, | ||
1761 | struct sk_buff *skb); | ||
1760 | extern int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, | 1762 | extern int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, |
1761 | unsigned int flags); | 1763 | unsigned int flags); |
1762 | extern __wsum skb_checksum(const struct sk_buff *skb, int offset, | 1764 | extern __wsum skb_checksum(const struct sk_buff *skb, int offset, |
diff --git a/net/core/datagram.c b/net/core/datagram.c index 1c6cf3a1a4f..4ade3011bb3 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c | |||
@@ -224,6 +224,15 @@ void skb_free_datagram(struct sock *sk, struct sk_buff *skb) | |||
224 | consume_skb(skb); | 224 | consume_skb(skb); |
225 | sk_mem_reclaim_partial(sk); | 225 | sk_mem_reclaim_partial(sk); |
226 | } | 226 | } |
227 | EXPORT_SYMBOL(skb_free_datagram); | ||
228 | |||
229 | void skb_free_datagram_locked(struct sock *sk, struct sk_buff *skb) | ||
230 | { | ||
231 | lock_sock(sk); | ||
232 | skb_free_datagram(sk, skb); | ||
233 | release_sock(sk); | ||
234 | } | ||
235 | EXPORT_SYMBOL(skb_free_datagram_locked); | ||
227 | 236 | ||
228 | /** | 237 | /** |
229 | * skb_kill_datagram - Free a datagram skbuff forcibly | 238 | * skb_kill_datagram - Free a datagram skbuff forcibly |
@@ -752,5 +761,4 @@ unsigned int datagram_poll(struct file *file, struct socket *sock, | |||
752 | EXPORT_SYMBOL(datagram_poll); | 761 | EXPORT_SYMBOL(datagram_poll); |
753 | EXPORT_SYMBOL(skb_copy_and_csum_datagram_iovec); | 762 | EXPORT_SYMBOL(skb_copy_and_csum_datagram_iovec); |
754 | EXPORT_SYMBOL(skb_copy_datagram_iovec); | 763 | EXPORT_SYMBOL(skb_copy_datagram_iovec); |
755 | EXPORT_SYMBOL(skb_free_datagram); | ||
756 | EXPORT_SYMBOL(skb_recv_datagram); | 764 | EXPORT_SYMBOL(skb_recv_datagram); |
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index d0d436d6216..0fa9f70e4b1 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -999,9 +999,7 @@ try_again: | |||
999 | err = ulen; | 999 | err = ulen; |
1000 | 1000 | ||
1001 | out_free: | 1001 | out_free: |
1002 | lock_sock(sk); | 1002 | skb_free_datagram_locked(sk, skb); |
1003 | skb_free_datagram(sk, skb); | ||
1004 | release_sock(sk); | ||
1005 | out: | 1003 | out: |
1006 | return err; | 1004 | return err; |
1007 | 1005 | ||
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 3a60f12b34e..cf538ed5ef6 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -288,9 +288,7 @@ try_again: | |||
288 | err = ulen; | 288 | err = ulen; |
289 | 289 | ||
290 | out_free: | 290 | out_free: |
291 | lock_sock(sk); | 291 | skb_free_datagram_locked(sk, skb); |
292 | skb_free_datagram(sk, skb); | ||
293 | release_sock(sk); | ||
294 | out: | 292 | out: |
295 | return err; | 293 | return err; |
296 | 294 | ||
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index ccc5e83cae5..1c246a4f491 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c | |||
@@ -111,7 +111,7 @@ static void svc_release_skb(struct svc_rqst *rqstp) | |||
111 | rqstp->rq_xprt_ctxt = NULL; | 111 | rqstp->rq_xprt_ctxt = NULL; |
112 | 112 | ||
113 | dprintk("svc: service %p, releasing skb %p\n", rqstp, skb); | 113 | dprintk("svc: service %p, releasing skb %p\n", rqstp, skb); |
114 | skb_free_datagram(svsk->sk_sk, skb); | 114 | skb_free_datagram_locked(svsk->sk_sk, skb); |
115 | } | 115 | } |
116 | } | 116 | } |
117 | 117 | ||
@@ -578,7 +578,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp) | |||
578 | "svc: received unknown control message %d/%d; " | 578 | "svc: received unknown control message %d/%d; " |
579 | "dropping RPC reply datagram\n", | 579 | "dropping RPC reply datagram\n", |
580 | cmh->cmsg_level, cmh->cmsg_type); | 580 | cmh->cmsg_level, cmh->cmsg_type); |
581 | skb_free_datagram(svsk->sk_sk, skb); | 581 | skb_free_datagram_locked(svsk->sk_sk, skb); |
582 | return 0; | 582 | return 0; |
583 | } | 583 | } |
584 | 584 | ||
@@ -588,18 +588,18 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp) | |||
588 | if (csum_partial_copy_to_xdr(&rqstp->rq_arg, skb)) { | 588 | if (csum_partial_copy_to_xdr(&rqstp->rq_arg, skb)) { |
589 | local_bh_enable(); | 589 | local_bh_enable(); |
590 | /* checksum error */ | 590 | /* checksum error */ |
591 | skb_free_datagram(svsk->sk_sk, skb); | 591 | skb_free_datagram_locked(svsk->sk_sk, skb); |
592 | return 0; | 592 | return 0; |
593 | } | 593 | } |
594 | local_bh_enable(); | 594 | local_bh_enable(); |
595 | skb_free_datagram(svsk->sk_sk, skb); | 595 | skb_free_datagram_locked(svsk->sk_sk, skb); |
596 | } else { | 596 | } else { |
597 | /* we can use it in-place */ | 597 | /* we can use it in-place */ |
598 | rqstp->rq_arg.head[0].iov_base = skb->data + | 598 | rqstp->rq_arg.head[0].iov_base = skb->data + |
599 | sizeof(struct udphdr); | 599 | sizeof(struct udphdr); |
600 | rqstp->rq_arg.head[0].iov_len = len; | 600 | rqstp->rq_arg.head[0].iov_len = len; |
601 | if (skb_checksum_complete(skb)) { | 601 | if (skb_checksum_complete(skb)) { |
602 | skb_free_datagram(svsk->sk_sk, skb); | 602 | skb_free_datagram_locked(svsk->sk_sk, skb); |
603 | return 0; | 603 | return 0; |
604 | } | 604 | } |
605 | rqstp->rq_xprt_ctxt = skb; | 605 | rqstp->rq_xprt_ctxt = skb; |