Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / net / core / filter.c
index c23543cba132bc19a213ed242e47386e8e41ca65..dad2a178f9f8a477488f091962c5e771d1d117b3 100644 (file)
@@ -348,6 +348,9 @@ load_b:
                case BPF_S_ANC_VLAN_TAG_PRESENT:
                        A = !!vlan_tx_tag_present(skb);
                        continue;
+               case BPF_S_ANC_PAY_OFFSET:
+                       A = __skb_get_poff(skb);
+                       continue;
                case BPF_S_ANC_NLATTR: {
                        struct nlattr *nla;
 
@@ -532,6 +535,7 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
                [BPF_JMP|BPF_JSET|BPF_X] = BPF_S_JMP_JSET_X,
        };
        int pc;
+       bool anc_found;
 
        if (flen == 0 || flen > BPF_MAXINSNS)
                return -EINVAL;
@@ -592,8 +596,10 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
                case BPF_S_LD_W_ABS:
                case BPF_S_LD_H_ABS:
                case BPF_S_LD_B_ABS:
+                       anc_found = false;
 #define ANCILLARY(CODE) case SKF_AD_OFF + SKF_AD_##CODE:       \
                                code = BPF_S_ANC_##CODE;        \
+                               anc_found = true;               \
                                break
                        switch (ftest->k) {
                        ANCILLARY(PROTOCOL);
@@ -609,7 +615,12 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
                        ANCILLARY(ALU_XOR_X);
                        ANCILLARY(VLAN_TAG);
                        ANCILLARY(VLAN_TAG_PRESENT);
+                       ANCILLARY(PAY_OFFSET);
                        }
+
+                       /* ancillary operation unknown or unsupported */
+                       if (anc_found == false && ftest->k >= SKF_AD_OFF)
+                               return -EINVAL;
                }
                ftest->code = code;
        }
@@ -714,6 +725,9 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
        unsigned int fsize = sizeof(struct sock_filter) * fprog->len;
        int err;
 
+       if (sock_flag(sk, SOCK_FILTER_LOCKED))
+               return -EPERM;
+
        /* Make sure new filter is there and in the right amounts. */
        if (fprog->filter == NULL)
                return -EINVAL;
@@ -750,6 +764,9 @@ int sk_detach_filter(struct sock *sk)
        int ret = -ENOENT;
        struct sk_filter *filter;
 
+       if (sock_flag(sk, SOCK_FILTER_LOCKED))
+               return -EPERM;
+
        filter = rcu_dereference_protected(sk->sk_filter,
                                           sock_owned_by_user(sk));
        if (filter) {
@@ -801,6 +818,7 @@ static void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to)
                [BPF_S_ANC_SECCOMP_LD_W] = BPF_LD|BPF_B|BPF_ABS,
                [BPF_S_ANC_VLAN_TAG]    = BPF_LD|BPF_B|BPF_ABS,
                [BPF_S_ANC_VLAN_TAG_PRESENT] = BPF_LD|BPF_B|BPF_ABS,
+               [BPF_S_ANC_PAY_OFFSET]  = BPF_LD|BPF_B|BPF_ABS,
                [BPF_S_LD_W_LEN]        = BPF_LD|BPF_W|BPF_LEN,
                [BPF_S_LD_W_IND]        = BPF_LD|BPF_W|BPF_IND,
                [BPF_S_LD_H_IND]        = BPF_LD|BPF_H|BPF_IND,