diff options
author | Florian Westphal <fw@strlen.de> | 2016-04-01 15:37:59 +0200 |
---|---|---|
committer | Sasha Levin <sasha.levin@oracle.com> | 2016-07-10 23:07:37 -0400 |
commit | b7aa372fec2c1d4a1fcf4398ae88ba94134b9522 (patch) | |
tree | ae6c08d84a0155819fe4745abc9324ce2f1c9c58 /net/netfilter | |
parent | af815d264b7ed1cdceb0e1fdf4fa7d3bd5fe9a99 (diff) | |
download | linux-stable-b7aa372fec2c1d4a1fcf4398ae88ba94134b9522.tar.gz linux-stable-b7aa372fec2c1d4a1fcf4398ae88ba94134b9522.tar.bz2 linux-stable-b7aa372fec2c1d4a1fcf4398ae88ba94134b9522.zip |
netfilter: x_tables: introduce and use xt_copy_counters_from_user
[ Upstream commit d7591f0c41ce3e67600a982bab6989ef0f07b3ce ]
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: Sasha Levin <sasha.levin@oracle.com>
Diffstat (limited to 'net/netfilter')
-rw-r--r-- | net/netfilter/x_tables.c | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 141ae20fa456..4b850c639ac5 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -754,6 +754,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) { |