aboutsummaryrefslogtreecommitdiffstats
path: root/main/linux-grsec/ipv6-udp-packets-following-an-UFO-enqueued-packet-needs-al.patch
diff options
context:
space:
mode:
Diffstat (limited to 'main/linux-grsec/ipv6-udp-packets-following-an-UFO-enqueued-packet-needs-al.patch')
-rw-r--r--main/linux-grsec/ipv6-udp-packets-following-an-UFO-enqueued-packet-needs-al.patch118
1 files changed, 118 insertions, 0 deletions
diff --git a/main/linux-grsec/ipv6-udp-packets-following-an-UFO-enqueued-packet-needs-al.patch b/main/linux-grsec/ipv6-udp-packets-following-an-UFO-enqueued-packet-needs-al.patch
new file mode 100644
index 0000000000..a98faca44e
--- /dev/null
+++ b/main/linux-grsec/ipv6-udp-packets-following-an-UFO-enqueued-packet-needs-al.patch
@@ -0,0 +1,118 @@
+From 2811ebac2521ceac84f2bdae402455baa6a7fb47 Mon Sep 17 00:00:00 2001
+From: Hannes Frederic Sowa <hannes@stressinduktion.org>
+Date: Sat, 21 Sep 2013 04:27:00 +0000
+Subject: ipv6: udp packets following an UFO enqueued packet need also be handled by UFO
+
+In the following scenario the socket is corked:
+If the first UDP packet is larger then the mtu we try to append it to the
+write queue via ip6_ufo_append_data. A following packet, which is smaller
+than the mtu would be appended to the already queued up gso-skb via
+plain ip6_append_data. This causes random memory corruptions.
+
+In ip6_ufo_append_data we also have to be careful to not queue up the
+same skb multiple times. So setup the gso frame only when no first skb
+is available.
+
+This also fixes a shortcoming where we add the current packet's length to
+cork->length but return early because of a packet > mtu with dontfrag set
+(instead of sutracting it again).
+
+Found with trinity.
+
+Cc: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
+Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
+Reported-by: Dmitry Vyukov <dvyukov@google.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index 3a692d5..a54c45c 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -1015,6 +1015,8 @@ static inline int ip6_ufo_append_data(struct sock *sk,
+ * udp datagram
+ */
+ if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) {
++ struct frag_hdr fhdr;
++
+ skb = sock_alloc_send_skb(sk,
+ hh_len + fragheaderlen + transhdrlen + 20,
+ (flags & MSG_DONTWAIT), &err);
+@@ -1036,12 +1038,6 @@ static inline int ip6_ufo_append_data(struct sock *sk,
+ skb->protocol = htons(ETH_P_IPV6);
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ skb->csum = 0;
+- }
+-
+- err = skb_append_datato_frags(sk,skb, getfrag, from,
+- (length - transhdrlen));
+- if (!err) {
+- struct frag_hdr fhdr;
+
+ /* Specify the length of each IPv6 datagram fragment.
+ * It has to be a multiple of 8.
+@@ -1052,15 +1048,10 @@ static inline int ip6_ufo_append_data(struct sock *sk,
+ ipv6_select_ident(&fhdr, rt);
+ skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
+ __skb_queue_tail(&sk->sk_write_queue, skb);
+-
+- return 0;
+ }
+- /* There is not enough support do UPD LSO,
+- * so follow normal path
+- */
+- kfree_skb(skb);
+
+- return err;
++ return skb_append_datato_frags(sk, skb, getfrag, from,
++ (length - transhdrlen));
+ }
+
+ static inline struct ipv6_opt_hdr *ip6_opt_dup(struct ipv6_opt_hdr *src,
+@@ -1227,27 +1218,27 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
+ * --yoshfuji
+ */
+
+- cork->length += length;
+- if (length > mtu) {
+- int proto = sk->sk_protocol;
+- if (dontfrag && (proto == IPPROTO_UDP || proto == IPPROTO_RAW)){
+- ipv6_local_rxpmtu(sk, fl6, mtu-exthdrlen);
+- return -EMSGSIZE;
+- }
+-
+- if (proto == IPPROTO_UDP &&
+- (rt->dst.dev->features & NETIF_F_UFO)) {
++ if ((length > mtu) && dontfrag && (sk->sk_protocol == IPPROTO_UDP ||
++ sk->sk_protocol == IPPROTO_RAW)) {
++ ipv6_local_rxpmtu(sk, fl6, mtu-exthdrlen);
++ return -EMSGSIZE;
++ }
+
+- err = ip6_ufo_append_data(sk, getfrag, from, length,
+- hh_len, fragheaderlen,
+- transhdrlen, mtu, flags, rt);
+- if (err)
+- goto error;
+- return 0;
+- }
++ skb = skb_peek_tail(&sk->sk_write_queue);
++ cork->length += length;
++ if (((length > mtu) ||
++ (skb && skb_is_gso(skb))) &&
++ (sk->sk_protocol == IPPROTO_UDP) &&
++ (rt->dst.dev->features & NETIF_F_UFO)) {
++ err = ip6_ufo_append_data(sk, getfrag, from, length,
++ hh_len, fragheaderlen,
++ transhdrlen, mtu, flags, rt);
++ if (err)
++ goto error;
++ return 0;
+ }
+
+- if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL)
++ if (!skb)
+ goto alloc_new_skb;
+
+ while (length > 0) {
+--
+cgit v0.9.2