Skip to content

Commit e917563

Browse files
Florian Westphalgregkh
authored andcommitted
netfilter: x_tables: introduce and use xt_copy_counters_from_user
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>
1 parent d69f93d commit e917563

5 files changed

Lines changed: 92 additions & 130 deletions

File tree

include/linux/netfilter/x_tables.h

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

251+
void *xt_copy_counters_from_user(const void __user *user, unsigned int len,
252+
struct xt_counters_info *info, bool compat);
253+
251254
struct xt_table *xt_register_table(struct net *net,
252255
const struct xt_table *table,
253256
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
@@ -1130,63 +1130,25 @@ static int do_add_counters(struct net *net, const void __user *user,
11301130
unsigned int i;
11311131
struct xt_counters_info tmp;
11321132
struct xt_counters *paddc;
1133-
unsigned int num_counters;
1134-
const char *name;
1135-
int size;
1136-
void *ptmp;
11371133
struct xt_table *t;
11381134
const struct xt_table_info *private;
11391135
int ret = 0;
11401136
struct arpt_entry *iter;
11411137
unsigned int addend;
1142-
#ifdef CONFIG_COMPAT
1143-
struct compat_xt_counters_info compat_tmp;
1144-
1145-
if (compat) {
1146-
ptmp = &compat_tmp;
1147-
size = sizeof(struct compat_xt_counters_info);
1148-
} else
1149-
#endif
1150-
{
1151-
ptmp = &tmp;
1152-
size = sizeof(struct xt_counters_info);
1153-
}
11541138

1155-
if (copy_from_user(ptmp, user, size) != 0)
1156-
return -EFAULT;
1157-
1158-
#ifdef CONFIG_COMPAT
1159-
if (compat) {
1160-
num_counters = compat_tmp.num_counters;
1161-
name = compat_tmp.name;
1162-
} else
1163-
#endif
1164-
{
1165-
num_counters = tmp.num_counters;
1166-
name = tmp.name;
1167-
}
1168-
1169-
if (len != size + num_counters * sizeof(struct xt_counters))
1170-
return -EINVAL;
1171-
1172-
paddc = vmalloc(len - size);
1173-
if (!paddc)
1174-
return -ENOMEM;
1175-
1176-
if (copy_from_user(paddc, user + size, len - size) != 0) {
1177-
ret = -EFAULT;
1178-
goto free;
1179-
}
1139+
paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
1140+
if (IS_ERR(paddc))
1141+
return PTR_ERR(paddc);
11801142

1181-
t = xt_find_table_lock(net, NFPROTO_ARP, name);
1143+
t = xt_find_table_lock(net, NFPROTO_ARP, tmp.name);
11821144
if (IS_ERR_OR_NULL(t)) {
11831145
ret = t ? PTR_ERR(t) : -ENOENT;
11841146
goto free;
11851147
}
11861148

11871149
local_bh_disable();
11881150
private = t->private;
1189-
if (private->number != num_counters) {
1151+
if (private->number != tmp.num_counters) {
11901152
ret = -EINVAL;
11911153
goto unlock_up_free;
11921154
}

net/ipv4/netfilter/ip_tables.c

Lines changed: 5 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,63 +1313,25 @@ do_add_counters(struct net *net, const void __user *user,
13131313
unsigned int i;
13141314
struct xt_counters_info tmp;
13151315
struct xt_counters *paddc;
1316-
unsigned int num_counters;
1317-
const char *name;
1318-
int size;
1319-
void *ptmp;
13201316
struct xt_table *t;
13211317
const struct xt_table_info *private;
13221318
int ret = 0;
13231319
struct ipt_entry *iter;
13241320
unsigned int addend;
1325-
#ifdef CONFIG_COMPAT
1326-
struct compat_xt_counters_info compat_tmp;
1327-
1328-
if (compat) {
1329-
ptmp = &compat_tmp;
1330-
size = sizeof(struct compat_xt_counters_info);
1331-
} else
1332-
#endif
1333-
{
1334-
ptmp = &tmp;
1335-
size = sizeof(struct xt_counters_info);
1336-
}
13371321

1338-
if (copy_from_user(ptmp, user, size) != 0)
1339-
return -EFAULT;
1340-
1341-
#ifdef CONFIG_COMPAT
1342-
if (compat) {
1343-
num_counters = compat_tmp.num_counters;
1344-
name = compat_tmp.name;
1345-
} else
1346-
#endif
1347-
{
1348-
num_counters = tmp.num_counters;
1349-
name = tmp.name;
1350-
}
1351-
1352-
if (len != size + num_counters * sizeof(struct xt_counters))
1353-
return -EINVAL;
1354-
1355-
paddc = vmalloc(len - size);
1356-
if (!paddc)
1357-
return -ENOMEM;
1358-
1359-
if (copy_from_user(paddc, user + size, len - size) != 0) {
1360-
ret = -EFAULT;
1361-
goto free;
1362-
}
1322+
paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
1323+
if (IS_ERR(paddc))
1324+
return PTR_ERR(paddc);
13631325

1364-
t = xt_find_table_lock(net, AF_INET, name);
1326+
t = xt_find_table_lock(net, AF_INET, tmp.name);
13651327
if (IS_ERR_OR_NULL(t)) {
13661328
ret = t ? PTR_ERR(t) : -ENOENT;
13671329
goto free;
13681330
}
13691331

13701332
local_bh_disable();
13711333
private = t->private;
1372-
if (private->number != num_counters) {
1334+
if (private->number != tmp.num_counters) {
13731335
ret = -EINVAL;
13741336
goto unlock_up_free;
13751337
}

net/ipv6/netfilter/ip6_tables.c

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,63 +1325,24 @@ do_add_counters(struct net *net, const void __user *user, unsigned int len,
13251325
unsigned int i;
13261326
struct xt_counters_info tmp;
13271327
struct xt_counters *paddc;
1328-
unsigned int num_counters;
1329-
char *name;
1330-
int size;
1331-
void *ptmp;
13321328
struct xt_table *t;
13331329
const struct xt_table_info *private;
13341330
int ret = 0;
13351331
struct ip6t_entry *iter;
13361332
unsigned int addend;
1337-
#ifdef CONFIG_COMPAT
1338-
struct compat_xt_counters_info compat_tmp;
1339-
1340-
if (compat) {
1341-
ptmp = &compat_tmp;
1342-
size = sizeof(struct compat_xt_counters_info);
1343-
} else
1344-
#endif
1345-
{
1346-
ptmp = &tmp;
1347-
size = sizeof(struct xt_counters_info);
1348-
}
1349-
1350-
if (copy_from_user(ptmp, user, size) != 0)
1351-
return -EFAULT;
1352-
1353-
#ifdef CONFIG_COMPAT
1354-
if (compat) {
1355-
num_counters = compat_tmp.num_counters;
1356-
name = compat_tmp.name;
1357-
} else
1358-
#endif
1359-
{
1360-
num_counters = tmp.num_counters;
1361-
name = tmp.name;
1362-
}
1363-
1364-
if (len != size + num_counters * sizeof(struct xt_counters))
1365-
return -EINVAL;
1366-
1367-
paddc = vmalloc(len - size);
1368-
if (!paddc)
1369-
return -ENOMEM;
1370-
1371-
if (copy_from_user(paddc, user + size, len - size) != 0) {
1372-
ret = -EFAULT;
1373-
goto free;
1374-
}
13751333

1376-
t = xt_find_table_lock(net, AF_INET6, name);
1334+
paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
1335+
if (IS_ERR(paddc))
1336+
return PTR_ERR(paddc);
1337+
t = xt_find_table_lock(net, AF_INET6, tmp.name);
13771338
if (IS_ERR_OR_NULL(t)) {
13781339
ret = t ? PTR_ERR(t) : -ENOENT;
13791340
goto free;
13801341
}
13811342

13821343
local_bh_disable();
13831344
private = t->private;
1384-
if (private->number != num_counters) {
1345+
if (private->number != tmp.num_counters) {
13851346
ret = -EINVAL;
13861347
goto unlock_up_free;
13871348
}

net/netfilter/x_tables.c

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

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

0 commit comments

Comments
 (0)