Commit dac1dc7e authored by Jakub Kicinski's avatar Jakub Kicinski

Merge git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf

Pablo Neira Ayuso says:

====================
Netfilter/IPVS fixes for net

1) netlink socket notifier might win race to release objects that are
   already pending to be released via commit release path, reported by
   syzbot.

2) No need to postpone flow rule release to commit release path, this
   triggered the syzbot report, complementary fix to previous patch.

3) Use explicit signed chars in IPVS to unbreak arm, from Jason A. Donenfeld.

4) Missing check for proc entry creation failure in IPVS, from Zhengchao Shao.

5) Incorrect error path handling when BPF NAT fails to register, from
   Chen Zhongjin.

6) Prevent huge memory allocation in ipset hash types, from Jozsef Kadlecsik.

Except the incorrect BPF NAT error path which is broken in 6.1-rc, anything
else has been broken for several releases.

* git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
  netfilter: ipset: enforce documented limit to prevent allocating huge memory
  netfilter: nf_nat: Fix possible memory leak in nf_nat_init()
  ipvs: fix WARNING in ip_vs_app_net_cleanup()
  ipvs: fix WARNING in __ip_vs_cleanup_batch()
  ipvs: use explicitly signed chars
  netfilter: nf_tables: release flow rule object from commit path
  netfilter: nf_tables: netlink notifier might race to release objects
====================

Link: https://lore.kernel.org/r/20221102184659.2502-1-pablo@netfilter.orgSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents ef1fdc93 510841da
...@@ -42,31 +42,8 @@ ...@@ -42,31 +42,8 @@
#define AHASH_MAX_SIZE (6 * AHASH_INIT_SIZE) #define AHASH_MAX_SIZE (6 * AHASH_INIT_SIZE)
/* Max muber of elements in the array block when tuned */ /* Max muber of elements in the array block when tuned */
#define AHASH_MAX_TUNED 64 #define AHASH_MAX_TUNED 64
#define AHASH_MAX(h) ((h)->bucketsize) #define AHASH_MAX(h) ((h)->bucketsize)
/* Max number of elements can be tuned */
#ifdef IP_SET_HASH_WITH_MULTI
static u8
tune_bucketsize(u8 curr, u32 multi)
{
u32 n;
if (multi < curr)
return curr;
n = curr + AHASH_INIT_SIZE;
/* Currently, at listing one hash bucket must fit into a message.
* Therefore we have a hard limit here.
*/
return n > curr && n <= AHASH_MAX_TUNED ? n : curr;
}
#define TUNE_BUCKETSIZE(h, multi) \
((h)->bucketsize = tune_bucketsize((h)->bucketsize, multi))
#else
#define TUNE_BUCKETSIZE(h, multi)
#endif
/* A hash bucket */ /* A hash bucket */
struct hbucket { struct hbucket {
struct rcu_head rcu; /* for call_rcu */ struct rcu_head rcu; /* for call_rcu */
...@@ -936,7 +913,12 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, ...@@ -936,7 +913,12 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
goto set_full; goto set_full;
/* Create a new slot */ /* Create a new slot */
if (n->pos >= n->size) { if (n->pos >= n->size) {
TUNE_BUCKETSIZE(h, multi); #ifdef IP_SET_HASH_WITH_MULTI
if (h->bucketsize >= AHASH_MAX_TUNED)
goto set_full;
else if (h->bucketsize < multi)
h->bucketsize += AHASH_INIT_SIZE;
#endif
if (n->size >= AHASH_MAX(h)) { if (n->size >= AHASH_MAX(h)) {
/* Trigger rehashing */ /* Trigger rehashing */
mtype_data_next(&h->next, d); mtype_data_next(&h->next, d);
......
...@@ -599,13 +599,19 @@ static const struct seq_operations ip_vs_app_seq_ops = { ...@@ -599,13 +599,19 @@ static const struct seq_operations ip_vs_app_seq_ops = {
int __net_init ip_vs_app_net_init(struct netns_ipvs *ipvs) int __net_init ip_vs_app_net_init(struct netns_ipvs *ipvs)
{ {
INIT_LIST_HEAD(&ipvs->app_list); INIT_LIST_HEAD(&ipvs->app_list);
proc_create_net("ip_vs_app", 0, ipvs->net->proc_net, &ip_vs_app_seq_ops, #ifdef CONFIG_PROC_FS
sizeof(struct seq_net_private)); if (!proc_create_net("ip_vs_app", 0, ipvs->net->proc_net,
&ip_vs_app_seq_ops,
sizeof(struct seq_net_private)))
return -ENOMEM;
#endif
return 0; return 0;
} }
void __net_exit ip_vs_app_net_cleanup(struct netns_ipvs *ipvs) void __net_exit ip_vs_app_net_cleanup(struct netns_ipvs *ipvs)
{ {
unregister_ip_vs_app(ipvs, NULL /* all */); unregister_ip_vs_app(ipvs, NULL /* all */);
#ifdef CONFIG_PROC_FS
remove_proc_entry("ip_vs_app", ipvs->net->proc_net); remove_proc_entry("ip_vs_app", ipvs->net->proc_net);
#endif
} }
...@@ -1265,8 +1265,8 @@ static inline int todrop_entry(struct ip_vs_conn *cp) ...@@ -1265,8 +1265,8 @@ static inline int todrop_entry(struct ip_vs_conn *cp)
* The drop rate array needs tuning for real environments. * The drop rate array needs tuning for real environments.
* Called from timer bh only => no locking * Called from timer bh only => no locking
*/ */
static const char todrop_rate[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; static const signed char todrop_rate[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
static char todrop_counter[9] = {0}; static signed char todrop_counter[9] = {0};
int i; int i;
/* if the conn entry hasn't lasted for 60 seconds, don't drop it. /* if the conn entry hasn't lasted for 60 seconds, don't drop it.
...@@ -1447,20 +1447,36 @@ int __net_init ip_vs_conn_net_init(struct netns_ipvs *ipvs) ...@@ -1447,20 +1447,36 @@ int __net_init ip_vs_conn_net_init(struct netns_ipvs *ipvs)
{ {
atomic_set(&ipvs->conn_count, 0); atomic_set(&ipvs->conn_count, 0);
proc_create_net("ip_vs_conn", 0, ipvs->net->proc_net, #ifdef CONFIG_PROC_FS
&ip_vs_conn_seq_ops, sizeof(struct ip_vs_iter_state)); if (!proc_create_net("ip_vs_conn", 0, ipvs->net->proc_net,
proc_create_net("ip_vs_conn_sync", 0, ipvs->net->proc_net, &ip_vs_conn_seq_ops,
&ip_vs_conn_sync_seq_ops, sizeof(struct ip_vs_iter_state)))
sizeof(struct ip_vs_iter_state)); goto err_conn;
if (!proc_create_net("ip_vs_conn_sync", 0, ipvs->net->proc_net,
&ip_vs_conn_sync_seq_ops,
sizeof(struct ip_vs_iter_state)))
goto err_conn_sync;
#endif
return 0; return 0;
#ifdef CONFIG_PROC_FS
err_conn_sync:
remove_proc_entry("ip_vs_conn", ipvs->net->proc_net);
err_conn:
return -ENOMEM;
#endif
} }
void __net_exit ip_vs_conn_net_cleanup(struct netns_ipvs *ipvs) void __net_exit ip_vs_conn_net_cleanup(struct netns_ipvs *ipvs)
{ {
/* flush all the connection entries first */ /* flush all the connection entries first */
ip_vs_conn_flush(ipvs); ip_vs_conn_flush(ipvs);
#ifdef CONFIG_PROC_FS
remove_proc_entry("ip_vs_conn", ipvs->net->proc_net); remove_proc_entry("ip_vs_conn", ipvs->net->proc_net);
remove_proc_entry("ip_vs_conn_sync", ipvs->net->proc_net); remove_proc_entry("ip_vs_conn_sync", ipvs->net->proc_net);
#endif
} }
int __init ip_vs_conn_init(void) int __init ip_vs_conn_init(void)
......
...@@ -1152,7 +1152,16 @@ static int __init nf_nat_init(void) ...@@ -1152,7 +1152,16 @@ static int __init nf_nat_init(void)
WARN_ON(nf_nat_hook != NULL); WARN_ON(nf_nat_hook != NULL);
RCU_INIT_POINTER(nf_nat_hook, &nat_hook); RCU_INIT_POINTER(nf_nat_hook, &nat_hook);
return register_nf_nat_bpf(); ret = register_nf_nat_bpf();
if (ret < 0) {
RCU_INIT_POINTER(nf_nat_hook, NULL);
nf_ct_helper_expectfn_unregister(&follow_master_nat);
synchronize_net();
unregister_pernet_subsys(&nat_net_ops);
kvfree(nf_nat_bysource);
}
return ret;
} }
static void __exit nf_nat_cleanup(void) static void __exit nf_nat_cleanup(void)
......
...@@ -8465,9 +8465,6 @@ static void nft_commit_release(struct nft_trans *trans) ...@@ -8465,9 +8465,6 @@ static void nft_commit_release(struct nft_trans *trans)
nf_tables_chain_destroy(&trans->ctx); nf_tables_chain_destroy(&trans->ctx);
break; break;
case NFT_MSG_DELRULE: case NFT_MSG_DELRULE:
if (trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)
nft_flow_rule_destroy(nft_trans_flow_rule(trans));
nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans)); nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
break; break;
case NFT_MSG_DELSET: case NFT_MSG_DELSET:
...@@ -8973,6 +8970,9 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) ...@@ -8973,6 +8970,9 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
nft_rule_expr_deactivate(&trans->ctx, nft_rule_expr_deactivate(&trans->ctx,
nft_trans_rule(trans), nft_trans_rule(trans),
NFT_TRANS_COMMIT); NFT_TRANS_COMMIT);
if (trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)
nft_flow_rule_destroy(nft_trans_flow_rule(trans));
break; break;
case NFT_MSG_NEWSET: case NFT_MSG_NEWSET:
nft_clear(net, nft_trans_set(trans)); nft_clear(net, nft_trans_set(trans));
...@@ -10030,6 +10030,8 @@ static int nft_rcv_nl_event(struct notifier_block *this, unsigned long event, ...@@ -10030,6 +10030,8 @@ static int nft_rcv_nl_event(struct notifier_block *this, unsigned long event,
nft_net = nft_pernet(net); nft_net = nft_pernet(net);
deleted = 0; deleted = 0;
mutex_lock(&nft_net->commit_mutex); mutex_lock(&nft_net->commit_mutex);
if (!list_empty(&nf_tables_destroy_list))
rcu_barrier();
again: again:
list_for_each_entry(table, &nft_net->tables, list) { list_for_each_entry(table, &nft_net->tables, list) {
if (nft_table_has_owner(table) && if (nft_table_has_owner(table) &&
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment