Skip to content

Commit d7591f0

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: x_tables: introduce and use xt_copy_counters_from_user
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>
1 parent aded9f3 commit d7591f0

File tree

5 files changed

+92
-130
lines changed

5 files changed

+92
-130
lines changed

include/linux/netfilter/x_tables.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,9 @@ int xt_check_match(struct xt_mtchk_param *, unsigned int size, u_int8_t proto,
251251
int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto,
252252
bool inv_proto);
253253

254+
void *xt_copy_counters_from_user(const void __user *user, unsigned int len,
255+
struct xt_counters_info *info, bool compat);
256+
254257
struct xt_table *xt_register_table(struct net *net,
255258
const struct xt_table *table,
256259
struct xt_table_info *bootstrap,

net/ipv4/netfilter/arp_tables.c

Lines changed: 5 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,63 +1123,25 @@ static int do_add_counters(struct net *net, const void __user *user,
11231123
unsigned int i;
11241124
struct xt_counters_info tmp;
11251125
struct xt_counters *paddc;
1126-
unsigned int num_counters;
1127-
const char *name;
1128-
int size;
1129-
void *ptmp;
11301126
struct xt_table *t;
11311127
const struct xt_table_info *private;
11321128
int ret = 0;
11331129
struct arpt_entry *iter;
11341130
unsigned int addend;
1135-
#ifdef CONFIG_COMPAT
1136-
struct compat_xt_counters_info compat_tmp;
1137-
1138-
if (compat) {
1139-
ptmp = &compat_tmp;
1140-
size = sizeof(struct compat_xt_counters_info);
1141-
} else
1142-
#endif
1143-
{
1144-
ptmp = &tmp;
1145-
size = sizeof(struct xt_counters_info);
1146-
}
11471131

1148-
if (copy_from_user(ptmp, user, size) != 0)
1149-
return -EFAULT;
1150-
1151-
#ifdef CONFIG_COMPAT
1152-
if (compat) {
1153-
num_counters = compat_tmp.num_counters;
1154-
name = compat_tmp.name;
1155-
} else
1156-
#endif
1157-
{
1158-
num_counters = tmp.num_counters;
1159-
name = tmp.name;
1160-
}
1161-
1162-
if (len != size + num_counters * sizeof(struct xt_counters))
1163-
return -EINVAL;
1164-
1165-
paddc = vmalloc(len - size);
1166-
if (!paddc)
1167-
return -ENOMEM;
1168-
1169-
if (copy_from_user(paddc, user + size, len - size) != 0) {
1170-
ret = -EFAULT;
1171-
goto free;
1172-
}
1132+
paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
1133+
if (IS_ERR(paddc))
1134+
return PTR_ERR(paddc);
11731135

1174-
t = xt_find_table_lock(net, NFPROTO_ARP, name);
1136+
t = xt_find_table_lock(net, NFPROTO_ARP, tmp.name);
11751137
if (IS_ERR_OR_NULL(t)) {
11761138
ret = t ? PTR_ERR(t) : -ENOENT;
11771139
goto free;
11781140
}
11791141

11801142
local_bh_disable();
11811143
private = t->private;
1182-
if (private->number != num_counters) {
1144+
if (private->number != tmp.num_counters) {
11831145
ret = -EINVAL;
11841146
goto unlock_up_free;
11851147
}

net/ipv4/netfilter/ip_tables.c

Lines changed: 5 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,63 +1307,25 @@ do_add_counters(struct net *net, const void __user *user,
13071307
unsigned int i;
13081308
struct xt_counters_info tmp;
13091309
struct xt_counters *paddc;
1310-
unsigned int num_counters;
1311-
const char *name;
1312-
int size;
1313-
void *ptmp;
13141310
struct xt_table *t;
13151311
const struct xt_table_info *private;
13161312
int ret = 0;
13171313
struct ipt_entry *iter;
13181314
unsigned int addend;
1319-
#ifdef CONFIG_COMPAT
1320-
struct compat_xt_counters_info compat_tmp;
1321-
1322-
if (compat) {
1323-
ptmp = &compat_tmp;
1324-
size = sizeof(struct compat_xt_counters_info);
1325-
} else
1326-
#endif
1327-
{
1328-
ptmp = &tmp;
1329-
size = sizeof(struct xt_counters_info);
1330-
}
13311315

1332-
if (copy_from_user(ptmp, user, size) != 0)
1333-
return -EFAULT;
1334-
1335-
#ifdef CONFIG_COMPAT
1336-
if (compat) {
1337-
num_counters = compat_tmp.num_counters;
1338-
name = compat_tmp.name;
1339-
} else
1340-
#endif
1341-
{
1342-
num_counters = tmp.num_counters;
1343-
name = tmp.name;
1344-
}
1345-
1346-
if (len != size + num_counters * sizeof(struct xt_counters))
1347-
return -EINVAL;
1348-
1349-
paddc = vmalloc(len - size);
1350-
if (!paddc)
1351-
return -ENOMEM;
1352-
1353-
if (copy_from_user(paddc, user + size, len - size) != 0) {
1354-
ret = -EFAULT;
1355-
goto free;
1356-
}
1316+
paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
1317+
if (IS_ERR(paddc))
1318+
return PTR_ERR(paddc);
13571319

1358-
t = xt_find_table_lock(net, AF_INET, name);
1320+
t = xt_find_table_lock(net, AF_INET, tmp.name);
13591321
if (IS_ERR_OR_NULL(t)) {
13601322
ret = t ? PTR_ERR(t) : -ENOENT;
13611323
goto free;
13621324
}
13631325

13641326
local_bh_disable();
13651327
private = t->private;
1366-
if (private->number != num_counters) {
1328+
if (private->number != tmp.num_counters) {
13671329
ret = -EINVAL;
13681330
goto unlock_up_free;
13691331
}

net/ipv6/netfilter/ip6_tables.c

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,63 +1319,24 @@ do_add_counters(struct net *net, const void __user *user, unsigned int len,
13191319
unsigned int i;
13201320
struct xt_counters_info tmp;
13211321
struct xt_counters *paddc;
1322-
unsigned int num_counters;
1323-
char *name;
1324-
int size;
1325-
void *ptmp;
13261322
struct xt_table *t;
13271323
const struct xt_table_info *private;
13281324
int ret = 0;
13291325
struct ip6t_entry *iter;
13301326
unsigned int addend;
1331-
#ifdef CONFIG_COMPAT
1332-
struct compat_xt_counters_info compat_tmp;
1333-
1334-
if (compat) {
1335-
ptmp = &compat_tmp;
1336-
size = sizeof(struct compat_xt_counters_info);
1337-
} else
1338-
#endif
1339-
{
1340-
ptmp = &tmp;
1341-
size = sizeof(struct xt_counters_info);
1342-
}
1343-
1344-
if (copy_from_user(ptmp, user, size) != 0)
1345-
return -EFAULT;
1346-
1347-
#ifdef CONFIG_COMPAT
1348-
if (compat) {
1349-
num_counters = compat_tmp.num_counters;
1350-
name = compat_tmp.name;
1351-
} else
1352-
#endif
1353-
{
1354-
num_counters = tmp.num_counters;
1355-
name = tmp.name;
1356-
}
1357-
1358-
if (len != size + num_counters * sizeof(struct xt_counters))
1359-
return -EINVAL;
1360-
1361-
paddc = vmalloc(len - size);
1362-
if (!paddc)
1363-
return -ENOMEM;
1364-
1365-
if (copy_from_user(paddc, user + size, len - size) != 0) {
1366-
ret = -EFAULT;
1367-
goto free;
1368-
}
13691327

1370-
t = xt_find_table_lock(net, AF_INET6, name);
1328+
paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
1329+
if (IS_ERR(paddc))
1330+
return PTR_ERR(paddc);
1331+
t = xt_find_table_lock(net, AF_INET6, tmp.name);
13711332
if (IS_ERR_OR_NULL(t)) {
13721333
ret = t ? PTR_ERR(t) : -ENOENT;
13731334
goto free;
13741335
}
13751336

13761337
local_bh_disable();
13771338
private = t->private;
1378-
if (private->number != num_counters) {
1339+
if (private->number != tmp.num_counters) {
13791340
ret = -EINVAL;
13801341
goto unlock_up_free;
13811342
}

net/netfilter/x_tables.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,80 @@ int xt_check_target(struct xt_tgchk_param *par,
752752
}
753753
EXPORT_SYMBOL_GPL(xt_check_target);
754754

755+
/**
756+
* xt_copy_counters_from_user - copy counters and metadata from userspace
757+
*
758+
* @user: src pointer to userspace memory
759+
* @len: alleged size of userspace memory
760+
* @info: where to store the xt_counters_info metadata
761+
* @compat: true if we setsockopt call is done by 32bit task on 64bit kernel
762+
*
763+
* Copies counter meta data from @user and stores it in @info.
764+
*
765+
* vmallocs memory to hold the counters, then copies the counter data
766+
* from @user to the new memory and returns a pointer to it.
767+
*
768+
* If @compat is true, @info gets converted automatically to the 64bit
769+
* representation.
770+
*
771+
* The metadata associated with the counters is stored in @info.
772+
*
773+
* Return: returns pointer that caller has to test via IS_ERR().
774+
* If IS_ERR is false, caller has to vfree the pointer.
775+
*/
776+
void *xt_copy_counters_from_user(const void __user *user, unsigned int len,
777+
struct xt_counters_info *info, bool compat)
778+
{
779+
void *mem;
780+
u64 size;
781+
782+
#ifdef CONFIG_COMPAT
783+
if (compat) {
784+
/* structures only differ in size due to alignment */
785+
struct compat_xt_counters_info compat_tmp;
786+
787+
if (len <= sizeof(compat_tmp))
788+
return ERR_PTR(-EINVAL);
789+
790+
len -= sizeof(compat_tmp);
791+
if (copy_from_user(&compat_tmp, user, sizeof(compat_tmp)) != 0)
792+
return ERR_PTR(-EFAULT);
793+
794+
strlcpy(info->name, compat_tmp.name, sizeof(info->name));
795+
info->num_counters = compat_tmp.num_counters;
796+
user += sizeof(compat_tmp);
797+
} else
798+
#endif
799+
{
800+
if (len <= sizeof(*info))
801+
return ERR_PTR(-EINVAL);
802+
803+
len -= sizeof(*info);
804+
if (copy_from_user(info, user, sizeof(*info)) != 0)
805+
return ERR_PTR(-EFAULT);
806+
807+
info->name[sizeof(info->name) - 1] = '\0';
808+
user += sizeof(*info);
809+
}
810+
811+
size = sizeof(struct xt_counters);
812+
size *= info->num_counters;
813+
814+
if (size != (u64)len)
815+
return ERR_PTR(-EINVAL);
816+
817+
mem = vmalloc(len);
818+
if (!mem)
819+
return ERR_PTR(-ENOMEM);
820+
821+
if (copy_from_user(mem, user, len) == 0)
822+
return mem;
823+
824+
vfree(mem);
825+
return ERR_PTR(-EFAULT);
826+
}
827+
EXPORT_SYMBOL_GPL(xt_copy_counters_from_user);
828+
755829
#ifdef CONFIG_COMPAT
756830
int xt_compat_target_offset(const struct xt_target *target)
757831
{

0 commit comments

Comments
 (0)