diff options
author | Jakub Kicinski <kuba@kernel.org> | 2023-02-22 21:25:23 -0800 |
---|---|---|
committer | Jakub Kicinski <kuba@kernel.org> | 2023-02-22 21:25:23 -0800 |
commit | fd2a55e74a991ae5ff531c9da52963277dc7fbd5 (patch) | |
tree | 88939886a927b6e878f78898d229367798f6ce6f /net | |
parent | 67d93ffc0f3c47094750bde6d62e7c5765dc47a6 (diff) | |
parent | 0af8c09c896810879387decfba8c942994bb61f5 (diff) | |
download | linux-stable-fd2a55e74a991ae5ff531c9da52963277dc7fbd5.tar.gz linux-stable-fd2a55e74a991ae5ff531c9da52963277dc7fbd5.tar.bz2 linux-stable-fd2a55e74a991ae5ff531c9da52963277dc7fbd5.zip |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf
Pablo Neira Ayuso says:
====================
Netfilter fixes for net
1) Fix broken listing of set elements when table has an owner.
2) Fix conntrack refcount leak in ctnetlink with related conntrack
entries, from Hangyu Hua.
3) Fix use-after-free/double-free in ctnetlink conntrack insert path,
from Florian Westphal.
4) Fix ip6t_rpfilter with VRF, from Phil Sutter.
5) Fix use-after-free in ebtables reported by syzbot, also from Florian.
6) Use skb->len in xt_length to deal with IPv6 jumbo packets,
from Xin Long.
7) Fix NETLINK_LISTEN_ALL_NSID with ctnetlink, from Florian Westphal.
8) Fix memleak in {ip_,ip6_,arp_}tables in ENOMEM error case,
from Pavel Tikhomirov.
* git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
netfilter: x_tables: fix percpu counter block leak on error path when creating new netns
netfilter: ctnetlink: make event listener tracking global
netfilter: xt_length: use skb len to match in length_mt6
netfilter: ebtables: fix table blob use-after-free
netfilter: ip6t_rpfilter: Fix regression with VRF interfaces
netfilter: conntrack: fix rmmod double-free race
netfilter: ctnetlink: fix possible refcount leak in ctnetlink_create_conntrack()
netfilter: nf_tables: allow to fetch set elements when table has an owner
====================
Link: https://lore.kernel.org/r/20230222092137.88637-1-pablo@netfilter.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/bridge/netfilter/ebtables.c | 2 | ||||
-rw-r--r-- | net/ipv4/netfilter/arp_tables.c | 4 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_tables.c | 7 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6_tables.c | 7 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_rpfilter.c | 4 | ||||
-rw-r--r-- | net/netfilter/core.c | 3 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_bpf.c | 1 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_core.c | 25 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_ecache.c | 2 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_netlink.c | 8 | ||||
-rw-r--r-- | net/netfilter/nf_tables_api.c | 2 | ||||
-rw-r--r-- | net/netfilter/nfnetlink.c | 9 | ||||
-rw-r--r-- | net/netfilter/xt_length.c | 3 |
13 files changed, 48 insertions, 29 deletions
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index ce5dfa3babd2..757ec46fc45a 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1090,7 +1090,7 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl, audit_log_nfcfg(repl->name, AF_BRIDGE, repl->nentries, AUDIT_XT_OP_REPLACE, GFP_KERNEL); - return ret; + return 0; free_unlock: mutex_unlock(&ebt_mutex); diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index ffc0cab7cf18..2407066b0fec 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -1525,6 +1525,10 @@ int arpt_register_table(struct net *net, new_table = xt_register_table(net, table, &bootstrap, newinfo); if (IS_ERR(new_table)) { + struct arpt_entry *iter; + + xt_entry_foreach(iter, loc_cpu_entry, newinfo->size) + cleanup_entry(iter, net); xt_free_table_info(newinfo); return PTR_ERR(new_table); } diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 2ed7c58b471a..da5998011ab9 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -1045,7 +1045,6 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks, struct xt_counters *counters; struct ipt_entry *iter; - ret = 0; counters = xt_counters_alloc(num_counters); if (!counters) { ret = -ENOMEM; @@ -1091,7 +1090,7 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks, net_warn_ratelimited("iptables: counters copy to user failed while replacing table\n"); } vfree(counters); - return ret; + return 0; put_module: module_put(t->me); @@ -1742,6 +1741,10 @@ int ipt_register_table(struct net *net, const struct xt_table *table, new_table = xt_register_table(net, table, &bootstrap, newinfo); if (IS_ERR(new_table)) { + struct ipt_entry *iter; + + xt_entry_foreach(iter, loc_cpu_entry, newinfo->size) + cleanup_entry(iter, net); xt_free_table_info(newinfo); return PTR_ERR(new_table); } diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 2d816277f2c5..0ce0ed17c758 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -1062,7 +1062,6 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks, struct xt_counters *counters; struct ip6t_entry *iter; - ret = 0; counters = xt_counters_alloc(num_counters); if (!counters) { ret = -ENOMEM; @@ -1108,7 +1107,7 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks, net_warn_ratelimited("ip6tables: counters copy to user failed while replacing table\n"); } vfree(counters); - return ret; + return 0; put_module: module_put(t->me); @@ -1751,6 +1750,10 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, new_table = xt_register_table(net, table, &bootstrap, newinfo); if (IS_ERR(new_table)) { + struct ip6t_entry *iter; + + xt_entry_foreach(iter, loc_cpu_entry, newinfo->size) + cleanup_entry(iter, net); xt_free_table_info(newinfo); return PTR_ERR(new_table); } diff --git a/net/ipv6/netfilter/ip6t_rpfilter.c b/net/ipv6/netfilter/ip6t_rpfilter.c index a01d9b842bd0..67c87a88cde4 100644 --- a/net/ipv6/netfilter/ip6t_rpfilter.c +++ b/net/ipv6/netfilter/ip6t_rpfilter.c @@ -72,7 +72,9 @@ static bool rpfilter_lookup_reverse6(struct net *net, const struct sk_buff *skb, goto out; } - if (rt->rt6i_idev->dev == dev || (flags & XT_RPFILTER_LOOSE)) + if (rt->rt6i_idev->dev == dev || + l3mdev_master_ifindex_rcu(rt->rt6i_idev->dev) == dev->ifindex || + (flags & XT_RPFILTER_LOOSE)) ret = true; out: ip6_rt_put(rt); diff --git a/net/netfilter/core.c b/net/netfilter/core.c index b2fdbbed2b4b..358220b58521 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -669,6 +669,9 @@ const struct nf_ct_hook __rcu *nf_ct_hook __read_mostly; EXPORT_SYMBOL_GPL(nf_ct_hook); #if IS_ENABLED(CONFIG_NF_CONNTRACK) +u8 nf_ctnetlink_has_listener; +EXPORT_SYMBOL_GPL(nf_ctnetlink_has_listener); + const struct nf_nat_hook __rcu *nf_nat_hook __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_hook); diff --git a/net/netfilter/nf_conntrack_bpf.c b/net/netfilter/nf_conntrack_bpf.c index 34913521c385..cd99e6dc1f35 100644 --- a/net/netfilter/nf_conntrack_bpf.c +++ b/net/netfilter/nf_conntrack_bpf.c @@ -381,7 +381,6 @@ __bpf_kfunc struct nf_conn *bpf_ct_insert_entry(struct nf_conn___init *nfct_i) struct nf_conn *nfct = (struct nf_conn *)nfct_i; int err; - nfct->status |= IPS_CONFIRMED; err = nf_conntrack_hash_check_insert(nfct); if (err < 0) { nf_conntrack_free(nfct); diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 70c4f892174e..7250082e7de5 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -884,10 +884,8 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) zone = nf_ct_zone(ct); - if (!nf_ct_ext_valid_pre(ct->ext)) { - NF_CT_STAT_INC_ATOMIC(net, insert_failed); - return -ETIMEDOUT; - } + if (!nf_ct_ext_valid_pre(ct->ext)) + return -EAGAIN; local_bh_disable(); do { @@ -922,6 +920,19 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) goto chaintoolong; } + /* If genid has changed, we can't insert anymore because ct + * extensions could have stale pointers and nf_ct_iterate_destroy + * might have completed its table scan already. + * + * Increment of the ext genid right after this check is fine: + * nf_ct_iterate_destroy blocks until locks are released. + */ + if (!nf_ct_ext_valid_post(ct->ext)) { + err = -EAGAIN; + goto out; + } + + ct->status |= IPS_CONFIRMED; smp_wmb(); /* The caller holds a reference to this object */ refcount_set(&ct->ct_general.use, 2); @@ -930,12 +941,6 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) NF_CT_STAT_INC(net, insert); local_bh_enable(); - if (!nf_ct_ext_valid_post(ct->ext)) { - nf_ct_kill(ct); - NF_CT_STAT_INC_ATOMIC(net, drop); - return -ETIMEDOUT; - } - return 0; chaintoolong: NF_CT_STAT_INC(net, chaintoolong); diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index 8698b3424646..69948e1d6974 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -309,7 +309,7 @@ bool nf_ct_ecache_ext_add(struct nf_conn *ct, u16 ctmask, u16 expmask, gfp_t gfp break; return true; case 2: /* autodetect: no event listener, don't allocate extension. */ - if (!READ_ONCE(net->ct.ctnetlink_has_listener)) + if (!READ_ONCE(nf_ctnetlink_has_listener)) return true; fallthrough; case 1: diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 308fc0023c7e..c11dff91d52d 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -2316,9 +2316,6 @@ ctnetlink_create_conntrack(struct net *net, nfct_seqadj_ext_add(ct); nfct_synproxy_ext_add(ct); - /* we must add conntrack extensions before confirmation. */ - ct->status |= IPS_CONFIRMED; - if (cda[CTA_STATUS]) { err = ctnetlink_change_status(ct, cda); if (err < 0) @@ -2375,12 +2372,15 @@ ctnetlink_create_conntrack(struct net *net, err = nf_conntrack_hash_check_insert(ct); if (err < 0) - goto err2; + goto err3; rcu_read_unlock(); return ct; +err3: + if (ct->master) + nf_ct_put(ct->master); err2: rcu_read_unlock(); err1: diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index d73edbd4eec4..6004d4b24451 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -5507,7 +5507,7 @@ static int nf_tables_getsetelem(struct sk_buff *skb, int rem, err = 0; table = nft_table_lookup(net, nla[NFTA_SET_ELEM_LIST_TABLE], family, - genmask, NETLINK_CB(skb).portid); + genmask, 0); if (IS_ERR(table)) { NL_SET_BAD_ATTR(extack, nla[NFTA_SET_ELEM_LIST_TABLE]); return PTR_ERR(table); diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 6d18fb346868..81c7737c803a 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -29,6 +29,7 @@ #include <net/netlink.h> #include <net/netns/generic.h> +#include <linux/netfilter.h> #include <linux/netfilter/nfnetlink.h> MODULE_LICENSE("GPL"); @@ -685,12 +686,12 @@ static void nfnetlink_bind_event(struct net *net, unsigned int group) group_bit = (1 << group); spin_lock(&nfnl_grp_active_lock); - v = READ_ONCE(net->ct.ctnetlink_has_listener); + v = READ_ONCE(nf_ctnetlink_has_listener); if ((v & group_bit) == 0) { v |= group_bit; /* read concurrently without nfnl_grp_active_lock held. */ - WRITE_ONCE(net->ct.ctnetlink_has_listener, v); + WRITE_ONCE(nf_ctnetlink_has_listener, v); } spin_unlock(&nfnl_grp_active_lock); @@ -744,12 +745,12 @@ static void nfnetlink_unbind(struct net *net, int group) spin_lock(&nfnl_grp_active_lock); if (!nfnetlink_has_listeners(net, group)) { - u8 v = READ_ONCE(net->ct.ctnetlink_has_listener); + u8 v = READ_ONCE(nf_ctnetlink_has_listener); v &= ~group_bit; /* read concurrently without nfnl_grp_active_lock held. */ - WRITE_ONCE(net->ct.ctnetlink_has_listener, v); + WRITE_ONCE(nf_ctnetlink_has_listener, v); } spin_unlock(&nfnl_grp_active_lock); #endif diff --git a/net/netfilter/xt_length.c b/net/netfilter/xt_length.c index b3d623a52885..ca730cedb5d4 100644 --- a/net/netfilter/xt_length.c +++ b/net/netfilter/xt_length.c @@ -30,8 +30,7 @@ static bool length_mt6(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_length_info *info = par->matchinfo; - const u_int16_t pktlen = ntohs(ipv6_hdr(skb)->payload_len) + - sizeof(struct ipv6hdr); + u32 pktlen = skb->len; return (pktlen >= info->min && pktlen <= info->max) ^ info->invert; } |