diff options
author | Patrick McHardy <kaber@trash.net> | 2006-09-20 15:04:08 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-09-22 18:19:59 -0400 |
commit | bec71b162747708d4b45b0cd399b484f52f2901a (patch) | |
tree | 50f93845e00c9aed07383b9c9b003a2749672fd4 /net | |
parent | 1192e403e9ea2dc23bbbe2b4fe9bdbc47e8c6056 (diff) |
[NETFILTER]: ip_tables: fix module refcount leaks in compat error paths
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 | 39 |
1 files changed, 27 insertions, 12 deletions
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 38e1e4fba0db..3d5d4a4640c3 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c | |||
@@ -1529,7 +1529,7 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e, | |||
1529 | ret = IPT_MATCH_ITERATE(e, compat_check_calc_match, name, &e->ip, | 1529 | ret = IPT_MATCH_ITERATE(e, compat_check_calc_match, name, &e->ip, |
1530 | e->comefrom, &off, &j); | 1530 | e->comefrom, &off, &j); |
1531 | if (ret != 0) | 1531 | if (ret != 0) |
1532 | goto out; | 1532 | goto cleanup_matches; |
1533 | 1533 | ||
1534 | t = ipt_get_target(e); | 1534 | t = ipt_get_target(e); |
1535 | target = try_then_request_module(xt_find_target(AF_INET, | 1535 | target = try_then_request_module(xt_find_target(AF_INET, |
@@ -1539,7 +1539,7 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e, | |||
1539 | if (IS_ERR(target) || !target) { | 1539 | if (IS_ERR(target) || !target) { |
1540 | duprintf("check_entry: `%s' not found\n", t->u.user.name); | 1540 | duprintf("check_entry: `%s' not found\n", t->u.user.name); |
1541 | ret = target ? PTR_ERR(target) : -ENOENT; | 1541 | ret = target ? PTR_ERR(target) : -ENOENT; |
1542 | goto out; | 1542 | goto cleanup_matches; |
1543 | } | 1543 | } |
1544 | t->u.kernel.target = target; | 1544 | t->u.kernel.target = target; |
1545 | 1545 | ||
@@ -1566,14 +1566,17 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e, | |||
1566 | 1566 | ||
1567 | (*i)++; | 1567 | (*i)++; |
1568 | return 0; | 1568 | return 0; |
1569 | |||
1569 | out: | 1570 | out: |
1571 | module_put(t->u.kernel.target->me); | ||
1572 | cleanup_matches: | ||
1570 | IPT_MATCH_ITERATE(e, cleanup_match, &j); | 1573 | IPT_MATCH_ITERATE(e, cleanup_match, &j); |
1571 | return ret; | 1574 | return ret; |
1572 | } | 1575 | } |
1573 | 1576 | ||
1574 | static inline int compat_copy_match_from_user(struct ipt_entry_match *m, | 1577 | static inline int compat_copy_match_from_user(struct ipt_entry_match *m, |
1575 | void **dstptr, compat_uint_t *size, const char *name, | 1578 | void **dstptr, compat_uint_t *size, const char *name, |
1576 | const struct ipt_ip *ip, unsigned int hookmask) | 1579 | const struct ipt_ip *ip, unsigned int hookmask, int *i) |
1577 | { | 1580 | { |
1578 | struct ipt_entry_match *dm; | 1581 | struct ipt_entry_match *dm; |
1579 | struct ipt_match *match; | 1582 | struct ipt_match *match; |
@@ -1590,16 +1593,22 @@ static inline int compat_copy_match_from_user(struct ipt_entry_match *m, | |||
1590 | name, hookmask, ip->proto, | 1593 | name, hookmask, ip->proto, |
1591 | ip->invflags & IPT_INV_PROTO); | 1594 | ip->invflags & IPT_INV_PROTO); |
1592 | if (ret) | 1595 | if (ret) |
1593 | return ret; | 1596 | goto err; |
1594 | 1597 | ||
1595 | if (m->u.kernel.match->checkentry | 1598 | if (m->u.kernel.match->checkentry |
1596 | && !m->u.kernel.match->checkentry(name, ip, match, dm->data, | 1599 | && !m->u.kernel.match->checkentry(name, ip, match, dm->data, |
1597 | hookmask)) { | 1600 | hookmask)) { |
1598 | duprintf("ip_tables: check failed for `%s'.\n", | 1601 | duprintf("ip_tables: check failed for `%s'.\n", |
1599 | m->u.kernel.match->name); | 1602 | m->u.kernel.match->name); |
1600 | return -EINVAL; | 1603 | ret = -EINVAL; |
1604 | goto err; | ||
1601 | } | 1605 | } |
1606 | (*i)++; | ||
1602 | return 0; | 1607 | return 0; |
1608 | |||
1609 | err: | ||
1610 | module_put(m->u.kernel.match->me); | ||
1611 | return ret; | ||
1603 | } | 1612 | } |
1604 | 1613 | ||
1605 | static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr, | 1614 | static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr, |
@@ -1610,18 +1619,19 @@ static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr, | |||
1610 | struct ipt_target *target; | 1619 | struct ipt_target *target; |
1611 | struct ipt_entry *de; | 1620 | struct ipt_entry *de; |
1612 | unsigned int origsize; | 1621 | unsigned int origsize; |
1613 | int ret, h; | 1622 | int ret, h, j; |
1614 | 1623 | ||
1615 | ret = 0; | 1624 | ret = 0; |
1616 | origsize = *size; | 1625 | origsize = *size; |
1617 | de = (struct ipt_entry *)*dstptr; | 1626 | de = (struct ipt_entry *)*dstptr; |
1618 | memcpy(de, e, sizeof(struct ipt_entry)); | 1627 | memcpy(de, e, sizeof(struct ipt_entry)); |
1619 | 1628 | ||
1629 | j = 0; | ||
1620 | *dstptr += sizeof(struct compat_ipt_entry); | 1630 | *dstptr += sizeof(struct compat_ipt_entry); |
1621 | ret = IPT_MATCH_ITERATE(e, compat_copy_match_from_user, dstptr, size, | 1631 | ret = IPT_MATCH_ITERATE(e, compat_copy_match_from_user, dstptr, size, |
1622 | name, &de->ip, de->comefrom); | 1632 | name, &de->ip, de->comefrom, &j); |
1623 | if (ret) | 1633 | if (ret) |
1624 | goto out; | 1634 | goto cleanup_matches; |
1625 | de->target_offset = e->target_offset - (origsize - *size); | 1635 | de->target_offset = e->target_offset - (origsize - *size); |
1626 | t = ipt_get_target(e); | 1636 | t = ipt_get_target(e); |
1627 | target = t->u.kernel.target; | 1637 | target = t->u.kernel.target; |
@@ -1644,21 +1654,26 @@ static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr, | |||
1644 | name, e->comefrom, e->ip.proto, | 1654 | name, e->comefrom, e->ip.proto, |
1645 | e->ip.invflags & IPT_INV_PROTO); | 1655 | e->ip.invflags & IPT_INV_PROTO); |
1646 | if (ret) | 1656 | if (ret) |
1647 | goto out; | 1657 | goto err; |
1648 | 1658 | ||
1649 | ret = -EINVAL; | 1659 | ret = -EINVAL; |
1650 | if (t->u.kernel.target == &ipt_standard_target) { | 1660 | if (t->u.kernel.target == &ipt_standard_target) { |
1651 | if (!standard_check(t, *size)) | 1661 | if (!standard_check(t, *size)) |
1652 | goto out; | 1662 | goto err; |
1653 | } else if (t->u.kernel.target->checkentry | 1663 | } else if (t->u.kernel.target->checkentry |
1654 | && !t->u.kernel.target->checkentry(name, de, target, | 1664 | && !t->u.kernel.target->checkentry(name, de, target, |
1655 | t->data, de->comefrom)) { | 1665 | t->data, de->comefrom)) { |
1656 | duprintf("ip_tables: compat: check failed for `%s'.\n", | 1666 | duprintf("ip_tables: compat: check failed for `%s'.\n", |
1657 | t->u.kernel.target->name); | 1667 | t->u.kernel.target->name); |
1658 | goto out; | 1668 | goto err; |
1659 | } | 1669 | } |
1660 | ret = 0; | 1670 | ret = 0; |
1661 | out: | 1671 | return ret; |
1672 | |||
1673 | err: | ||
1674 | module_put(t->u.kernel.target->me); | ||
1675 | cleanup_matches: | ||
1676 | IPT_MATCH_ITERATE(e, cleanup_match, &j); | ||
1662 | return ret; | 1677 | return ret; |
1663 | } | 1678 | } |
1664 | 1679 | ||