From: Greg Kroah-Hartman Date: Fri, 24 Jun 2016 15:54:10 +0000 (-0700) Subject: 3.14-stable patches X-Git-Tag: v3.14.73~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5fd3fd526a1ea89a6f4022f3acf39a2ac09ba31c;p=thirdparty%2Fkernel%2Fstable-queue.git 3.14-stable patches added patches: netfilter-x_tables-introduce-and-use-xt_copy_counters_from_user.patch --- diff --git a/queue-3.14/netfilter-x_tables-introduce-and-use-xt_copy_counters_from_user.patch b/queue-3.14/netfilter-x_tables-introduce-and-use-xt_copy_counters_from_user.patch new file mode 100644 index 00000000000..dffc6abee0a --- /dev/null +++ b/queue-3.14/netfilter-x_tables-introduce-and-use-xt_copy_counters_from_user.patch @@ -0,0 +1,336 @@ +From dd77f91a54e32f3f84a1420268302c1ad0e2cfe8 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Fri, 1 Apr 2016 15:37:59 +0200 +Subject: netfilter: x_tables: introduce and use xt_copy_counters_from_user + +From: Florian Westphal + +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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/netfilter/x_tables.h | 3 + + net/ipv4/netfilter/arp_tables.c | 48 ++---------------------- + net/ipv4/netfilter/ip_tables.c | 48 ++---------------------- + net/ipv6/netfilter/ip6_tables.c | 49 ++---------------------- + net/netfilter/x_tables.c | 74 +++++++++++++++++++++++++++++++++++++ + 5 files changed, 92 insertions(+), 130 deletions(-) + +--- a/include/linux/netfilter/x_tables.h ++++ b/include/linux/netfilter/x_tables.h +@@ -248,6 +248,9 @@ int xt_check_match(struct xt_mtchk_param + 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, +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -1125,56 +1125,18 @@ static int do_add_counters(struct net *n + unsigned int i, curcpu; + 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; + void *loc_cpu_entry; + 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; +@@ -1182,7 +1144,7 @@ static int do_add_counters(struct net *n + + local_bh_disable(); + private = t->private; +- if (private->number != num_counters) { ++ if (private->number != tmp.num_counters) { + ret = -EINVAL; + goto unlock_up_free; + } +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -1312,56 +1312,18 @@ do_add_counters(struct net *net, const v + unsigned int i, curcpu; + 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; + void *loc_cpu_entry; + 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 v + + local_bh_disable(); + private = t->private; +- if (private->number != num_counters) { ++ if (private->number != tmp.num_counters) { + ret = -EINVAL; + goto unlock_up_free; + } +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -1322,56 +1322,17 @@ do_add_counters(struct net *net, const v + unsigned int i, curcpu; + 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; + const void *loc_cpu_entry; + 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; +@@ -1380,7 +1341,7 @@ do_add_counters(struct net *net, const v + + local_bh_disable(); + private = t->private; +- if (private->number != num_counters) { ++ if (private->number != tmp.num_counters) { + ret = -EINVAL; + goto unlock_up_free; + } +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -771,6 +771,80 @@ int xt_check_target(struct xt_tgchk_para + } + 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) + { diff --git a/queue-3.14/series b/queue-3.14/series index cf5da033a65..e8a1cf4a47c 100644 --- a/queue-3.14/series +++ b/queue-3.14/series @@ -34,3 +34,4 @@ netfilter-x_tables-xt_compat_match_from_user-doesn-t-need-a-retval.patch netfilter-ensure-number-of-counters-is-0-in-do_replace.patch netfilter-x_tables-do-compat-validation-via-translate_table.patch revert-netfilter-ensure-number-of-counters-is-0-in-do_replace.patch +netfilter-x_tables-introduce-and-use-xt_copy_counters_from_user.patch