| From 251e28b0a1b1a4e2c3d373610d2becd1a55a9480 Mon Sep 17 00:00:00 2001 |
| From: Sabrina Dubroca <sd@queasysnail.net> |
| Date: Mon, 25 Mar 2019 14:30:00 +0100 |
| Subject: esp4: add length check for UDP encapsulation |
| |
| [ Upstream commit 8dfb4eba4100e7cdd161a8baef2d8d61b7a7e62e ] |
| |
| esp_output_udp_encap can produce a length that doesn't fit in the 16 |
| bits of a UDP header's length field. In that case, we'll send a |
| fragmented packet whose length is larger than IP_MAX_MTU (resulting in |
| "Oversized IP packet" warnings on receive) and with a bogus UDP |
| length. |
| |
| To prevent this, add a length check to esp_output_udp_encap and return |
| -EMSGSIZE on failure. |
| |
| This seems to be older than git history. |
| |
| Signed-off-by: Sabrina Dubroca <sd@queasysnail.net> |
| Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/ipv4/esp4.c | 20 +++++++++++++++----- |
| 1 file changed, 15 insertions(+), 5 deletions(-) |
| |
| diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c |
| index 12a43a5369a54..114f9def1ec54 100644 |
| --- a/net/ipv4/esp4.c |
| +++ b/net/ipv4/esp4.c |
| @@ -223,7 +223,7 @@ static void esp_output_fill_trailer(u8 *tail, int tfclen, int plen, __u8 proto) |
| tail[plen - 1] = proto; |
| } |
| |
| -static void esp_output_udp_encap(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp) |
| +static int esp_output_udp_encap(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp) |
| { |
| int encap_type; |
| struct udphdr *uh; |
| @@ -231,6 +231,7 @@ static void esp_output_udp_encap(struct xfrm_state *x, struct sk_buff *skb, stru |
| __be16 sport, dport; |
| struct xfrm_encap_tmpl *encap = x->encap; |
| struct ip_esp_hdr *esph = esp->esph; |
| + unsigned int len; |
| |
| spin_lock_bh(&x->lock); |
| sport = encap->encap_sport; |
| @@ -238,11 +239,14 @@ static void esp_output_udp_encap(struct xfrm_state *x, struct sk_buff *skb, stru |
| encap_type = encap->encap_type; |
| spin_unlock_bh(&x->lock); |
| |
| + len = skb->len + esp->tailen - skb_transport_offset(skb); |
| + if (len + sizeof(struct iphdr) >= IP_MAX_MTU) |
| + return -EMSGSIZE; |
| + |
| uh = (struct udphdr *)esph; |
| uh->source = sport; |
| uh->dest = dport; |
| - uh->len = htons(skb->len + esp->tailen |
| - - skb_transport_offset(skb)); |
| + uh->len = htons(len); |
| uh->check = 0; |
| |
| switch (encap_type) { |
| @@ -259,6 +263,8 @@ static void esp_output_udp_encap(struct xfrm_state *x, struct sk_buff *skb, stru |
| |
| *skb_mac_header(skb) = IPPROTO_UDP; |
| esp->esph = esph; |
| + |
| + return 0; |
| } |
| |
| int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp) |
| @@ -272,8 +278,12 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * |
| int tailen = esp->tailen; |
| |
| /* this is non-NULL only with UDP Encapsulation */ |
| - if (x->encap) |
| - esp_output_udp_encap(x, skb, esp); |
| + if (x->encap) { |
| + int err = esp_output_udp_encap(x, skb, esp); |
| + |
| + if (err < 0) |
| + return err; |
| + } |
| |
| if (!skb_cloned(skb)) { |
| if (tailen <= skb_tailroom(skb)) { |
| -- |
| 2.20.1 |
| |