diff options
author | Dmitry Mishin <dim@openvz.org> | 2006-10-30 18:14:27 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-10-30 18:24:47 -0500 |
commit | 920b868ae1dfdac77c5e8c97e7067b23680f043e (patch) | |
tree | cec854f186e6ac37ea0b599f34a44fbfe7d49d2c /net/ipv4 | |
parent | c073e3fa8b7f9841aa6451885f135656d455f511 (diff) |
[NETFILTER]: ip_tables: compat code module refcounting fix
This patch fixes bug in iptables modules refcounting on compat error way.
As we are getting modules in check_compat_entry_size_and_hooks(), in case of
later error, we should put them all in translate_compat_table(), not in the
compat_copy_entry_from_user() or compat_copy_match_from_user(), as it is now.
Signed-off-by: Dmitry Mishin <dim@openvz.org>
Acked-by: Vasily Averin <vvs@openvz.org>
Acked-by: Kirill Korotaev <dev@openvz.org>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/netfilter/ip_tables.c | 36 |
1 files changed, 11 insertions, 25 deletions
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 0f4835cf0e4d..8a455439b128 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c | |||
@@ -1527,7 +1527,7 @@ cleanup_matches: | |||
1527 | 1527 | ||
1528 | static inline int compat_copy_match_from_user(struct ipt_entry_match *m, | 1528 | static inline int compat_copy_match_from_user(struct ipt_entry_match *m, |
1529 | void **dstptr, compat_uint_t *size, const char *name, | 1529 | void **dstptr, compat_uint_t *size, const char *name, |
1530 | const struct ipt_ip *ip, unsigned int hookmask, int *i) | 1530 | const struct ipt_ip *ip, unsigned int hookmask) |
1531 | { | 1531 | { |
1532 | struct ipt_entry_match *dm; | 1532 | struct ipt_entry_match *dm; |
1533 | struct ipt_match *match; | 1533 | struct ipt_match *match; |
@@ -1540,22 +1540,13 @@ static inline int compat_copy_match_from_user(struct ipt_entry_match *m, | |||
1540 | ret = xt_check_match(match, AF_INET, dm->u.match_size - sizeof(*dm), | 1540 | ret = xt_check_match(match, AF_INET, dm->u.match_size - sizeof(*dm), |
1541 | name, hookmask, ip->proto, | 1541 | name, hookmask, ip->proto, |
1542 | ip->invflags & IPT_INV_PROTO); | 1542 | ip->invflags & IPT_INV_PROTO); |
1543 | if (ret) | 1543 | if (!ret && m->u.kernel.match->checkentry |
1544 | goto err; | ||
1545 | |||
1546 | if (m->u.kernel.match->checkentry | ||
1547 | && !m->u.kernel.match->checkentry(name, ip, match, dm->data, | 1544 | && !m->u.kernel.match->checkentry(name, ip, match, dm->data, |
1548 | hookmask)) { | 1545 | hookmask)) { |
1549 | duprintf("ip_tables: check failed for `%s'.\n", | 1546 | duprintf("ip_tables: check failed for `%s'.\n", |
1550 | m->u.kernel.match->name); | 1547 | m->u.kernel.match->name); |
1551 | ret = -EINVAL; | 1548 | ret = -EINVAL; |
1552 | goto err; | ||
1553 | } | 1549 | } |
1554 | (*i)++; | ||
1555 | return 0; | ||
1556 | |||
1557 | err: | ||
1558 | module_put(m->u.kernel.match->me); | ||
1559 | return ret; | 1550 | return ret; |
1560 | } | 1551 | } |
1561 | 1552 | ||
@@ -1567,19 +1558,18 @@ static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr, | |||
1567 | struct ipt_target *target; | 1558 | struct ipt_target *target; |
1568 | struct ipt_entry *de; | 1559 | struct ipt_entry *de; |
1569 | unsigned int origsize; | 1560 | unsigned int origsize; |
1570 | int ret, h, j; | 1561 | int ret, h; |
1571 | 1562 | ||
1572 | ret = 0; | 1563 | ret = 0; |
1573 | origsize = *size; | 1564 | origsize = *size; |
1574 | de = (struct ipt_entry *)*dstptr; | 1565 | de = (struct ipt_entry *)*dstptr; |
1575 | memcpy(de, e, sizeof(struct ipt_entry)); | 1566 | memcpy(de, e, sizeof(struct ipt_entry)); |
1576 | 1567 | ||
1577 | j = 0; | ||
1578 | *dstptr += sizeof(struct compat_ipt_entry); | 1568 | *dstptr += sizeof(struct compat_ipt_entry); |
1579 | ret = IPT_MATCH_ITERATE(e, compat_copy_match_from_user, dstptr, size, | 1569 | ret = IPT_MATCH_ITERATE(e, compat_copy_match_from_user, dstptr, size, |
1580 | name, &de->ip, de->comefrom, &j); | 1570 | name, &de->ip, de->comefrom); |
1581 | if (ret) | 1571 | if (ret) |
1582 | goto cleanup_matches; | 1572 | goto err; |
1583 | de->target_offset = e->target_offset - (origsize - *size); | 1573 | de->target_offset = e->target_offset - (origsize - *size); |
1584 | t = ipt_get_target(e); | 1574 | t = ipt_get_target(e); |
1585 | target = t->u.kernel.target; | 1575 | target = t->u.kernel.target; |
@@ -1613,12 +1603,7 @@ static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr, | |||
1613 | goto err; | 1603 | goto err; |
1614 | } | 1604 | } |
1615 | ret = 0; | 1605 | ret = 0; |
1616 | return ret; | ||
1617 | |||
1618 | err: | 1606 | err: |
1619 | module_put(t->u.kernel.target->me); | ||
1620 | cleanup_matches: | ||
1621 | IPT_MATCH_ITERATE(e, cleanup_match, &j); | ||
1622 | return ret; | 1607 | return ret; |
1623 | } | 1608 | } |
1624 | 1609 | ||
@@ -1632,7 +1617,7 @@ translate_compat_table(const char *name, | |||
1632 | unsigned int *hook_entries, | 1617 | unsigned int *hook_entries, |
1633 | unsigned int *underflows) | 1618 | unsigned int *underflows) |
1634 | { | 1619 | { |
1635 | unsigned int i; | 1620 | unsigned int i, j; |
1636 | struct xt_table_info *newinfo, *info; | 1621 | struct xt_table_info *newinfo, *info; |
1637 | void *pos, *entry0, *entry1; | 1622 | void *pos, *entry0, *entry1; |
1638 | unsigned int size; | 1623 | unsigned int size; |
@@ -1650,21 +1635,21 @@ translate_compat_table(const char *name, | |||
1650 | } | 1635 | } |
1651 | 1636 | ||
1652 | duprintf("translate_compat_table: size %u\n", info->size); | 1637 | duprintf("translate_compat_table: size %u\n", info->size); |
1653 | i = 0; | 1638 | j = 0; |
1654 | xt_compat_lock(AF_INET); | 1639 | xt_compat_lock(AF_INET); |
1655 | /* Walk through entries, checking offsets. */ | 1640 | /* Walk through entries, checking offsets. */ |
1656 | ret = IPT_ENTRY_ITERATE(entry0, total_size, | 1641 | ret = IPT_ENTRY_ITERATE(entry0, total_size, |
1657 | check_compat_entry_size_and_hooks, | 1642 | check_compat_entry_size_and_hooks, |
1658 | info, &size, entry0, | 1643 | info, &size, entry0, |
1659 | entry0 + total_size, | 1644 | entry0 + total_size, |
1660 | hook_entries, underflows, &i, name); | 1645 | hook_entries, underflows, &j, name); |
1661 | if (ret != 0) | 1646 | if (ret != 0) |
1662 | goto out_unlock; | 1647 | goto out_unlock; |
1663 | 1648 | ||
1664 | ret = -EINVAL; | 1649 | ret = -EINVAL; |
1665 | if (i != number) { | 1650 | if (j != number) { |
1666 | duprintf("translate_compat_table: %u not %u entries\n", | 1651 | duprintf("translate_compat_table: %u not %u entries\n", |
1667 | i, number); | 1652 | j, number); |
1668 | goto out_unlock; | 1653 | goto out_unlock; |
1669 | } | 1654 | } |
1670 | 1655 | ||
@@ -1723,6 +1708,7 @@ translate_compat_table(const char *name, | |||
1723 | free_newinfo: | 1708 | free_newinfo: |
1724 | xt_free_table_info(newinfo); | 1709 | xt_free_table_info(newinfo); |
1725 | out: | 1710 | out: |
1711 | IPT_ENTRY_ITERATE(entry0, total_size, cleanup_entry, &j); | ||
1726 | return ret; | 1712 | return ret; |
1727 | out_unlock: | 1713 | out_unlock: |
1728 | compat_flush_offsets(); | 1714 | compat_flush_offsets(); |