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/ipv4 | |
| 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/ipv4')
| -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 38e1e4fba0d..3d5d4a4640c 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 | ||
