import PULS_20180308
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / ipv4 / netfilter / ip_tables.c
index d23118d95ff9291401396953d094391db0e2e44e..e6cd22262ed18fe77920c1bdbdfbf346db9d8656 100644 (file)
@@ -168,11 +168,12 @@ get_entry(const void *base, unsigned int offset)
 
 /* All zeroes == unconditional rule. */
 /* Mildly perf critical (only if packet tracing is on) */
-static inline bool unconditional(const struct ipt_ip *ip)
+static inline bool unconditional(const struct ipt_entry *e)
 {
        static const struct ipt_ip uncond;
 
-       return memcmp(ip, &uncond, sizeof(uncond)) == 0;
+       return e->target_offset == sizeof(struct ipt_entry) &&
+               memcmp(&e->ip, &uncond, sizeof(uncond)) == 0;
 #undef FWINV
 }
 
@@ -229,11 +230,10 @@ get_chainname_rulenum(const struct ipt_entry *s, const struct ipt_entry *e,
        } else if (s == e) {
                (*rulenum)++;
 
-               if (s->target_offset == sizeof(struct ipt_entry) &&
+               if (unconditional(s) &&
                    strcmp(t->target.u.kernel.target->name,
                           XT_STANDARD_TARGET) == 0 &&
-                  t->verdict < 0 &&
-                  unconditional(&s->ip)) {
+                  t->verdict < 0) {
                        /* Tail of chains: STANDARD target (return/policy) */
                        *comment = *chainname == hookname
                                ? comments[NF_IP_TRACE_COMMENT_POLICY]
@@ -467,11 +467,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
                        e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
 
                        /* Unconditional return/END. */
-                       if ((e->target_offset == sizeof(struct ipt_entry) &&
+                       if ((unconditional(e) &&
                             (strcmp(t->target.u.user.name,
                                     XT_STANDARD_TARGET) == 0) &&
-                            t->verdict < 0 && unconditional(&e->ip)) ||
-                           visited) {
+                            t->verdict < 0) || visited) {
                                unsigned int oldpos, size;
 
                                if ((strcmp(t->target.u.user.name,
@@ -560,14 +559,12 @@ static void cleanup_match(struct xt_entry_match *m, struct net *net)
 }
 
 static int
-check_entry(const struct ipt_entry *e, const char *name)
+check_entry(const struct ipt_entry *e)
 {
        const struct xt_entry_target *t;
 
-       if (!ip_checkentry(&e->ip)) {
-               duprintf("ip check failed %p %s.\n", e, name);
+       if (!ip_checkentry(&e->ip))
                return -EINVAL;
-       }
 
        if (e->target_offset + sizeof(struct xt_entry_target) >
            e->next_offset)
@@ -580,6 +577,7 @@ check_entry(const struct ipt_entry *e, const char *name)
        return 0;
 }
 
+
 static int
 check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
 {
@@ -657,10 +655,6 @@ find_check_entry(struct ipt_entry *e, struct net *net, const char *name,
        struct xt_mtchk_param mtpar;
        struct xt_entry_match *ematch;
 
-       ret = check_entry(e, name);
-       if (ret)
-               return ret;
-
        j = 0;
        mtpar.net       = net;
        mtpar.table     = name;
@@ -704,7 +698,7 @@ static bool check_underflow(const struct ipt_entry *e)
        const struct xt_entry_target *t;
        unsigned int verdict;
 
-       if (!unconditional(&e->ip))
+       if (!unconditional(e))
                return false;
        t = ipt_get_target_c(e);
        if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
@@ -724,9 +718,11 @@ check_entry_size_and_hooks(struct ipt_entry *e,
                           unsigned int valid_hooks)
 {
        unsigned int h;
+       int err;
 
        if ((unsigned long)e % __alignof__(struct ipt_entry) != 0 ||
-           (unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
+           (unsigned char *)e + sizeof(struct ipt_entry) >= limit ||
+           (unsigned char *)e + e->next_offset > limit) {
                duprintf("Bad offset %p\n", e);
                return -EINVAL;
        }
@@ -738,6 +734,10 @@ check_entry_size_and_hooks(struct ipt_entry *e,
                return -EINVAL;
        }
 
+       err = check_entry(e);
+       if (err)
+               return err;
+
        /* Check hooks & underflows */
        for (h = 0; h < NF_INET_NUMHOOKS; h++) {
                if (!(valid_hooks & (1 << h)))
@@ -1226,8 +1226,10 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks,
 
        xt_free_table_info(oldinfo);
        if (copy_to_user(counters_ptr, counters,
-                        sizeof(struct xt_counters) * num_counters) != 0)
-               ret = -EFAULT;
+                        sizeof(struct xt_counters) * num_counters) != 0) {
+               /* Silent error, can't fail, new table is already in place */
+               net_warn_ratelimited("iptables: counters copy to user failed while replacing table\n");
+       }
        vfree(counters);
        xt_table_unlock(t);
        return ret;
@@ -1483,7 +1485,8 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
 
        duprintf("check_compat_entry_size_and_hooks %p\n", e);
        if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 ||
-           (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) {
+           (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit ||
+           (unsigned char *)e + e->next_offset > limit) {
                duprintf("Bad offset %p, limit = %p\n", e, limit);
                return -EINVAL;
        }
@@ -1496,7 +1499,7 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
        }
 
        /* For purposes of check_entry casting the compat entry is fine */
-       ret = check_entry((struct ipt_entry *)e, name);
+       ret = check_entry((struct ipt_entry *)e);
        if (ret)
                return ret;