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 | |
| 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')
| -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(); |
