cls_u32: use skb_header_pointer() to dereference data safely
authorChangli Gao <xiaosuo@gmail.com>
Wed, 2 Jun 2010 14:32:42 +0000 (07:32 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 2 Jun 2010 14:32:42 +0000 (07:32 -0700)
use skb_header_pointer() to dereference data safely

the original skb->data dereference isn't safe, as there isn't any skb->len or
skb_is_nonlinear() check. skb_header_pointer() is used instead in this patch.
And when the skb isn't long enough, we terminate the function u32_classify()
immediately with -1.

Signed-off-by: Changli Gao <xiaosuo@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/sched/cls_u32.c

index 9627542..4f52214 100644 (file)
@@ -98,11 +98,11 @@ static int u32_classify(struct sk_buff *skb, struct tcf_proto *tp, struct tcf_re
 {
        struct {
                struct tc_u_knode *knode;
-               u8                *ptr;
+               unsigned int      off;
        } stack[TC_U32_MAXDEPTH];
 
        struct tc_u_hnode *ht = (struct tc_u_hnode*)tp->root;
-       u8 *ptr = skb_network_header(skb);
+       unsigned int off = skb_network_offset(skb);
        struct tc_u_knode *n;
        int sdepth = 0;
        int off2 = 0;
@@ -134,8 +134,14 @@ next_knode:
 #endif
 
                for (i = n->sel.nkeys; i>0; i--, key++) {
-
-                       if ((*(__be32*)(ptr+key->off+(off2&key->offmask))^key->val)&key->mask) {
+                       unsigned int toff;
+                       __be32 *data, _data;
+
+                       toff = off + key->off + (off2 & key->offmask);
+                       data = skb_header_pointer(skb, toff, 4, &_data);
+                       if (!data)
+                               goto out;
+                       if ((*data ^ key->val) & key->mask) {
                                n = n->next;
                                goto next_knode;
                        }
@@ -174,29 +180,45 @@ check_terminal:
                if (sdepth >= TC_U32_MAXDEPTH)
                        goto deadloop;
                stack[sdepth].knode = n;
-               stack[sdepth].ptr = ptr;
+               stack[sdepth].off = off;
                sdepth++;
 
                ht = n->ht_down;
                sel = 0;
-               if (ht->divisor)
-                       sel = ht->divisor&u32_hash_fold(*(__be32*)(ptr+n->sel.hoff), &n->sel,n->fshift);
-
+               if (ht->divisor) {
+                       __be32 *data, _data;
+
+                       data = skb_header_pointer(skb, off + n->sel.hoff, 4,
+                                                 &_data);
+                       if (!data)
+                               goto out;
+                       sel = ht->divisor & u32_hash_fold(*data, &n->sel,
+                                                         n->fshift);
+               }
                if (!(n->sel.flags&(TC_U32_VAROFFSET|TC_U32_OFFSET|TC_U32_EAT)))
                        goto next_ht;
 
                if (n->sel.flags&(TC_U32_OFFSET|TC_U32_VAROFFSET)) {
                        off2 = n->sel.off + 3;
-                       if (n->sel.flags&TC_U32_VAROFFSET)
-                               off2 += ntohs(n->sel.offmask & *(__be16*)(ptr+n->sel.offoff)) >>n->sel.offshift;
+                       if (n->sel.flags & TC_U32_VAROFFSET) {
+                               __be16 *data, _data;
+
+                               data = skb_header_pointer(skb,
+                                                         off + n->sel.offoff,
+                                                         2, &_data);
+                               if (!data)
+                                       goto out;
+                               off2 += ntohs(n->sel.offmask & *data) >>
+                                       n->sel.offshift;
+                       }
                        off2 &= ~3;
                }
                if (n->sel.flags&TC_U32_EAT) {
-                       ptr += off2;
+                       off += off2;
                        off2 = 0;
                }
 
-               if (ptr < skb_tail_pointer(skb))
+               if (off < skb->len)
                        goto next_ht;
        }
 
@@ -204,9 +226,10 @@ check_terminal:
        if (sdepth--) {
                n = stack[sdepth].knode;
                ht = n->ht_up;
-               ptr = stack[sdepth].ptr;
+               off = stack[sdepth].off;
                goto check_terminal;
        }
+out:
        return -1;
 
 deadloop: