]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
netfilter: x_tables: introduce and use xt_copy_counters_from_user
authorFlorian Westphal <fw@strlen.de>
Fri, 1 Apr 2016 13:37:59 +0000 (15:37 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 24 Jun 2016 17:18:24 +0000 (10:18 -0700)
commit d7591f0c41ce3e67600a982bab6989ef0f07b3ce upstream.

The three variants use same copy&pasted code, condense this into a
helper and use that.

Make sure info.name is 0-terminated.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/linux/netfilter/x_tables.h
net/ipv4/netfilter/arp_tables.c
net/ipv4/netfilter/ip_tables.c
net/ipv6/netfilter/ip6_tables.c
net/netfilter/x_tables.c

index 67de5ac4029b706342f5a448d0309e52f3ae3e34..04078e8a4803aad2bb638386126a9dd5070f6f3d 100644 (file)
@@ -248,6 +248,9 @@ int xt_check_match(struct xt_mtchk_param *, unsigned int size, u_int8_t proto,
 int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto,
                    bool inv_proto);
 
+void *xt_copy_counters_from_user(const void __user *user, unsigned int len,
+                                struct xt_counters_info *info, bool compat);
+
 struct xt_table *xt_register_table(struct net *net,
                                   const struct xt_table *table,
                                   struct xt_table_info *bootstrap,
index 5158846b55ef00fdcec02b78c764c71f9ec1e6c9..6e3e0e8b1ce3254a96220a82f40dc2a36703cc6f 100644 (file)
@@ -1130,55 +1130,17 @@ static int do_add_counters(struct net *net, const void __user *user,
        unsigned int i;
        struct xt_counters_info tmp;
        struct xt_counters *paddc;
-       unsigned int num_counters;
-       const char *name;
-       int size;
-       void *ptmp;
        struct xt_table *t;
        const struct xt_table_info *private;
        int ret = 0;
        struct arpt_entry *iter;
        unsigned int addend;
-#ifdef CONFIG_COMPAT
-       struct compat_xt_counters_info compat_tmp;
-
-       if (compat) {
-               ptmp = &compat_tmp;
-               size = sizeof(struct compat_xt_counters_info);
-       } else
-#endif
-       {
-               ptmp = &tmp;
-               size = sizeof(struct xt_counters_info);
-       }
 
-       if (copy_from_user(ptmp, user, size) != 0)
-               return -EFAULT;
-
-#ifdef CONFIG_COMPAT
-       if (compat) {
-               num_counters = compat_tmp.num_counters;
-               name = compat_tmp.name;
-       } else
-#endif
-       {
-               num_counters = tmp.num_counters;
-               name = tmp.name;
-       }
-
-       if (len != size + num_counters * sizeof(struct xt_counters))
-               return -EINVAL;
-
-       paddc = vmalloc(len - size);
-       if (!paddc)
-               return -ENOMEM;
-
-       if (copy_from_user(paddc, user + size, len - size) != 0) {
-               ret = -EFAULT;
-               goto free;
-       }
+       paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
+       if (IS_ERR(paddc))
+               return PTR_ERR(paddc);
 
-       t = xt_find_table_lock(net, NFPROTO_ARP, name);
+       t = xt_find_table_lock(net, NFPROTO_ARP, tmp.name);
        if (IS_ERR_OR_NULL(t)) {
                ret = t ? PTR_ERR(t) : -ENOENT;
                goto free;
@@ -1186,7 +1148,7 @@ static int do_add_counters(struct net *net, const void __user *user,
 
        local_bh_disable();
        private = t->private;
-       if (private->number != num_counters) {
+       if (private->number != tmp.num_counters) {
                ret = -EINVAL;
                goto unlock_up_free;
        }
index c2f7f58c001076e955829c6bc5b9e951e346c207..a399c5419622d5f47acd74922bb3771fc55bad73 100644 (file)
@@ -1313,55 +1313,17 @@ do_add_counters(struct net *net, const void __user *user,
        unsigned int i;
        struct xt_counters_info tmp;
        struct xt_counters *paddc;
-       unsigned int num_counters;
-       const char *name;
-       int size;
-       void *ptmp;
        struct xt_table *t;
        const struct xt_table_info *private;
        int ret = 0;
        struct ipt_entry *iter;
        unsigned int addend;
-#ifdef CONFIG_COMPAT
-       struct compat_xt_counters_info compat_tmp;
-
-       if (compat) {
-               ptmp = &compat_tmp;
-               size = sizeof(struct compat_xt_counters_info);
-       } else
-#endif
-       {
-               ptmp = &tmp;
-               size = sizeof(struct xt_counters_info);
-       }
 
-       if (copy_from_user(ptmp, user, size) != 0)
-               return -EFAULT;
-
-#ifdef CONFIG_COMPAT
-       if (compat) {
-               num_counters = compat_tmp.num_counters;
-               name = compat_tmp.name;
-       } else
-#endif
-       {
-               num_counters = tmp.num_counters;
-               name = tmp.name;
-       }
-
-       if (len != size + num_counters * sizeof(struct xt_counters))
-               return -EINVAL;
-
-       paddc = vmalloc(len - size);
-       if (!paddc)
-               return -ENOMEM;
-
-       if (copy_from_user(paddc, user + size, len - size) != 0) {
-               ret = -EFAULT;
-               goto free;
-       }
+       paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
+       if (IS_ERR(paddc))
+               return PTR_ERR(paddc);
 
-       t = xt_find_table_lock(net, AF_INET, name);
+       t = xt_find_table_lock(net, AF_INET, tmp.name);
        if (IS_ERR_OR_NULL(t)) {
                ret = t ? PTR_ERR(t) : -ENOENT;
                goto free;
@@ -1369,7 +1331,7 @@ do_add_counters(struct net *net, const void __user *user,
 
        local_bh_disable();
        private = t->private;
-       if (private->number != num_counters) {
+       if (private->number != tmp.num_counters) {
                ret = -EINVAL;
                goto unlock_up_free;
        }
index ead2c78087e9962053e70b8b13d7915dc2779fc8..22f39e00bef3d2f3d534d10d492858fe7cfa7878 100644 (file)
@@ -1325,55 +1325,16 @@ do_add_counters(struct net *net, const void __user *user, unsigned int len,
        unsigned int i;
        struct xt_counters_info tmp;
        struct xt_counters *paddc;
-       unsigned int num_counters;
-       char *name;
-       int size;
-       void *ptmp;
        struct xt_table *t;
        const struct xt_table_info *private;
        int ret = 0;
        struct ip6t_entry *iter;
        unsigned int addend;
-#ifdef CONFIG_COMPAT
-       struct compat_xt_counters_info compat_tmp;
-
-       if (compat) {
-               ptmp = &compat_tmp;
-               size = sizeof(struct compat_xt_counters_info);
-       } else
-#endif
-       {
-               ptmp = &tmp;
-               size = sizeof(struct xt_counters_info);
-       }
-
-       if (copy_from_user(ptmp, user, size) != 0)
-               return -EFAULT;
-
-#ifdef CONFIG_COMPAT
-       if (compat) {
-               num_counters = compat_tmp.num_counters;
-               name = compat_tmp.name;
-       } else
-#endif
-       {
-               num_counters = tmp.num_counters;
-               name = tmp.name;
-       }
-
-       if (len != size + num_counters * sizeof(struct xt_counters))
-               return -EINVAL;
-
-       paddc = vmalloc(len - size);
-       if (!paddc)
-               return -ENOMEM;
-
-       if (copy_from_user(paddc, user + size, len - size) != 0) {
-               ret = -EFAULT;
-               goto free;
-       }
 
-       t = xt_find_table_lock(net, AF_INET6, name);
+       paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
+       if (IS_ERR(paddc))
+               return PTR_ERR(paddc);
+       t = xt_find_table_lock(net, AF_INET6, tmp.name);
        if (IS_ERR_OR_NULL(t)) {
                ret = t ? PTR_ERR(t) : -ENOENT;
                goto free;
@@ -1381,7 +1342,7 @@ do_add_counters(struct net *net, const void __user *user, unsigned int len,
 
        local_bh_disable();
        private = t->private;
-       if (private->number != num_counters) {
+       if (private->number != tmp.num_counters) {
                ret = -EINVAL;
                goto unlock_up_free;
        }
index 08c9e312bd78a41f8e86ade29c0fe87cd3f3d592..25391fb255162204b197895647f9c2f3c6d96507 100644 (file)
@@ -751,6 +751,80 @@ int xt_check_target(struct xt_tgchk_param *par,
 }
 EXPORT_SYMBOL_GPL(xt_check_target);
 
+/**
+ * xt_copy_counters_from_user - copy counters and metadata from userspace
+ *
+ * @user: src pointer to userspace memory
+ * @len: alleged size of userspace memory
+ * @info: where to store the xt_counters_info metadata
+ * @compat: true if we setsockopt call is done by 32bit task on 64bit kernel
+ *
+ * Copies counter meta data from @user and stores it in @info.
+ *
+ * vmallocs memory to hold the counters, then copies the counter data
+ * from @user to the new memory and returns a pointer to it.
+ *
+ * If @compat is true, @info gets converted automatically to the 64bit
+ * representation.
+ *
+ * The metadata associated with the counters is stored in @info.
+ *
+ * Return: returns pointer that caller has to test via IS_ERR().
+ * If IS_ERR is false, caller has to vfree the pointer.
+ */
+void *xt_copy_counters_from_user(const void __user *user, unsigned int len,
+                                struct xt_counters_info *info, bool compat)
+{
+       void *mem;
+       u64 size;
+
+#ifdef CONFIG_COMPAT
+       if (compat) {
+               /* structures only differ in size due to alignment */
+               struct compat_xt_counters_info compat_tmp;
+
+               if (len <= sizeof(compat_tmp))
+                       return ERR_PTR(-EINVAL);
+
+               len -= sizeof(compat_tmp);
+               if (copy_from_user(&compat_tmp, user, sizeof(compat_tmp)) != 0)
+                       return ERR_PTR(-EFAULT);
+
+               strlcpy(info->name, compat_tmp.name, sizeof(info->name));
+               info->num_counters = compat_tmp.num_counters;
+               user += sizeof(compat_tmp);
+       } else
+#endif
+       {
+               if (len <= sizeof(*info))
+                       return ERR_PTR(-EINVAL);
+
+               len -= sizeof(*info);
+               if (copy_from_user(info, user, sizeof(*info)) != 0)
+                       return ERR_PTR(-EFAULT);
+
+               info->name[sizeof(info->name) - 1] = '\0';
+               user += sizeof(*info);
+       }
+
+       size = sizeof(struct xt_counters);
+       size *= info->num_counters;
+
+       if (size != (u64)len)
+               return ERR_PTR(-EINVAL);
+
+       mem = vmalloc(len);
+       if (!mem)
+               return ERR_PTR(-ENOMEM);
+
+       if (copy_from_user(mem, user, len) == 0)
+               return mem;
+
+       vfree(mem);
+       return ERR_PTR(-EFAULT);
+}
+EXPORT_SYMBOL_GPL(xt_copy_counters_from_user);
+
 #ifdef CONFIG_COMPAT
 int xt_compat_target_offset(const struct xt_target *target)
 {