aboutsummaryrefslogtreecommitdiffstats
path: root/include/net/sock.h
diff options
context:
space:
mode:
authorZhu Yi <yi.zhu@intel.com>2010-03-04 13:01:40 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2010-04-01 19:02:02 -0400
commitab9dd052e4d0c4d281ceec0b7e410e229beb6fb2 (patch)
tree9aac9007cf07ab3043667a0ce572d7a8536b41f3 /include/net/sock.h
parentb4aedbe935216fd8db97163b0e6dba6036391bcf (diff)
net: add limit for socket backlog
[ Upstream commit 8eae939f1400326b06d0c9afe53d2a484a326871 ] We got system OOM while running some UDP netperf testing on the loopback device. The case is multiple senders sent stream UDP packets to a single receiver via loopback on local host. Of course, the receiver is not able to handle all the packets in time. But we surprisingly found that these packets were not discarded due to the receiver's sk->sk_rcvbuf limit. Instead, they are kept queuing to sk->sk_backlog and finally ate up all the memory. We believe this is a secure hole that a none privileged user can crash the system. The root cause for this problem is, when the receiver is doing __release_sock() (i.e. after userspace recv, kernel udp_recvmsg -> skb_free_datagram_locked -> release_sock), it moves skbs from backlog to sk_receive_queue with the softirq enabled. In the above case, multiple busy senders will almost make it an endless loop. The skbs in the backlog end up eat all the system memory. The issue is not only for UDP. Any protocols using socket backlog is potentially affected. The patch adds limit for socket backlog so that the backlog size cannot be expanded endlessly. Reported-by: Alex Shi <alex.shi@intel.com> Cc: David Miller <davem@davemloft.net> Cc: Arnaldo Carvalho de Melo <acme@ghostprotocols.net> Cc: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> Cc: "Pekka Savola (ipv6)" <pekkas@netcore.fi> Cc: Patrick McHardy <kaber@trash.net> Cc: Vlad Yasevich <vladislav.yasevich@hp.com> Cc: Sridhar Samudrala <sri@us.ibm.com> Cc: Jon Maloy <jon.maloy@ericsson.com> Cc: Allan Stephens <allan.stephens@windriver.com> Cc: Andrew Hendry <andrew.hendry@gmail.com> Signed-off-by: Zhu Yi <yi.zhu@intel.com> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'include/net/sock.h')
-rw-r--r--include/net/sock.h15
1 files changed, 14 insertions, 1 deletions
diff --git a/include/net/sock.h b/include/net/sock.h
index 3f1a4804bb3f..cd0b34a4f2b9 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -253,6 +253,8 @@ struct sock {
253 struct { 253 struct {
254 struct sk_buff *head; 254 struct sk_buff *head;
255 struct sk_buff *tail; 255 struct sk_buff *tail;
256 int len;
257 int limit;
256 } sk_backlog; 258 } sk_backlog;
257 wait_queue_head_t *sk_sleep; 259 wait_queue_head_t *sk_sleep;
258 struct dst_entry *sk_dst_cache; 260 struct dst_entry *sk_dst_cache;
@@ -574,7 +576,7 @@ static inline int sk_stream_memory_free(struct sock *sk)
574 return sk->sk_wmem_queued < sk->sk_sndbuf; 576 return sk->sk_wmem_queued < sk->sk_sndbuf;
575} 577}
576 578
577/* The per-socket spinlock must be held here. */ 579/* OOB backlog add */
578static inline void sk_add_backlog(struct sock *sk, struct sk_buff *skb) 580static inline void sk_add_backlog(struct sock *sk, struct sk_buff *skb)
579{ 581{
580 if (!sk->sk_backlog.tail) { 582 if (!sk->sk_backlog.tail) {
@@ -586,6 +588,17 @@ static inline void sk_add_backlog(struct sock *sk, struct sk_buff *skb)
586 skb->next = NULL; 588 skb->next = NULL;
587} 589}
588 590
591/* The per-socket spinlock must be held here. */
592static inline int sk_add_backlog_limited(struct sock *sk, struct sk_buff *skb)
593{
594 if (sk->sk_backlog.len >= max(sk->sk_backlog.limit, sk->sk_rcvbuf << 1))
595 return -ENOBUFS;
596
597 sk_add_backlog(sk, skb);
598 sk->sk_backlog.len += skb->truesize;
599 return 0;
600}
601
589static inline int sk_backlog_rcv(struct sock *sk, struct sk_buff *skb) 602static inline int sk_backlog_rcv(struct sock *sk, struct sk_buff *skb)
590{ 603{
591 return sk->sk_backlog_rcv(sk, skb); 604 return sk->sk_backlog_rcv(sk, skb);