Commit aaa155f8 authored by Florian Westphal's avatar Florian Westphal Committed by Sasha Levin

netfilter: x_tables: do compat validation via translate_table

[ Upstream commit 09d96860 ]

This looks like refactoring, but its also a bug fix.

Problem is that the compat path (32bit iptables, 64bit kernel) lacks a few
sanity tests that are done in the normal path.

For example, we do not check for underflows and the base chain policies.

While its possible to also add such checks to the compat path, its more
copy&pastry, for instance we cannot reuse check_underflow() helper as
e->target_offset differs in the compat case.

Other problem is that it makes auditing for validation errors harder; two
places need to be checked and kept in sync.

At a high level 32 bit compat works like this:
1- initial pass over blob:
   validate match/entry offsets, bounds checking
   lookup all matches and targets
   do bookkeeping wrt. size delta of 32/64bit structures
   assign match/target.u.kernel pointer (points at kernel
   implementation, needed to access ->compatsize etc.)

2- allocate memory according to the total bookkeeping size to
   contain the translated ruleset

3- second pass over original blob:
   for each entry, copy the 32bit representation to the newly allocated
   memory.  This also does any special match translations (e.g.
   adjust 32bit to 64bit longs, etc).

4- check if ruleset is free of loops (chase all jumps)

5-first pass over translated blob:
   call the checkentry function of all matches and targets.

The alternative implemented by this patch is to drop steps 3&4 from the
compat process, the translation is changed into an intermediate step
rather than a full 1:1 translate_table replacement.

In the 2nd pass (step #3), change the 64bit ruleset back to a kernel
representation, i.e. put() the kernel pointer and restore ->u.user.name .

This gets us a 64bit ruleset that is in the format generated by a 64bit
iptables userspace -- we can then use translate_table() to get the
'native' sanity checks.

This has two drawbacks:

1. we re-validate all the match and target entry structure sizes even
though compat translation is supposed to never generate bogus offsets.
2. we put and then re-lookup each match and target.

THe upside is that we get all sanity tests and ruleset validations
provided by the normal path and can remove some duplicated compat code.

iptables-restore time of autogenerated ruleset with 300k chains of form
-A CHAIN0001 -m limit --limit 1/s -j CHAIN0002
-A CHAIN0002 -m limit --limit 1/s -j CHAIN0003

shows no noticeable differences in restore times:
old:   0m30.796s
new:   0m31.521s
64bit: 0m25.674s
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: default avatarSasha Levin <sasha.levin@oracle.com>
parent edd8ae36
...@@ -1224,19 +1224,17 @@ static inline void compat_release_entry(struct compat_arpt_entry *e) ...@@ -1224,19 +1224,17 @@ static inline void compat_release_entry(struct compat_arpt_entry *e)
module_put(t->u.kernel.target->me); module_put(t->u.kernel.target->me);
} }
static inline int static int
check_compat_entry_size_and_hooks(struct compat_arpt_entry *e, check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
struct xt_table_info *newinfo, struct xt_table_info *newinfo,
unsigned int *size, unsigned int *size,
const unsigned char *base, const unsigned char *base,
const unsigned char *limit, const unsigned char *limit)
const unsigned int *hook_entries,
const unsigned int *underflows)
{ {
struct xt_entry_target *t; struct xt_entry_target *t;
struct xt_target *target; struct xt_target *target;
unsigned int entry_offset; unsigned int entry_offset;
int ret, off, h; int ret, off;
duprintf("check_compat_entry_size_and_hooks %p\n", e); duprintf("check_compat_entry_size_and_hooks %p\n", e);
if ((unsigned long)e % __alignof__(struct compat_arpt_entry) != 0 || if ((unsigned long)e % __alignof__(struct compat_arpt_entry) != 0 ||
...@@ -1281,17 +1279,6 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e, ...@@ -1281,17 +1279,6 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
if (ret) if (ret)
goto release_target; goto release_target;
/* Check hooks & underflows */
for (h = 0; h < NF_ARP_NUMHOOKS; h++) {
if ((unsigned char *)e - base == hook_entries[h])
newinfo->hook_entry[h] = hook_entries[h];
if ((unsigned char *)e - base == underflows[h])
newinfo->underflow[h] = underflows[h];
}
/* Clear counters and comefrom */
memset(&e->counters, 0, sizeof(e->counters));
e->comefrom = 0;
return 0; return 0;
release_target: release_target:
...@@ -1341,7 +1328,7 @@ static int translate_compat_table(struct xt_table_info **pinfo, ...@@ -1341,7 +1328,7 @@ static int translate_compat_table(struct xt_table_info **pinfo,
struct xt_table_info *newinfo, *info; struct xt_table_info *newinfo, *info;
void *pos, *entry0, *entry1; void *pos, *entry0, *entry1;
struct compat_arpt_entry *iter0; struct compat_arpt_entry *iter0;
struct arpt_entry *iter1; struct arpt_replace repl;
unsigned int size; unsigned int size;
int ret = 0; int ret = 0;
...@@ -1350,12 +1337,6 @@ static int translate_compat_table(struct xt_table_info **pinfo, ...@@ -1350,12 +1337,6 @@ static int translate_compat_table(struct xt_table_info **pinfo,
size = compatr->size; size = compatr->size;
info->number = compatr->num_entries; info->number = compatr->num_entries;
/* Init all hooks to impossible value. */
for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
info->hook_entry[i] = 0xFFFFFFFF;
info->underflow[i] = 0xFFFFFFFF;
}
duprintf("translate_compat_table: size %u\n", info->size); duprintf("translate_compat_table: size %u\n", info->size);
j = 0; j = 0;
xt_compat_lock(NFPROTO_ARP); xt_compat_lock(NFPROTO_ARP);
...@@ -1364,9 +1345,7 @@ static int translate_compat_table(struct xt_table_info **pinfo, ...@@ -1364,9 +1345,7 @@ static int translate_compat_table(struct xt_table_info **pinfo,
xt_entry_foreach(iter0, entry0, compatr->size) { xt_entry_foreach(iter0, entry0, compatr->size) {
ret = check_compat_entry_size_and_hooks(iter0, info, &size, ret = check_compat_entry_size_and_hooks(iter0, info, &size,
entry0, entry0,
entry0 + compatr->size, entry0 + compatr->size);
compatr->hook_entry,
compatr->underflow);
if (ret != 0) if (ret != 0)
goto out_unlock; goto out_unlock;
++j; ++j;
...@@ -1379,23 +1358,6 @@ static int translate_compat_table(struct xt_table_info **pinfo, ...@@ -1379,23 +1358,6 @@ static int translate_compat_table(struct xt_table_info **pinfo,
goto out_unlock; goto out_unlock;
} }
/* Check hooks all assigned */
for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
/* Only hooks which are valid */
if (!(compatr->valid_hooks & (1 << i)))
continue;
if (info->hook_entry[i] == 0xFFFFFFFF) {
duprintf("Invalid hook entry %u %u\n",
i, info->hook_entry[i]);
goto out_unlock;
}
if (info->underflow[i] == 0xFFFFFFFF) {
duprintf("Invalid underflow %u %u\n",
i, info->underflow[i]);
goto out_unlock;
}
}
ret = -ENOMEM; ret = -ENOMEM;
newinfo = xt_alloc_table_info(size); newinfo = xt_alloc_table_info(size);
if (!newinfo) if (!newinfo)
...@@ -1412,51 +1374,25 @@ static int translate_compat_table(struct xt_table_info **pinfo, ...@@ -1412,51 +1374,25 @@ static int translate_compat_table(struct xt_table_info **pinfo,
xt_entry_foreach(iter0, entry0, compatr->size) xt_entry_foreach(iter0, entry0, compatr->size)
compat_copy_entry_from_user(iter0, &pos, &size, compat_copy_entry_from_user(iter0, &pos, &size,
newinfo, entry1); newinfo, entry1);
/* all module references in entry0 are now gone */
xt_compat_flush_offsets(NFPROTO_ARP); xt_compat_flush_offsets(NFPROTO_ARP);
xt_compat_unlock(NFPROTO_ARP); xt_compat_unlock(NFPROTO_ARP);
ret = -ELOOP; memcpy(&repl, compatr, sizeof(*compatr));
if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
goto free_newinfo;
i = 0; for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
xt_entry_foreach(iter1, entry1, newinfo->size) { repl.hook_entry[i] = newinfo->hook_entry[i];
ret = check_target(iter1, compatr->name); repl.underflow[i] = newinfo->underflow[i];
if (ret != 0)
break;
++i;
if (strcmp(arpt_get_target(iter1)->u.user.name,
XT_ERROR_TARGET) == 0)
++newinfo->stacksize;
}
if (ret) {
/*
* The first i matches need cleanup_entry (calls ->destroy)
* because they had called ->check already. The other j-i
* entries need only release.
*/
int skip = i;
j -= i;
xt_entry_foreach(iter0, entry0, newinfo->size) {
if (skip-- > 0)
continue;
if (j-- == 0)
break;
compat_release_entry(iter0);
}
xt_entry_foreach(iter1, entry1, newinfo->size) {
if (i-- == 0)
break;
cleanup_entry(iter1);
}
xt_free_table_info(newinfo);
return ret;
} }
/* And one copy for every other CPU */ repl.num_counters = 0;
for_each_possible_cpu(i) repl.counters = NULL;
if (newinfo->entries[i] && newinfo->entries[i] != entry1) repl.size = newinfo->size;
memcpy(newinfo->entries[i], entry1, newinfo->size); ret = translate_table(newinfo, entry1, &repl);
if (ret)
goto free_newinfo;
*pinfo = newinfo; *pinfo = newinfo;
*pentry0 = entry1; *pentry0 = entry1;
...@@ -1465,17 +1401,16 @@ static int translate_compat_table(struct xt_table_info **pinfo, ...@@ -1465,17 +1401,16 @@ static int translate_compat_table(struct xt_table_info **pinfo,
free_newinfo: free_newinfo:
xt_free_table_info(newinfo); xt_free_table_info(newinfo);
out: return ret;
out_unlock:
xt_compat_flush_offsets(NFPROTO_ARP);
xt_compat_unlock(NFPROTO_ARP);
xt_entry_foreach(iter0, entry0, compatr->size) { xt_entry_foreach(iter0, entry0, compatr->size) {
if (j-- == 0) if (j-- == 0)
break; break;
compat_release_entry(iter0); compat_release_entry(iter0);
} }
return ret; return ret;
out_unlock:
xt_compat_flush_offsets(NFPROTO_ARP);
xt_compat_unlock(NFPROTO_ARP);
goto out;
} }
static int compat_do_replace(struct net *net, void __user *user, static int compat_do_replace(struct net *net, void __user *user,
......
...@@ -1479,16 +1479,14 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e, ...@@ -1479,16 +1479,14 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
struct xt_table_info *newinfo, struct xt_table_info *newinfo,
unsigned int *size, unsigned int *size,
const unsigned char *base, const unsigned char *base,
const unsigned char *limit, const unsigned char *limit)
const unsigned int *hook_entries,
const unsigned int *underflows)
{ {
struct xt_entry_match *ematch; struct xt_entry_match *ematch;
struct xt_entry_target *t; struct xt_entry_target *t;
struct xt_target *target; struct xt_target *target;
unsigned int entry_offset; unsigned int entry_offset;
unsigned int j; unsigned int j;
int ret, off, h; int ret, off;
duprintf("check_compat_entry_size_and_hooks %p\n", e); duprintf("check_compat_entry_size_and_hooks %p\n", e);
if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 || if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 ||
...@@ -1541,17 +1539,6 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e, ...@@ -1541,17 +1539,6 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
if (ret) if (ret)
goto out; goto out;
/* Check hooks & underflows */
for (h = 0; h < NF_INET_NUMHOOKS; h++) {
if ((unsigned char *)e - base == hook_entries[h])
newinfo->hook_entry[h] = hook_entries[h];
if ((unsigned char *)e - base == underflows[h])
newinfo->underflow[h] = underflows[h];
}
/* Clear counters and comefrom */
memset(&e->counters, 0, sizeof(e->counters));
e->comefrom = 0;
return 0; return 0;
out: out:
...@@ -1594,6 +1581,7 @@ compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr, ...@@ -1594,6 +1581,7 @@ compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr,
xt_compat_target_from_user(t, dstptr, size); xt_compat_target_from_user(t, dstptr, size);
de->next_offset = e->next_offset - (origsize - *size); de->next_offset = e->next_offset - (origsize - *size);
for (h = 0; h < NF_INET_NUMHOOKS; h++) { for (h = 0; h < NF_INET_NUMHOOKS; h++) {
if ((unsigned char *)de - base < newinfo->hook_entry[h]) if ((unsigned char *)de - base < newinfo->hook_entry[h])
newinfo->hook_entry[h] -= origsize - *size; newinfo->hook_entry[h] -= origsize - *size;
...@@ -1602,41 +1590,6 @@ compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr, ...@@ -1602,41 +1590,6 @@ compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr,
} }
} }
static int
compat_check_entry(struct ipt_entry *e, struct net *net, const char *name)
{
struct xt_entry_match *ematch;
struct xt_mtchk_param mtpar;
unsigned int j;
int ret = 0;
j = 0;
mtpar.net = net;
mtpar.table = name;
mtpar.entryinfo = &e->ip;
mtpar.hook_mask = e->comefrom;
mtpar.family = NFPROTO_IPV4;
xt_ematch_foreach(ematch, e) {
ret = check_match(ematch, &mtpar);
if (ret != 0)
goto cleanup_matches;
++j;
}
ret = check_target(e, net, name);
if (ret)
goto cleanup_matches;
return 0;
cleanup_matches:
xt_ematch_foreach(ematch, e) {
if (j-- == 0)
break;
cleanup_match(ematch, net);
}
return ret;
}
static int static int
translate_compat_table(struct net *net, translate_compat_table(struct net *net,
struct xt_table_info **pinfo, struct xt_table_info **pinfo,
...@@ -1647,7 +1600,7 @@ translate_compat_table(struct net *net, ...@@ -1647,7 +1600,7 @@ translate_compat_table(struct net *net,
struct xt_table_info *newinfo, *info; struct xt_table_info *newinfo, *info;
void *pos, *entry0, *entry1; void *pos, *entry0, *entry1;
struct compat_ipt_entry *iter0; struct compat_ipt_entry *iter0;
struct ipt_entry *iter1; struct ipt_replace repl;
unsigned int size; unsigned int size;
int ret; int ret;
...@@ -1656,12 +1609,6 @@ translate_compat_table(struct net *net, ...@@ -1656,12 +1609,6 @@ translate_compat_table(struct net *net,
size = compatr->size; size = compatr->size;
info->number = compatr->num_entries; info->number = compatr->num_entries;
/* Init all hooks to impossible value. */
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
info->hook_entry[i] = 0xFFFFFFFF;
info->underflow[i] = 0xFFFFFFFF;
}
duprintf("translate_compat_table: size %u\n", info->size); duprintf("translate_compat_table: size %u\n", info->size);
j = 0; j = 0;
xt_compat_lock(AF_INET); xt_compat_lock(AF_INET);
...@@ -1670,9 +1617,7 @@ translate_compat_table(struct net *net, ...@@ -1670,9 +1617,7 @@ translate_compat_table(struct net *net,
xt_entry_foreach(iter0, entry0, compatr->size) { xt_entry_foreach(iter0, entry0, compatr->size) {
ret = check_compat_entry_size_and_hooks(iter0, info, &size, ret = check_compat_entry_size_and_hooks(iter0, info, &size,
entry0, entry0,
entry0 + compatr->size, entry0 + compatr->size);
compatr->hook_entry,
compatr->underflow);
if (ret != 0) if (ret != 0)
goto out_unlock; goto out_unlock;
++j; ++j;
...@@ -1685,23 +1630,6 @@ translate_compat_table(struct net *net, ...@@ -1685,23 +1630,6 @@ translate_compat_table(struct net *net,
goto out_unlock; goto out_unlock;
} }
/* Check hooks all assigned */
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
/* Only hooks which are valid */
if (!(compatr->valid_hooks & (1 << i)))
continue;
if (info->hook_entry[i] == 0xFFFFFFFF) {
duprintf("Invalid hook entry %u %u\n",
i, info->hook_entry[i]);
goto out_unlock;
}
if (info->underflow[i] == 0xFFFFFFFF) {
duprintf("Invalid underflow %u %u\n",
i, info->underflow[i]);
goto out_unlock;
}
}
ret = -ENOMEM; ret = -ENOMEM;
newinfo = xt_alloc_table_info(size); newinfo = xt_alloc_table_info(size);
if (!newinfo) if (!newinfo)
...@@ -1709,8 +1637,8 @@ translate_compat_table(struct net *net, ...@@ -1709,8 +1637,8 @@ translate_compat_table(struct net *net,
newinfo->number = compatr->num_entries; newinfo->number = compatr->num_entries;
for (i = 0; i < NF_INET_NUMHOOKS; i++) { for (i = 0; i < NF_INET_NUMHOOKS; i++) {
newinfo->hook_entry[i] = info->hook_entry[i]; newinfo->hook_entry[i] = compatr->hook_entry[i];
newinfo->underflow[i] = info->underflow[i]; newinfo->underflow[i] = compatr->underflow[i];
} }
entry1 = newinfo->entries[raw_smp_processor_id()]; entry1 = newinfo->entries[raw_smp_processor_id()];
pos = entry1; pos = entry1;
...@@ -1719,51 +1647,29 @@ translate_compat_table(struct net *net, ...@@ -1719,51 +1647,29 @@ translate_compat_table(struct net *net,
compat_copy_entry_from_user(iter0, &pos, &size, compat_copy_entry_from_user(iter0, &pos, &size,
newinfo, entry1); newinfo, entry1);
/* all module references in entry0 are now gone.
* entry1/newinfo contains a 64bit ruleset that looks exactly as
* generated by 64bit userspace.
*
* Call standard translate_table() to validate all hook_entrys,
* underflows, check for loops, etc.
*/
xt_compat_flush_offsets(AF_INET); xt_compat_flush_offsets(AF_INET);
xt_compat_unlock(AF_INET); xt_compat_unlock(AF_INET);
ret = -ELOOP; memcpy(&repl, compatr, sizeof(*compatr));
if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
goto free_newinfo;
i = 0; for (i = 0; i < NF_INET_NUMHOOKS; i++) {
xt_entry_foreach(iter1, entry1, newinfo->size) { repl.hook_entry[i] = newinfo->hook_entry[i];
ret = compat_check_entry(iter1, net, compatr->name); repl.underflow[i] = newinfo->underflow[i];
if (ret != 0)
break;
++i;
if (strcmp(ipt_get_target(iter1)->u.user.name,
XT_ERROR_TARGET) == 0)
++newinfo->stacksize;
}
if (ret) {
/*
* The first i matches need cleanup_entry (calls ->destroy)
* because they had called ->check already. The other j-i
* entries need only release.
*/
int skip = i;
j -= i;
xt_entry_foreach(iter0, entry0, newinfo->size) {
if (skip-- > 0)
continue;
if (j-- == 0)
break;
compat_release_entry(iter0);
}
xt_entry_foreach(iter1, entry1, newinfo->size) {
if (i-- == 0)
break;
cleanup_entry(iter1, net);
}
xt_free_table_info(newinfo);
return ret;
} }
/* And one copy for every other CPU */ repl.num_counters = 0;
for_each_possible_cpu(i) repl.counters = NULL;
if (newinfo->entries[i] && newinfo->entries[i] != entry1) repl.size = newinfo->size;
memcpy(newinfo->entries[i], entry1, newinfo->size); ret = translate_table(net, newinfo, entry1, &repl);
if (ret)
goto free_newinfo;
*pinfo = newinfo; *pinfo = newinfo;
*pentry0 = entry1; *pentry0 = entry1;
...@@ -1772,17 +1678,16 @@ translate_compat_table(struct net *net, ...@@ -1772,17 +1678,16 @@ translate_compat_table(struct net *net,
free_newinfo: free_newinfo:
xt_free_table_info(newinfo); xt_free_table_info(newinfo);
out: return ret;
out_unlock:
xt_compat_flush_offsets(AF_INET);
xt_compat_unlock(AF_INET);
xt_entry_foreach(iter0, entry0, compatr->size) { xt_entry_foreach(iter0, entry0, compatr->size) {
if (j-- == 0) if (j-- == 0)
break; break;
compat_release_entry(iter0); compat_release_entry(iter0);
} }
return ret; return ret;
out_unlock:
xt_compat_flush_offsets(AF_INET);
xt_compat_unlock(AF_INET);
goto out;
} }
static int static int
......
...@@ -1491,16 +1491,14 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e, ...@@ -1491,16 +1491,14 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
struct xt_table_info *newinfo, struct xt_table_info *newinfo,
unsigned int *size, unsigned int *size,
const unsigned char *base, const unsigned char *base,
const unsigned char *limit, const unsigned char *limit)
const unsigned int *hook_entries,
const unsigned int *underflows)
{ {
struct xt_entry_match *ematch; struct xt_entry_match *ematch;
struct xt_entry_target *t; struct xt_entry_target *t;
struct xt_target *target; struct xt_target *target;
unsigned int entry_offset; unsigned int entry_offset;
unsigned int j; unsigned int j;
int ret, off, h; int ret, off;
duprintf("check_compat_entry_size_and_hooks %p\n", e); duprintf("check_compat_entry_size_and_hooks %p\n", e);
if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 || if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 ||
...@@ -1553,17 +1551,6 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e, ...@@ -1553,17 +1551,6 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
if (ret) if (ret)
goto out; goto out;
/* Check hooks & underflows */
for (h = 0; h < NF_INET_NUMHOOKS; h++) {
if ((unsigned char *)e - base == hook_entries[h])
newinfo->hook_entry[h] = hook_entries[h];
if ((unsigned char *)e - base == underflows[h])
newinfo->underflow[h] = underflows[h];
}
/* Clear counters and comefrom */
memset(&e->counters, 0, sizeof(e->counters));
e->comefrom = 0;
return 0; return 0;
out: out:
...@@ -1612,41 +1599,6 @@ compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr, ...@@ -1612,41 +1599,6 @@ compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
} }
} }
static int compat_check_entry(struct ip6t_entry *e, struct net *net,
const char *name)
{
unsigned int j;
int ret = 0;
struct xt_mtchk_param mtpar;
struct xt_entry_match *ematch;
j = 0;
mtpar.net = net;
mtpar.table = name;
mtpar.entryinfo = &e->ipv6;
mtpar.hook_mask = e->comefrom;
mtpar.family = NFPROTO_IPV6;
xt_ematch_foreach(ematch, e) {
ret = check_match(ematch, &mtpar);
if (ret != 0)
goto cleanup_matches;
++j;
}
ret = check_target(e, net, name);
if (ret)
goto cleanup_matches;
return 0;
cleanup_matches:
xt_ematch_foreach(ematch, e) {
if (j-- == 0)
break;
cleanup_match(ematch, net);
}
return ret;
}
static int static int
translate_compat_table(struct net *net, translate_compat_table(struct net *net,
struct xt_table_info **pinfo, struct xt_table_info **pinfo,
...@@ -1657,7 +1609,7 @@ translate_compat_table(struct net *net, ...@@ -1657,7 +1609,7 @@ translate_compat_table(struct net *net,
struct xt_table_info *newinfo, *info; struct xt_table_info *newinfo, *info;
void *pos, *entry0, *entry1; void *pos, *entry0, *entry1;
struct compat_ip6t_entry *iter0; struct compat_ip6t_entry *iter0;
struct ip6t_entry *iter1; struct ip6t_replace repl;
unsigned int size; unsigned int size;
int ret = 0; int ret = 0;
...@@ -1666,12 +1618,6 @@ translate_compat_table(struct net *net, ...@@ -1666,12 +1618,6 @@ translate_compat_table(struct net *net,
size = compatr->size; size = compatr->size;
info->number = compatr->num_entries; info->number = compatr->num_entries;
/* Init all hooks to impossible value. */
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
info->hook_entry[i] = 0xFFFFFFFF;
info->underflow[i] = 0xFFFFFFFF;
}
duprintf("translate_compat_table: size %u\n", info->size); duprintf("translate_compat_table: size %u\n", info->size);
j = 0; j = 0;
xt_compat_lock(AF_INET6); xt_compat_lock(AF_INET6);
...@@ -1680,9 +1626,7 @@ translate_compat_table(struct net *net, ...@@ -1680,9 +1626,7 @@ translate_compat_table(struct net *net,
xt_entry_foreach(iter0, entry0, compatr->size) { xt_entry_foreach(iter0, entry0, compatr->size) {
ret = check_compat_entry_size_and_hooks(iter0, info, &size, ret = check_compat_entry_size_and_hooks(iter0, info, &size,
entry0, entry0,
entry0 + compatr->size, entry0 + compatr->size);
compatr->hook_entry,
compatr->underflow);
if (ret != 0) if (ret != 0)
goto out_unlock; goto out_unlock;
++j; ++j;
...@@ -1695,23 +1639,6 @@ translate_compat_table(struct net *net, ...@@ -1695,23 +1639,6 @@ translate_compat_table(struct net *net,
goto out_unlock; goto out_unlock;
} }
/* Check hooks all assigned */
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
/* Only hooks which are valid */
if (!(compatr->valid_hooks & (1 << i)))
continue;
if (info->hook_entry[i] == 0xFFFFFFFF) {
duprintf("Invalid hook entry %u %u\n",
i, info->hook_entry[i]);
goto out_unlock;
}
if (info->underflow[i] == 0xFFFFFFFF) {
duprintf("Invalid underflow %u %u\n",
i, info->underflow[i]);
goto out_unlock;
}
}
ret = -ENOMEM; ret = -ENOMEM;
newinfo = xt_alloc_table_info(size); newinfo = xt_alloc_table_info(size);
if (!newinfo) if (!newinfo)
...@@ -1719,60 +1646,33 @@ translate_compat_table(struct net *net, ...@@ -1719,60 +1646,33 @@ translate_compat_table(struct net *net,
newinfo->number = compatr->num_entries; newinfo->number = compatr->num_entries;
for (i = 0; i < NF_INET_NUMHOOKS; i++) { for (i = 0; i < NF_INET_NUMHOOKS; i++) {
newinfo->hook_entry[i] = info->hook_entry[i]; newinfo->hook_entry[i] = compatr->hook_entry[i];
newinfo->underflow[i] = info->underflow[i]; newinfo->underflow[i] = compatr->underflow[i];
} }
entry1 = newinfo->entries[raw_smp_processor_id()]; entry1 = newinfo->entries[raw_smp_processor_id()];
pos = entry1; pos = entry1;
size = compatr->size;
xt_entry_foreach(iter0, entry0, compatr->size) xt_entry_foreach(iter0, entry0, compatr->size)
compat_copy_entry_from_user(iter0, &pos, &size, compat_copy_entry_from_user(iter0, &pos, &size,
newinfo, entry1); newinfo, entry1);
/* all module references in entry0 are now gone. */
xt_compat_flush_offsets(AF_INET6); xt_compat_flush_offsets(AF_INET6);
xt_compat_unlock(AF_INET6); xt_compat_unlock(AF_INET6);
ret = -ELOOP; memcpy(&repl, compatr, sizeof(*compatr));
if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
goto free_newinfo;
i = 0; for (i = 0; i < NF_INET_NUMHOOKS; i++) {
xt_entry_foreach(iter1, entry1, newinfo->size) { repl.hook_entry[i] = newinfo->hook_entry[i];
ret = compat_check_entry(iter1, net, compatr->name); repl.underflow[i] = newinfo->underflow[i];
if (ret != 0)
break;
++i;
if (strcmp(ip6t_get_target(iter1)->u.user.name,
XT_ERROR_TARGET) == 0)
++newinfo->stacksize;
}
if (ret) {
/*
* The first i matches need cleanup_entry (calls ->destroy)
* because they had called ->check already. The other j-i
* entries need only release.
*/
int skip = i;
j -= i;
xt_entry_foreach(iter0, entry0, newinfo->size) {
if (skip-- > 0)
continue;
if (j-- == 0)
break;
compat_release_entry(iter0);
}
xt_entry_foreach(iter1, entry1, newinfo->size) {
if (i-- == 0)
break;
cleanup_entry(iter1, net);
}
xt_free_table_info(newinfo);
return ret;
} }
/* And one copy for every other CPU */ repl.num_counters = 0;
for_each_possible_cpu(i) repl.counters = NULL;
if (newinfo->entries[i] && newinfo->entries[i] != entry1) repl.size = newinfo->size;
memcpy(newinfo->entries[i], entry1, newinfo->size); ret = translate_table(net, newinfo, entry1, &repl);
if (ret)
goto free_newinfo;
*pinfo = newinfo; *pinfo = newinfo;
*pentry0 = entry1; *pentry0 = entry1;
...@@ -1781,17 +1681,16 @@ translate_compat_table(struct net *net, ...@@ -1781,17 +1681,16 @@ translate_compat_table(struct net *net,
free_newinfo: free_newinfo:
xt_free_table_info(newinfo); xt_free_table_info(newinfo);
out: return ret;
out_unlock:
xt_compat_flush_offsets(AF_INET6);
xt_compat_unlock(AF_INET6);
xt_entry_foreach(iter0, entry0, compatr->size) { xt_entry_foreach(iter0, entry0, compatr->size) {
if (j-- == 0) if (j-- == 0)
break; break;
compat_release_entry(iter0); compat_release_entry(iter0);
} }
return ret; return ret;
out_unlock:
xt_compat_flush_offsets(AF_INET6);
xt_compat_unlock(AF_INET6);
goto out;
} }
static int static int
......
...@@ -535,6 +535,7 @@ void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr, ...@@ -535,6 +535,7 @@ void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m; struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m;
int pad, off = xt_compat_match_offset(match); int pad, off = xt_compat_match_offset(match);
u_int16_t msize = cm->u.user.match_size; u_int16_t msize = cm->u.user.match_size;
char name[sizeof(m->u.user.name)];
m = *dstptr; m = *dstptr;
memcpy(m, cm, sizeof(*cm)); memcpy(m, cm, sizeof(*cm));
...@@ -548,6 +549,9 @@ void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr, ...@@ -548,6 +549,9 @@ void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
msize += off; msize += off;
m->u.user.match_size = msize; m->u.user.match_size = msize;
strlcpy(name, match->name, sizeof(name));
module_put(match->me);
strncpy(m->u.user.name, name, sizeof(m->u.user.name));
*size += off; *size += off;
*dstptr += msize; *dstptr += msize;
...@@ -765,6 +769,7 @@ void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr, ...@@ -765,6 +769,7 @@ void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t; struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t;
int pad, off = xt_compat_target_offset(target); int pad, off = xt_compat_target_offset(target);
u_int16_t tsize = ct->u.user.target_size; u_int16_t tsize = ct->u.user.target_size;
char name[sizeof(t->u.user.name)];
t = *dstptr; t = *dstptr;
memcpy(t, ct, sizeof(*ct)); memcpy(t, ct, sizeof(*ct));
...@@ -778,6 +783,9 @@ void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr, ...@@ -778,6 +783,9 @@ void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
tsize += off; tsize += off;
t->u.user.target_size = tsize; t->u.user.target_size = tsize;
strlcpy(name, target->name, sizeof(name));
module_put(target->me);
strncpy(t->u.user.name, name, sizeof(t->u.user.name));
*size += off; *size += off;
*dstptr += tsize; *dstptr += tsize;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment