6lowpan: read data from skb safely
authoralex.bluesman.smirnov@gmail.com <alex.bluesman.smirnov@gmail.com>
Mon, 25 Jun 2012 03:49:01 +0000 (03:49 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 25 Jun 2012 23:40:31 +0000 (16:40 -0700)
Check if skb buffer can pull requested amount of bytes and return
an error in opposite case.

Signed-off-by: Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ieee802154/6lowpan.c

index 32eb4179e8fa6b04165dac2bb3a84ff788c59cff..5c7bcf9663d34af3f83e0230189faae44c899af0 100644 (file)
@@ -291,25 +291,26 @@ lowpan_compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb)
        *hc06_ptr += 2;
 }
 
-static u8 lowpan_fetch_skb_u8(struct sk_buff *skb)
+static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val)
 {
-       u8 ret;
+       if (unlikely(!pskb_may_pull(skb, 1)))
+               return -EINVAL;
 
-       ret = skb->data[0];
+       *val = skb->data[0];
        skb_pull(skb, 1);
 
-       return ret;
+       return 0;
 }
 
-static u16 lowpan_fetch_skb_u16(struct sk_buff *skb)
+static inline int lowpan_fetch_skb_u16(struct sk_buff *skb, u16 *val)
 {
-       u16 ret;
-
-       BUG_ON(!pskb_may_pull(skb, 2));
+       if (unlikely(!pskb_may_pull(skb, 2)))
+               return -EINVAL;
 
-       ret = skb->data[0] | (skb->data[1] << 8);
+       *val = skb->data[0] | (skb->data[1] << 8);
        skb_pull(skb, 2);
-       return ret;
+
+       return 0;
 }
 
 static int
@@ -318,7 +319,8 @@ lowpan_uncompress_udp_header(struct sk_buff *skb)
        struct udphdr *uh = udp_hdr(skb);
        u8 tmp;
 
-       tmp = lowpan_fetch_skb_u8(skb);
+       if (lowpan_fetch_skb_u8(skb, &tmp))
+               goto err;
 
        if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) {
                pr_debug("(%s): UDP header uncompression\n", __func__);
@@ -710,7 +712,9 @@ lowpan_process_data(struct sk_buff *skb)
        /* at least two bytes will be used for the encoding */
        if (skb->len < 2)
                goto drop;
-       iphc0 = lowpan_fetch_skb_u8(skb);
+
+       if (lowpan_fetch_skb_u8(skb, &iphc0))
+               goto drop;
 
        /* fragments assembling */
        switch (iphc0 & LOWPAN_DISPATCH_MASK) {
@@ -722,8 +726,9 @@ lowpan_process_data(struct sk_buff *skb)
                u16 tag;
                bool found = false;
 
-               len = lowpan_fetch_skb_u8(skb); /* frame length */
-               tag = lowpan_fetch_skb_u16(skb);
+               if (lowpan_fetch_skb_u8(skb, &len) || /* frame length */
+                   lowpan_fetch_skb_u16(skb, &tag))  /* fragment tag */
+                       goto drop;
 
                /*
                 * check if frame assembling with the same tag is
@@ -747,7 +752,8 @@ lowpan_process_data(struct sk_buff *skb)
                if ((iphc0 & LOWPAN_DISPATCH_MASK) == LOWPAN_DISPATCH_FRAG1)
                        goto unlock_and_drop;
 
-               offset = lowpan_fetch_skb_u8(skb); /* fetch offset */
+               if (lowpan_fetch_skb_u8(skb, &offset)) /* fetch offset */
+                       goto unlock_and_drop;
 
                /* if payload fits buffer, copy it */
                if (likely((offset * 8 + skb->len) <= frame->length))
@@ -769,7 +775,10 @@ lowpan_process_data(struct sk_buff *skb)
                        dev_kfree_skb(skb);
                        skb = frame->skb;
                        kfree(frame);
-                       iphc0 = lowpan_fetch_skb_u8(skb);
+
+                       if (lowpan_fetch_skb_u8(skb, &iphc0))
+                               goto unlock_and_drop;
+
                        break;
                }
                spin_unlock(&flist_lock);
@@ -780,7 +789,8 @@ lowpan_process_data(struct sk_buff *skb)
                break;
        }
 
-       iphc1 = lowpan_fetch_skb_u8(skb);
+       if (lowpan_fetch_skb_u8(skb, &iphc1))
+               goto drop;
 
        _saddr = mac_cb(skb)->sa.hwaddr;
        _daddr = mac_cb(skb)->da.hwaddr;
@@ -791,9 +801,8 @@ lowpan_process_data(struct sk_buff *skb)
        if (iphc1 & LOWPAN_IPHC_CID) {
                pr_debug("(%s): CID flag is set, increase header with one\n",
                                                                __func__);
-               if (!skb->len)
+               if (lowpan_fetch_skb_u8(skb, &num_context))
                        goto drop;
-               num_context = lowpan_fetch_skb_u8(skb);
        }
 
        hdr.version = 6;
@@ -805,9 +814,9 @@ lowpan_process_data(struct sk_buff *skb)
         * ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)
         */
        case 0: /* 00b */
-               if (!skb->len)
+               if (lowpan_fetch_skb_u8(skb, &tmp))
                        goto drop;
-               tmp = lowpan_fetch_skb_u8(skb);
+
                memcpy(&hdr.flow_lbl, &skb->data[0], 3);
                skb_pull(skb, 3);
                hdr.priority = ((tmp >> 2) & 0x0f);
@@ -819,9 +828,9 @@ lowpan_process_data(struct sk_buff *skb)
         * ECN + DSCP (1 byte), Flow Label is elided
         */
        case 1: /* 10b */
-               if (!skb->len)
+               if (lowpan_fetch_skb_u8(skb, &tmp))
                        goto drop;
-               tmp = lowpan_fetch_skb_u8(skb);
+
                hdr.priority = ((tmp >> 2) & 0x0f);
                hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30);
                hdr.flow_lbl[1] = 0;
@@ -832,9 +841,9 @@ lowpan_process_data(struct sk_buff *skb)
         * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided
         */
        case 2: /* 01b */
-               if (!skb->len)
+               if (lowpan_fetch_skb_u8(skb, &tmp))
                        goto drop;
-               tmp = lowpan_fetch_skb_u8(skb);
+
                hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30);
                memcpy(&hdr.flow_lbl[1], &skb->data[0], 2);
                skb_pull(skb, 2);
@@ -853,9 +862,9 @@ lowpan_process_data(struct sk_buff *skb)
        /* Next Header */
        if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
                /* Next header is carried inline */
-               if (!skb->len)
+               if (lowpan_fetch_skb_u8(skb, &(hdr.nexthdr)))
                        goto drop;
-               hdr.nexthdr = lowpan_fetch_skb_u8(skb);
+
                pr_debug("(%s): NH flag is set, next header is carried "
                         "inline: %02x\n", __func__, hdr.nexthdr);
        }
@@ -864,9 +873,8 @@ lowpan_process_data(struct sk_buff *skb)
        if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I)
                hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03];
        else {
-               if (!skb->len)
+               if (lowpan_fetch_skb_u8(skb, &(hdr.hop_limit)))
                        goto drop;
-               hdr.hop_limit = lowpan_fetch_skb_u8(skb);
        }
 
        /* Extract SAM to the tmp variable */
@@ -894,10 +902,8 @@ lowpan_process_data(struct sk_buff *skb)
                        pr_debug("(%s): destination address non-context-based"
                                 " multicast compression\n", __func__);
                        if (0 < tmp && tmp < 3) {
-                               if (!skb->len)
+                               if (lowpan_fetch_skb_u8(skb, &prefix[1]))
                                        goto drop;
-                               else
-                                       prefix[1] = lowpan_fetch_skb_u8(skb);
                        }
 
                        err = lowpan_uncompress_addr(skb, &hdr.daddr, prefix,