]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - net/core/filter.c
arm: tegra: enterprise: Controls for 3d barrier
[linux-2.6.git] / net / core / filter.c
index da69fb728d328f1b47236cebaaea7ae26f3bcd47..36f975fa87cb4975a01e4105c5cc3b0f9d905367 100644 (file)
 #include <asm/uaccess.h>
 #include <asm/unaligned.h>
 #include <linux/filter.h>
+#include <linux/reciprocal_div.h>
+#include <linux/ratelimit.h>
 
 /* No hurry in this branch */
-static void *__load_pointer(struct sk_buff *skb, int k)
+static void *__load_pointer(const struct sk_buff *skb, int k, unsigned int size)
 {
        u8 *ptr = NULL;
 
@@ -48,21 +50,17 @@ static void *__load_pointer(struct sk_buff *skb, int k)
        else if (k >= SKF_LL_OFF)
                ptr = skb_mac_header(skb) + k - SKF_LL_OFF;
 
-       if (ptr >= skb->head && ptr < skb_tail_pointer(skb))
+       if (ptr >= skb->head && ptr + size <= skb_tail_pointer(skb))
                return ptr;
        return NULL;
 }
 
-static inline void *load_pointer(struct sk_buff *skb, int k,
+static inline void *load_pointer(const struct sk_buff *skb, int k,
                                 unsigned int size, void *buffer)
 {
        if (k >= 0)
                return skb_header_pointer(skb, k, size, buffer);
-       else {
-               if (k >= SKF_AD_OFF)
-                       return NULL;
-               return __load_pointer(skb, k);
-       }
+       return __load_pointer(skb, k, size);
 }
 
 /**
@@ -86,14 +84,14 @@ int sk_filter(struct sock *sk, struct sk_buff *skb)
        if (err)
                return err;
 
-       rcu_read_lock_bh();
-       filter = rcu_dereference_bh(sk->sk_filter);
+       rcu_read_lock();
+       filter = rcu_dereference(sk->sk_filter);
        if (filter) {
-               unsigned int pkt_len = sk_run_filter(skb, filter->insns,
-                               filter->len);
+               unsigned int pkt_len = SK_RUN_FILTER(filter, skb);
+
                err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM;
        }
-       rcu_read_unlock_bh();
+       rcu_read_unlock();
 
        return err;
 }
@@ -102,222 +100,222 @@ EXPORT_SYMBOL(sk_filter);
 /**
  *     sk_run_filter - run a filter on a socket
  *     @skb: buffer to run the filter on
- *     @filter: filter to apply
- *     @flen: length of filter
+ *     @fentry: filter to apply
  *
  * Decode and apply filter instructions to the skb->data.
- * Return length to keep, 0 for none. skb is the data we are
- * filtering, filter is the array of filter instructions, and
- * len is the number of filter blocks in the array.
+ * Return length to keep, 0 for none. @skb is the data we are
+ * filtering, @filter is the array of filter instructions.
+ * Because all jumps are guaranteed to be before last instruction,
+ * and last instruction guaranteed to be a RET, we dont need to check
+ * flen. (We used to pass to this function the length of filter)
  */
-unsigned int sk_run_filter(struct sk_buff *skb, struct sock_filter *filter, int flen)
+unsigned int sk_run_filter(const struct sk_buff *skb,
+                          const struct sock_filter *fentry)
 {
-       struct sock_filter *fentry;     /* We walk down these */
        void *ptr;
        u32 A = 0;                      /* Accumulator */
        u32 X = 0;                      /* Index Register */
        u32 mem[BPF_MEMWORDS];          /* Scratch Memory Store */
        u32 tmp;
        int k;
-       int pc;
 
        /*
         * Process array of filter instructions.
         */
-       for (pc = 0; pc < flen; pc++) {
-               fentry = &filter[pc];
+       for (;; fentry++) {
+#if defined(CONFIG_X86_32)
+#define        K (fentry->k)
+#else
+               const u32 K = fentry->k;
+#endif
 
                switch (fentry->code) {
-               case BPF_ALU|BPF_ADD|BPF_X:
+               case BPF_S_ALU_ADD_X:
                        A += X;
                        continue;
-               case BPF_ALU|BPF_ADD|BPF_K:
-                       A += fentry->k;
+               case BPF_S_ALU_ADD_K:
+                       A += K;
                        continue;
-               case BPF_ALU|BPF_SUB|BPF_X:
+               case BPF_S_ALU_SUB_X:
                        A -= X;
                        continue;
-               case BPF_ALU|BPF_SUB|BPF_K:
-                       A -= fentry->k;
+               case BPF_S_ALU_SUB_K:
+                       A -= K;
                        continue;
-               case BPF_ALU|BPF_MUL|BPF_X:
+               case BPF_S_ALU_MUL_X:
                        A *= X;
                        continue;
-               case BPF_ALU|BPF_MUL|BPF_K:
-                       A *= fentry->k;
+               case BPF_S_ALU_MUL_K:
+                       A *= K;
                        continue;
-               case BPF_ALU|BPF_DIV|BPF_X:
+               case BPF_S_ALU_DIV_X:
                        if (X == 0)
                                return 0;
                        A /= X;
                        continue;
-               case BPF_ALU|BPF_DIV|BPF_K:
-                       A /= fentry->k;
+               case BPF_S_ALU_DIV_K:
+                       A = reciprocal_divide(A, K);
                        continue;
-               case BPF_ALU|BPF_AND|BPF_X:
+               case BPF_S_ALU_AND_X:
                        A &= X;
                        continue;
-               case BPF_ALU|BPF_AND|BPF_K:
-                       A &= fentry->k;
+               case BPF_S_ALU_AND_K:
+                       A &= K;
                        continue;
-               case BPF_ALU|BPF_OR|BPF_X:
+               case BPF_S_ALU_OR_X:
                        A |= X;
                        continue;
-               case BPF_ALU|BPF_OR|BPF_K:
-                       A |= fentry->k;
+               case BPF_S_ALU_OR_K:
+                       A |= K;
                        continue;
-               case BPF_ALU|BPF_LSH|BPF_X:
+               case BPF_S_ALU_LSH_X:
                        A <<= X;
                        continue;
-               case BPF_ALU|BPF_LSH|BPF_K:
-                       A <<= fentry->k;
+               case BPF_S_ALU_LSH_K:
+                       A <<= K;
                        continue;
-               case BPF_ALU|BPF_RSH|BPF_X:
+               case BPF_S_ALU_RSH_X:
                        A >>= X;
                        continue;
-               case BPF_ALU|BPF_RSH|BPF_K:
-                       A >>= fentry->k;
+               case BPF_S_ALU_RSH_K:
+                       A >>= K;
                        continue;
-               case BPF_ALU|BPF_NEG:
+               case BPF_S_ALU_NEG:
                        A = -A;
                        continue;
-               case BPF_JMP|BPF_JA:
-                       pc += fentry->k;
+               case BPF_S_JMP_JA:
+                       fentry += K;
                        continue;
-               case BPF_JMP|BPF_JGT|BPF_K:
-                       pc += (A > fentry->k) ? fentry->jt : fentry->jf;
+               case BPF_S_JMP_JGT_K:
+                       fentry += (A > K) ? fentry->jt : fentry->jf;
                        continue;
-               case BPF_JMP|BPF_JGE|BPF_K:
-                       pc += (A >= fentry->k) ? fentry->jt : fentry->jf;
+               case BPF_S_JMP_JGE_K:
+                       fentry += (A >= K) ? fentry->jt : fentry->jf;
                        continue;
-               case BPF_JMP|BPF_JEQ|BPF_K:
-                       pc += (A == fentry->k) ? fentry->jt : fentry->jf;
+               case BPF_S_JMP_JEQ_K:
+                       fentry += (A == K) ? fentry->jt : fentry->jf;
                        continue;
-               case BPF_JMP|BPF_JSET|BPF_K:
-                       pc += (A & fentry->k) ? fentry->jt : fentry->jf;
+               case BPF_S_JMP_JSET_K:
+                       fentry += (A & K) ? fentry->jt : fentry->jf;
                        continue;
-               case BPF_JMP|BPF_JGT|BPF_X:
-                       pc += (A > X) ? fentry->jt : fentry->jf;
+               case BPF_S_JMP_JGT_X:
+                       fentry += (A > X) ? fentry->jt : fentry->jf;
                        continue;
-               case BPF_JMP|BPF_JGE|BPF_X:
-                       pc += (A >= X) ? fentry->jt : fentry->jf;
+               case BPF_S_JMP_JGE_X:
+                       fentry += (A >= X) ? fentry->jt : fentry->jf;
                        continue;
-               case BPF_JMP|BPF_JEQ|BPF_X:
-                       pc += (A == X) ? fentry->jt : fentry->jf;
+               case BPF_S_JMP_JEQ_X:
+                       fentry += (A == X) ? fentry->jt : fentry->jf;
                        continue;
-               case BPF_JMP|BPF_JSET|BPF_X:
-                       pc += (A & X) ? fentry->jt : fentry->jf;
+               case BPF_S_JMP_JSET_X:
+                       fentry += (A & X) ? fentry->jt : fentry->jf;
                        continue;
-               case BPF_LD|BPF_W|BPF_ABS:
-                       k = fentry->k;
+               case BPF_S_LD_W_ABS:
+                       k = K;
 load_w:
                        ptr = load_pointer(skb, k, 4, &tmp);
                        if (ptr != NULL) {
                                A = get_unaligned_be32(ptr);
                                continue;
                        }
-                       break;
-               case BPF_LD|BPF_H|BPF_ABS:
-                       k = fentry->k;
+                       return 0;
+               case BPF_S_LD_H_ABS:
+                       k = K;
 load_h:
                        ptr = load_pointer(skb, k, 2, &tmp);
                        if (ptr != NULL) {
                                A = get_unaligned_be16(ptr);
                                continue;
                        }
-                       break;
-               case BPF_LD|BPF_B|BPF_ABS:
-                       k = fentry->k;
+                       return 0;
+               case BPF_S_LD_B_ABS:
+                       k = K;
 load_b:
                        ptr = load_pointer(skb, k, 1, &tmp);
                        if (ptr != NULL) {
                                A = *(u8 *)ptr;
                                continue;
                        }
-                       break;
-               case BPF_LD|BPF_W|BPF_LEN:
+                       return 0;
+               case BPF_S_LD_W_LEN:
                        A = skb->len;
                        continue;
-               case BPF_LDX|BPF_W|BPF_LEN:
+               case BPF_S_LDX_W_LEN:
                        X = skb->len;
                        continue;
-               case BPF_LD|BPF_W|BPF_IND:
-                       k = X + fentry->k;
+               case BPF_S_LD_W_IND:
+                       k = X + K;
                        goto load_w;
-               case BPF_LD|BPF_H|BPF_IND:
-                       k = X + fentry->k;
+               case BPF_S_LD_H_IND:
+                       k = X + K;
                        goto load_h;
-               case BPF_LD|BPF_B|BPF_IND:
-                       k = X + fentry->k;
+               case BPF_S_LD_B_IND:
+                       k = X + K;
                        goto load_b;
-               case BPF_LDX|BPF_B|BPF_MSH:
-                       ptr = load_pointer(skb, fentry->k, 1, &tmp);
+               case BPF_S_LDX_B_MSH:
+                       ptr = load_pointer(skb, K, 1, &tmp);
                        if (ptr != NULL) {
                                X = (*(u8 *)ptr & 0xf) << 2;
                                continue;
                        }
                        return 0;
-               case BPF_LD|BPF_IMM:
-                       A = fentry->k;
+               case BPF_S_LD_IMM:
+                       A = K;
                        continue;
-               case BPF_LDX|BPF_IMM:
-                       X = fentry->k;
+               case BPF_S_LDX_IMM:
+                       X = K;
                        continue;
-               case BPF_LD|BPF_MEM:
-                       A = mem[fentry->k];
+               case BPF_S_LD_MEM:
+                       A = mem[K];
                        continue;
-               case BPF_LDX|BPF_MEM:
-                       X = mem[fentry->k];
+               case BPF_S_LDX_MEM:
+                       X = mem[K];
                        continue;
-               case BPF_MISC|BPF_TAX:
+               case BPF_S_MISC_TAX:
                        X = A;
                        continue;
-               case BPF_MISC|BPF_TXA:
+               case BPF_S_MISC_TXA:
                        A = X;
                        continue;
-               case BPF_RET|BPF_K:
-                       return fentry->k;
-               case BPF_RET|BPF_A:
+               case BPF_S_RET_K:
+                       return K;
+               case BPF_S_RET_A:
                        return A;
-               case BPF_ST:
-                       mem[fentry->k] = A;
+               case BPF_S_ST:
+                       mem[K] = A;
                        continue;
-               case BPF_STX:
-                       mem[fentry->k] = X;
+               case BPF_S_STX:
+                       mem[K] = X;
                        continue;
-               default:
-                       WARN_ON(1);
-                       return 0;
-               }
-
-               /*
-                * Handle ancillary data, which are impossible
-                * (or very difficult) to get parsing packet contents.
-                */
-               switch (k-SKF_AD_OFF) {
-               case SKF_AD_PROTOCOL:
+               case BPF_S_ANC_PROTOCOL:
                        A = ntohs(skb->protocol);
                        continue;
-               case SKF_AD_PKTTYPE:
+               case BPF_S_ANC_PKTTYPE:
                        A = skb->pkt_type;
                        continue;
-               case SKF_AD_IFINDEX:
+               case BPF_S_ANC_IFINDEX:
                        if (!skb->dev)
                                return 0;
                        A = skb->dev->ifindex;
                        continue;
-               case SKF_AD_MARK:
+               case BPF_S_ANC_MARK:
                        A = skb->mark;
                        continue;
-               case SKF_AD_QUEUE:
+               case BPF_S_ANC_QUEUE:
                        A = skb->queue_mapping;
                        continue;
-               case SKF_AD_HATYPE:
+               case BPF_S_ANC_HATYPE:
                        if (!skb->dev)
                                return 0;
                        A = skb->dev->type;
                        continue;
-               case SKF_AD_NLATTR: {
+               case BPF_S_ANC_RXHASH:
+                       A = skb->rxhash;
+                       continue;
+               case BPF_S_ANC_CPU:
+                       A = raw_smp_processor_id();
+                       continue;
+               case BPF_S_ANC_NLATTR: {
                        struct nlattr *nla;
 
                        if (skb_is_nonlinear(skb))
@@ -333,7 +331,7 @@ load_b:
                                A = 0;
                        continue;
                }
-               case SKF_AD_NLATTR_NEST: {
+               case BPF_S_ANC_NLATTR_NEST: {
                        struct nlattr *nla;
 
                        if (skb_is_nonlinear(skb))
@@ -353,6 +351,9 @@ load_b:
                        continue;
                }
                default:
+                       WARN_RATELIMIT(1, "Unknown code:%u jt:%u tf:%u k:%u\n",
+                                      fentry->code, fentry->jt,
+                                      fentry->jf, fentry->k);
                        return 0;
                }
        }
@@ -361,6 +362,66 @@ load_b:
 }
 EXPORT_SYMBOL(sk_run_filter);
 
+/*
+ * Security :
+ * A BPF program is able to use 16 cells of memory to store intermediate
+ * values (check u32 mem[BPF_MEMWORDS] in sk_run_filter())
+ * As we dont want to clear mem[] array for each packet going through
+ * sk_run_filter(), we check that filter loaded by user never try to read
+ * a cell if not previously written, and we check all branches to be sure
+ * a malicious user doesn't try to abuse us.
+ */
+static int check_load_and_stores(struct sock_filter *filter, int flen)
+{
+       u16 *masks, memvalid = 0; /* one bit per cell, 16 cells */
+       int pc, ret = 0;
+
+       BUILD_BUG_ON(BPF_MEMWORDS > 16);
+       masks = kmalloc(flen * sizeof(*masks), GFP_KERNEL);
+       if (!masks)
+               return -ENOMEM;
+       memset(masks, 0xff, flen * sizeof(*masks));
+
+       for (pc = 0; pc < flen; pc++) {
+               memvalid &= masks[pc];
+
+               switch (filter[pc].code) {
+               case BPF_S_ST:
+               case BPF_S_STX:
+                       memvalid |= (1 << filter[pc].k);
+                       break;
+               case BPF_S_LD_MEM:
+               case BPF_S_LDX_MEM:
+                       if (!(memvalid & (1 << filter[pc].k))) {
+                               ret = -EINVAL;
+                               goto error;
+                       }
+                       break;
+               case BPF_S_JMP_JA:
+                       /* a jump must set masks on target */
+                       masks[pc + 1 + filter[pc].k] &= memvalid;
+                       memvalid = ~0;
+                       break;
+               case BPF_S_JMP_JEQ_K:
+               case BPF_S_JMP_JEQ_X:
+               case BPF_S_JMP_JGE_K:
+               case BPF_S_JMP_JGE_X:
+               case BPF_S_JMP_JGT_K:
+               case BPF_S_JMP_JGT_X:
+               case BPF_S_JMP_JSET_X:
+               case BPF_S_JMP_JSET_K:
+                       /* a jump must set masks on targets */
+                       masks[pc + 1 + filter[pc].jt] &= memvalid;
+                       masks[pc + 1 + filter[pc].jf] &= memvalid;
+                       memvalid = ~0;
+                       break;
+               }
+       }
+error:
+       kfree(masks);
+       return ret;
+}
+
 /**
  *     sk_chk_filter - verify socket filter code
  *     @filter: filter to verify
@@ -377,7 +438,57 @@ EXPORT_SYMBOL(sk_run_filter);
  */
 int sk_chk_filter(struct sock_filter *filter, int flen)
 {
-       struct sock_filter *ftest;
+       /*
+        * Valid instructions are initialized to non-0.
+        * Invalid instructions are initialized to 0.
+        */
+       static const u8 codes[] = {
+               [BPF_ALU|BPF_ADD|BPF_K]  = BPF_S_ALU_ADD_K,
+               [BPF_ALU|BPF_ADD|BPF_X]  = BPF_S_ALU_ADD_X,
+               [BPF_ALU|BPF_SUB|BPF_K]  = BPF_S_ALU_SUB_K,
+               [BPF_ALU|BPF_SUB|BPF_X]  = BPF_S_ALU_SUB_X,
+               [BPF_ALU|BPF_MUL|BPF_K]  = BPF_S_ALU_MUL_K,
+               [BPF_ALU|BPF_MUL|BPF_X]  = BPF_S_ALU_MUL_X,
+               [BPF_ALU|BPF_DIV|BPF_X]  = BPF_S_ALU_DIV_X,
+               [BPF_ALU|BPF_AND|BPF_K]  = BPF_S_ALU_AND_K,
+               [BPF_ALU|BPF_AND|BPF_X]  = BPF_S_ALU_AND_X,
+               [BPF_ALU|BPF_OR|BPF_K]   = BPF_S_ALU_OR_K,
+               [BPF_ALU|BPF_OR|BPF_X]   = BPF_S_ALU_OR_X,
+               [BPF_ALU|BPF_LSH|BPF_K]  = BPF_S_ALU_LSH_K,
+               [BPF_ALU|BPF_LSH|BPF_X]  = BPF_S_ALU_LSH_X,
+               [BPF_ALU|BPF_RSH|BPF_K]  = BPF_S_ALU_RSH_K,
+               [BPF_ALU|BPF_RSH|BPF_X]  = BPF_S_ALU_RSH_X,
+               [BPF_ALU|BPF_NEG]        = BPF_S_ALU_NEG,
+               [BPF_LD|BPF_W|BPF_ABS]   = BPF_S_LD_W_ABS,
+               [BPF_LD|BPF_H|BPF_ABS]   = BPF_S_LD_H_ABS,
+               [BPF_LD|BPF_B|BPF_ABS]   = BPF_S_LD_B_ABS,
+               [BPF_LD|BPF_W|BPF_LEN]   = BPF_S_LD_W_LEN,
+               [BPF_LD|BPF_W|BPF_IND]   = BPF_S_LD_W_IND,
+               [BPF_LD|BPF_H|BPF_IND]   = BPF_S_LD_H_IND,
+               [BPF_LD|BPF_B|BPF_IND]   = BPF_S_LD_B_IND,
+               [BPF_LD|BPF_IMM]         = BPF_S_LD_IMM,
+               [BPF_LDX|BPF_W|BPF_LEN]  = BPF_S_LDX_W_LEN,
+               [BPF_LDX|BPF_B|BPF_MSH]  = BPF_S_LDX_B_MSH,
+               [BPF_LDX|BPF_IMM]        = BPF_S_LDX_IMM,
+               [BPF_MISC|BPF_TAX]       = BPF_S_MISC_TAX,
+               [BPF_MISC|BPF_TXA]       = BPF_S_MISC_TXA,
+               [BPF_RET|BPF_K]          = BPF_S_RET_K,
+               [BPF_RET|BPF_A]          = BPF_S_RET_A,
+               [BPF_ALU|BPF_DIV|BPF_K]  = BPF_S_ALU_DIV_K,
+               [BPF_LD|BPF_MEM]         = BPF_S_LD_MEM,
+               [BPF_LDX|BPF_MEM]        = BPF_S_LDX_MEM,
+               [BPF_ST]                 = BPF_S_ST,
+               [BPF_STX]                = BPF_S_STX,
+               [BPF_JMP|BPF_JA]         = BPF_S_JMP_JA,
+               [BPF_JMP|BPF_JEQ|BPF_K]  = BPF_S_JMP_JEQ_K,
+               [BPF_JMP|BPF_JEQ|BPF_X]  = BPF_S_JMP_JEQ_X,
+               [BPF_JMP|BPF_JGE|BPF_K]  = BPF_S_JMP_JGE_K,
+               [BPF_JMP|BPF_JGE|BPF_X]  = BPF_S_JMP_JGE_X,
+               [BPF_JMP|BPF_JGT|BPF_K]  = BPF_S_JMP_JGT_K,
+               [BPF_JMP|BPF_JGT|BPF_X]  = BPF_S_JMP_JGT_X,
+               [BPF_JMP|BPF_JSET|BPF_K] = BPF_S_JMP_JSET_K,
+               [BPF_JMP|BPF_JSET|BPF_X] = BPF_S_JMP_JSET_X,
+       };
        int pc;
 
        if (flen == 0 || flen > BPF_MAXINSNS)
@@ -385,61 +496,31 @@ int sk_chk_filter(struct sock_filter *filter, int flen)
 
        /* check the filter code now */
        for (pc = 0; pc < flen; pc++) {
-               ftest = &filter[pc];
-
-               /* Only allow valid instructions */
-               switch (ftest->code) {
-               case BPF_ALU|BPF_ADD|BPF_K:
-               case BPF_ALU|BPF_ADD|BPF_X:
-               case BPF_ALU|BPF_SUB|BPF_K:
-               case BPF_ALU|BPF_SUB|BPF_X:
-               case BPF_ALU|BPF_MUL|BPF_K:
-               case BPF_ALU|BPF_MUL|BPF_X:
-               case BPF_ALU|BPF_DIV|BPF_X:
-               case BPF_ALU|BPF_AND|BPF_K:
-               case BPF_ALU|BPF_AND|BPF_X:
-               case BPF_ALU|BPF_OR|BPF_K:
-               case BPF_ALU|BPF_OR|BPF_X:
-               case BPF_ALU|BPF_LSH|BPF_K:
-               case BPF_ALU|BPF_LSH|BPF_X:
-               case BPF_ALU|BPF_RSH|BPF_K:
-               case BPF_ALU|BPF_RSH|BPF_X:
-               case BPF_ALU|BPF_NEG:
-               case BPF_LD|BPF_W|BPF_ABS:
-               case BPF_LD|BPF_H|BPF_ABS:
-               case BPF_LD|BPF_B|BPF_ABS:
-               case BPF_LD|BPF_W|BPF_LEN:
-               case BPF_LD|BPF_W|BPF_IND:
-               case BPF_LD|BPF_H|BPF_IND:
-               case BPF_LD|BPF_B|BPF_IND:
-               case BPF_LD|BPF_IMM:
-               case BPF_LDX|BPF_W|BPF_LEN:
-               case BPF_LDX|BPF_B|BPF_MSH:
-               case BPF_LDX|BPF_IMM:
-               case BPF_MISC|BPF_TAX:
-               case BPF_MISC|BPF_TXA:
-               case BPF_RET|BPF_K:
-               case BPF_RET|BPF_A:
-                       break;
+               struct sock_filter *ftest = &filter[pc];
+               u16 code = ftest->code;
 
+               if (code >= ARRAY_SIZE(codes))
+                       return -EINVAL;
+               code = codes[code];
+               if (!code)
+                       return -EINVAL;
                /* Some instructions need special checks */
-
-               case BPF_ALU|BPF_DIV|BPF_K:
+               switch (code) {
+               case BPF_S_ALU_DIV_K:
                        /* check for division by zero */
                        if (ftest->k == 0)
                                return -EINVAL;
+                       ftest->k = reciprocal_value(ftest->k);
                        break;
-
-               case BPF_LD|BPF_MEM:
-               case BPF_LDX|BPF_MEM:
-               case BPF_ST:
-               case BPF_STX:
+               case BPF_S_LD_MEM:
+               case BPF_S_LDX_MEM:
+               case BPF_S_ST:
+               case BPF_S_STX:
                        /* check for invalid memory addresses */
                        if (ftest->k >= BPF_MEMWORDS)
                                return -EINVAL;
                        break;
-
-               case BPF_JMP|BPF_JA:
+               case BPF_S_JMP_JA:
                        /*
                         * Note, the large ftest->k might cause loops.
                         * Compare this with conditional jumps below,
@@ -448,48 +529,63 @@ int sk_chk_filter(struct sock_filter *filter, int flen)
                        if (ftest->k >= (unsigned)(flen-pc-1))
                                return -EINVAL;
                        break;
-
-               case BPF_JMP|BPF_JEQ|BPF_K:
-               case BPF_JMP|BPF_JEQ|BPF_X:
-               case BPF_JMP|BPF_JGE|BPF_K:
-               case BPF_JMP|BPF_JGE|BPF_X:
-               case BPF_JMP|BPF_JGT|BPF_K:
-               case BPF_JMP|BPF_JGT|BPF_X:
-               case BPF_JMP|BPF_JSET|BPF_K:
-               case BPF_JMP|BPF_JSET|BPF_X:
+               case BPF_S_JMP_JEQ_K:
+               case BPF_S_JMP_JEQ_X:
+               case BPF_S_JMP_JGE_K:
+               case BPF_S_JMP_JGE_X:
+               case BPF_S_JMP_JGT_K:
+               case BPF_S_JMP_JGT_X:
+               case BPF_S_JMP_JSET_X:
+               case BPF_S_JMP_JSET_K:
                        /* for conditionals both must be safe */
                        if (pc + ftest->jt + 1 >= flen ||
                            pc + ftest->jf + 1 >= flen)
                                return -EINVAL;
                        break;
-
-               default:
-                       return -EINVAL;
+               case BPF_S_LD_W_ABS:
+               case BPF_S_LD_H_ABS:
+               case BPF_S_LD_B_ABS:
+#define ANCILLARY(CODE) case SKF_AD_OFF + SKF_AD_##CODE:       \
+                               code = BPF_S_ANC_##CODE;        \
+                               break
+                       switch (ftest->k) {
+                       ANCILLARY(PROTOCOL);
+                       ANCILLARY(PKTTYPE);
+                       ANCILLARY(IFINDEX);
+                       ANCILLARY(NLATTR);
+                       ANCILLARY(NLATTR_NEST);
+                       ANCILLARY(MARK);
+                       ANCILLARY(QUEUE);
+                       ANCILLARY(HATYPE);
+                       ANCILLARY(RXHASH);
+                       ANCILLARY(CPU);
+                       }
                }
+               ftest->code = code;
        }
 
-       return (BPF_CLASS(filter[flen - 1].code) == BPF_RET) ? 0 : -EINVAL;
+       /* last instruction must be a RET code */
+       switch (filter[flen - 1].code) {
+       case BPF_S_RET_K:
+       case BPF_S_RET_A:
+               return check_load_and_stores(filter, flen);
+       }
+       return -EINVAL;
 }
 EXPORT_SYMBOL(sk_chk_filter);
 
 /**
- *     sk_filter_rcu_release: Release a socket filter by rcu_head
+ *     sk_filter_release_rcu - Release a socket filter by rcu_head
  *     @rcu: rcu_head that contains the sk_filter to free
  */
-static void sk_filter_rcu_release(struct rcu_head *rcu)
+void sk_filter_release_rcu(struct rcu_head *rcu)
 {
        struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu);
 
-       sk_filter_release(fp);
-}
-
-static void sk_filter_delayed_uncharge(struct sock *sk, struct sk_filter *fp)
-{
-       unsigned int size = sk_filter_len(fp);
-
-       atomic_sub(size, &sk->sk_omem_alloc);
-       call_rcu_bh(&fp->rcu, sk_filter_rcu_release);
+       bpf_jit_free(fp);
+       kfree(fp);
 }
+EXPORT_SYMBOL(sk_filter_release_rcu);
 
 /**
  *     sk_attach_filter - attach a socket filter
@@ -521,6 +617,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
 
        atomic_set(&fp->refcnt, 1);
        fp->len = fprog->len;
+       fp->bpf_func = sk_run_filter;
 
        err = sk_chk_filter(fp->insns, fp->len);
        if (err) {
@@ -528,13 +625,14 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
                return err;
        }
 
-       rcu_read_lock_bh();
-       old_fp = rcu_dereference_bh(sk->sk_filter);
+       bpf_jit_compile(fp);
+
+       old_fp = rcu_dereference_protected(sk->sk_filter,
+                                          sock_owned_by_user(sk));
        rcu_assign_pointer(sk->sk_filter, fp);
-       rcu_read_unlock_bh();
 
        if (old_fp)
-               sk_filter_delayed_uncharge(sk, old_fp);
+               sk_filter_uncharge(sk, old_fp);
        return 0;
 }
 EXPORT_SYMBOL_GPL(sk_attach_filter);
@@ -544,14 +642,13 @@ int sk_detach_filter(struct sock *sk)
        int ret = -ENOENT;
        struct sk_filter *filter;
 
-       rcu_read_lock_bh();
-       filter = rcu_dereference_bh(sk->sk_filter);
+       filter = rcu_dereference_protected(sk->sk_filter,
+                                          sock_owned_by_user(sk));
        if (filter) {
                rcu_assign_pointer(sk->sk_filter, NULL);
-               sk_filter_delayed_uncharge(sk, filter);
+               sk_filter_uncharge(sk, filter);
                ret = 0;
        }
-       rcu_read_unlock_bh();
        return ret;
 }
 EXPORT_SYMBOL_GPL(sk_detach_filter);