esp4: add length check for UDP encapsulation [Linux 4.19.46]

esp4: add length check for UDP encapsulation [Linux 4.19.46]

This Linux kernel change "esp4: add length check for UDP encapsulation" is included in the Linux 4.19.46 release. This change is authored by Sabrina Dubroca <sd [at] queasysnail.net> on Mon Mar 25 14:30:00 2019 +0100. The commit for this change in Linux stable tree is 3716c26 (patch) which is from upstream commit 8dfb4eb. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 8dfb4eb.

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>

There are 20 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 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 12a43a5..114f9de 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)) {

Leave a Reply

Your email address will not be published. Required fields are marked *