net: sched: ife: handle malformed tlv length
authorAlexander Aring <aring@mojatatu.com>
Fri, 20 Apr 2018 19:15:04 +0000 (15:15 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 29 Apr 2018 09:33:13 +0000 (11:33 +0200)
[ Upstream commit cc74eddd0ff325d57373cea99f642b787d7f76f5 ]

There is currently no handling to check on a invalid tlv length. This
patch adds such handling to avoid killing the kernel with a malformed
ife packet.

Signed-off-by: Alexander Aring <aring@mojatatu.com>
Reviewed-by: Yotam Gigi <yotam.gi@gmail.com>
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/net/ife.h
net/ife/ife.c
net/sched/act_ife.c

index 44b9c00f72232c31d3cf478c901e7b164748bb04..e117617e3c347af5eec412ebd8cbb2271de22b56 100644 (file)
@@ -12,7 +12,8 @@
 void *ife_encode(struct sk_buff *skb, u16 metalen);
 void *ife_decode(struct sk_buff *skb, u16 *metalen);
 
-void *ife_tlv_meta_decode(void *skbdata, u16 *attrtype, u16 *dlen, u16 *totlen);
+void *ife_tlv_meta_decode(void *skbdata, const void *ifehdr_end, u16 *attrtype,
+                         u16 *dlen, u16 *totlen);
 int ife_tlv_meta_encode(void *skbdata, u16 attrtype, u16 dlen,
                        const void *dval);
 
index 7d1ec76e7f433a2525581782a0bf14731548d4a8..7fbe70a0af4becb24204f47dfb47133d2591d44b 100644 (file)
@@ -92,12 +92,43 @@ struct meta_tlvhdr {
        __be16 len;
 };
 
+static bool __ife_tlv_meta_valid(const unsigned char *skbdata,
+                                const unsigned char *ifehdr_end)
+{
+       const struct meta_tlvhdr *tlv;
+       u16 tlvlen;
+
+       if (unlikely(skbdata + sizeof(*tlv) > ifehdr_end))
+               return false;
+
+       tlv = (const struct meta_tlvhdr *)skbdata;
+       tlvlen = ntohs(tlv->len);
+
+       /* tlv length field is inc header, check on minimum */
+       if (tlvlen < NLA_HDRLEN)
+               return false;
+
+       /* overflow by NLA_ALIGN check */
+       if (NLA_ALIGN(tlvlen) < tlvlen)
+               return false;
+
+       if (unlikely(skbdata + NLA_ALIGN(tlvlen) > ifehdr_end))
+               return false;
+
+       return true;
+}
+
 /* Caller takes care of presenting data in network order
  */
-void *ife_tlv_meta_decode(void *skbdata, u16 *attrtype, u16 *dlen, u16 *totlen)
+void *ife_tlv_meta_decode(void *skbdata, const void *ifehdr_end, u16 *attrtype,
+                         u16 *dlen, u16 *totlen)
 {
-       struct meta_tlvhdr *tlv = (struct meta_tlvhdr *) skbdata;
+       struct meta_tlvhdr *tlv;
+
+       if (!__ife_tlv_meta_valid(skbdata, ifehdr_end))
+               return NULL;
 
+       tlv = (struct meta_tlvhdr *)skbdata;
        *dlen = ntohs(tlv->len) - NLA_HDRLEN;
        *attrtype = ntohs(tlv->type);
 
index 4b4e4d490f42c020d773491a45c4e5ecf29aedfe..85757af7f1508522e4dc7468c92cacd402987047 100644 (file)
@@ -639,7 +639,12 @@ static int tcf_ife_decode(struct sk_buff *skb, const struct tc_action *a,
                u16 mtype;
                u16 dlen;
 
-               curr_data = ife_tlv_meta_decode(tlv_data, &mtype, &dlen, NULL);
+               curr_data = ife_tlv_meta_decode(tlv_data, ifehdr_end, &mtype,
+                                               &dlen, NULL);
+               if (!curr_data) {
+                       qstats_drop_inc(this_cpu_ptr(ife->common.cpu_qstats));
+                       return TC_ACT_SHOT;
+               }
 
                if (find_decode_metaid(skb, ife, mtype, dlen, curr_data)) {
                        /* abuse overlimits to count when we receive metadata