77 lines
3.0 KiB
Diff
77 lines
3.0 KiB
Diff
From 27e33640a8905b1aeefe9998242551caf24e84a6 Mon Sep 17 00:00:00 2001
|
|
From: Hannes Frederic Sowa <hannes@stressinduktion.org>
|
|
Date: Tue, 22 Oct 2013 00:07:47 +0200
|
|
Subject: inet: fix possible memory corruption with UDP_CORK and UFO
|
|
|
|
From: Hannes Frederic Sowa <hannes@stressinduktion.org>
|
|
|
|
[ This is a simplified -stable version of a set of upstream commits. ]
|
|
|
|
This is a replacement patch only for stable which does fix the problems
|
|
handled by the following two commits in -net:
|
|
|
|
"ip_output: do skb ufo init for peeked non ufo skb as well" (e93b7d748be887cd7639b113ba7d7ef792a7efb9)
|
|
"ip6_output: do skb ufo init for peeked non ufo skb as well" (c547dbf55d5f8cf615ccc0e7265e98db27d3fb8b)
|
|
|
|
Three frames are written on a corked udp socket for which the output
|
|
netdevice has UFO enabled. If the first and third frame are smaller than
|
|
the mtu and the second one is bigger, we enqueue the second frame with
|
|
skb_append_datato_frags without initializing the gso fields. This leads
|
|
to the third frame appended regulary and thus constructing an invalid skb.
|
|
|
|
This fixes the problem by always using skb_append_datato_frags as soon
|
|
as the first frag got enqueued to the skb without marking the packet
|
|
as SKB_GSO_UDP.
|
|
|
|
The problem with only two frames for ipv6 was fixed by "ipv6: udp
|
|
packets following an UFO enqueued packet need also be handled by UFO"
|
|
(2811ebac2521ceac84f2bdae402455baa6a7fb47).
|
|
|
|
Cc: Jiri Pirko <jiri@resnulli.us>
|
|
Cc: Eric Dumazet <eric.dumazet@gmail.com>
|
|
Cc: David Miller <davem@davemloft.net>
|
|
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
include/linux/skbuff.h | 5 +++++
|
|
net/ipv4/ip_output.c | 2 +-
|
|
net/ipv6/ip6_output.c | 2 +-
|
|
3 files changed, 7 insertions(+), 2 deletions(-)
|
|
|
|
--- a/include/linux/skbuff.h
|
|
+++ b/include/linux/skbuff.h
|
|
@@ -1316,6 +1316,11 @@ static inline int skb_pagelen(const stru
|
|
return len + skb_headlen(skb);
|
|
}
|
|
|
|
+static inline bool skb_has_frags(const struct sk_buff *skb)
|
|
+{
|
|
+ return skb_shinfo(skb)->nr_frags;
|
|
+}
|
|
+
|
|
/**
|
|
* __skb_fill_page_desc - initialise a paged fragment in an skb
|
|
* @skb: buffer containing fragment to be initialised
|
|
--- a/net/ipv4/ip_output.c
|
|
+++ b/net/ipv4/ip_output.c
|
|
@@ -836,7 +836,7 @@ static int __ip_append_data(struct sock
|
|
csummode = CHECKSUM_PARTIAL;
|
|
|
|
cork->length += length;
|
|
- if (((length > mtu) || (skb && skb_is_gso(skb))) &&
|
|
+ if (((length > mtu) || (skb && skb_has_frags(skb))) &&
|
|
(sk->sk_protocol == IPPROTO_UDP) &&
|
|
(rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len) {
|
|
err = ip_ufo_append_data(sk, queue, getfrag, from, length,
|
|
--- a/net/ipv6/ip6_output.c
|
|
+++ b/net/ipv6/ip6_output.c
|
|
@@ -1252,7 +1252,7 @@ int ip6_append_data(struct sock *sk, int
|
|
skb = skb_peek_tail(&sk->sk_write_queue);
|
|
cork->length += length;
|
|
if (((length > mtu) ||
|
|
- (skb && skb_is_gso(skb))) &&
|
|
+ (skb && skb_has_frags(skb))) &&
|
|
(sk->sk_protocol == IPPROTO_UDP) &&
|
|
(rt->dst.dev->features & NETIF_F_UFO)) {
|
|
err = ip6_ufo_append_data(sk, getfrag, from, length,
|