Message ID | 20231127100040.1944-1-fw@strlen.de |
---|---|
State | Accepted |
Headers | show |
Series | [nf-next] netfilter: nf_tables: mark newset as dead on transaction abort | expand |
Hi Florian, kernel test robot noticed the following build errors: [auto build test ERROR on nf-next/master] url: https://github.com/intel-lab-lkp/linux/commits/Florian-Westphal/netfilter-nf_tables-mark-newset-as-dead-on-transaction-abort/20231127-180311 base: https://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next.git master patch link: https://lore.kernel.org/r/20231127100040.1944-1-fw%40strlen.de patch subject: [PATCH nf-next] netfilter: nf_tables: mark newset as dead on transaction abort config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20231128/202311280410.7aUI1BHd-lkp@intel.com/config) compiler: m68k-linux-gcc (GCC) 13.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231128/202311280410.7aUI1BHd-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202311280410.7aUI1BHd-lkp@intel.com/ All errors (new ones prefixed by >>): net/netfilter/nf_tables_api.c: In function '__nf_tables_abort': >> net/netfilter/nf_tables_api.c:9037:45: error: 'struct nft_set' has no member named 'dead' 9037 | nft_trans_set(trans)->dead = 1; | ^~ vim +9037 net/netfilter/nf_tables_api.c 8954 8955 static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) 8956 { 8957 struct nftables_pernet *nft_net = nft_pernet(net); 8958 struct nft_trans *trans, *next; 8959 struct nft_trans_elem *te; 8960 struct nft_hook *hook; 8961 8962 if (action == NFNL_ABORT_VALIDATE && 8963 nf_tables_validate(net) < 0) 8964 return -EAGAIN; 8965 8966 list_for_each_entry_safe_reverse(trans, next, &nft_net->commit_list, 8967 list) { 8968 switch (trans->msg_type) { 8969 case NFT_MSG_NEWTABLE: 8970 if (nft_trans_table_update(trans)) { 8971 if (!(trans->ctx.table->flags & __NFT_TABLE_F_UPDATE)) { 8972 nft_trans_destroy(trans); 8973 break; 8974 } 8975 if (trans->ctx.table->flags & __NFT_TABLE_F_WAS_DORMANT) { 8976 nf_tables_table_disable(net, trans->ctx.table); 8977 trans->ctx.table->flags |= NFT_TABLE_F_DORMANT; 8978 } else if (trans->ctx.table->flags & __NFT_TABLE_F_WAS_AWAKEN) { 8979 trans->ctx.table->flags &= ~NFT_TABLE_F_DORMANT; 8980 } 8981 trans->ctx.table->flags &= ~__NFT_TABLE_F_UPDATE; 8982 nft_trans_destroy(trans); 8983 } else { 8984 list_del_rcu(&trans->ctx.table->list); 8985 } 8986 break; 8987 case NFT_MSG_DELTABLE: 8988 nft_clear(trans->ctx.net, trans->ctx.table); 8989 nft_trans_destroy(trans); 8990 break; 8991 case NFT_MSG_NEWCHAIN: 8992 if (nft_trans_chain_update(trans)) { 8993 free_percpu(nft_trans_chain_stats(trans)); 8994 kfree(nft_trans_chain_name(trans)); 8995 nft_trans_destroy(trans); 8996 } else { 8997 if (nft_chain_is_bound(trans->ctx.chain)) { 8998 nft_trans_destroy(trans); 8999 break; 9000 } 9001 trans->ctx.table->use--; 9002 nft_chain_del(trans->ctx.chain); 9003 nf_tables_unregister_hook(trans->ctx.net, 9004 trans->ctx.table, 9005 trans->ctx.chain); 9006 } 9007 break; 9008 case NFT_MSG_DELCHAIN: 9009 trans->ctx.table->use++; 9010 nft_clear(trans->ctx.net, trans->ctx.chain); 9011 nft_trans_destroy(trans); 9012 break; 9013 case NFT_MSG_NEWRULE: 9014 trans->ctx.chain->use--; 9015 list_del_rcu(&nft_trans_rule(trans)->list); 9016 nft_rule_expr_deactivate(&trans->ctx, 9017 nft_trans_rule(trans), 9018 NFT_TRANS_ABORT); 9019 if (trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD) 9020 nft_flow_rule_destroy(nft_trans_flow_rule(trans)); 9021 break; 9022 case NFT_MSG_DELRULE: 9023 trans->ctx.chain->use++; 9024 nft_clear(trans->ctx.net, nft_trans_rule(trans)); 9025 nft_rule_expr_activate(&trans->ctx, nft_trans_rule(trans)); 9026 if (trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD) 9027 nft_flow_rule_destroy(nft_trans_flow_rule(trans)); 9028 9029 nft_trans_destroy(trans); 9030 break; 9031 case NFT_MSG_NEWSET: 9032 trans->ctx.table->use--; 9033 if (nft_trans_set_bound(trans)) { 9034 nft_trans_destroy(trans); 9035 break; 9036 } > 9037 nft_trans_set(trans)->dead = 1; 9038 list_del_rcu(&nft_trans_set(trans)->list); 9039 break; 9040 case NFT_MSG_DELSET: 9041 trans->ctx.table->use++; 9042 nft_clear(trans->ctx.net, nft_trans_set(trans)); 9043 nft_trans_destroy(trans); 9044 break; 9045 case NFT_MSG_NEWSETELEM: 9046 if (nft_trans_elem_set_bound(trans)) { 9047 nft_trans_destroy(trans); 9048 break; 9049 } 9050 te = (struct nft_trans_elem *)trans->data; 9051 nft_setelem_remove(net, te->set, &te->elem); 9052 if (!nft_setelem_is_catchall(te->set, &te->elem)) 9053 atomic_dec(&te->set->nelems); 9054 break; 9055 case NFT_MSG_DELSETELEM: 9056 te = (struct nft_trans_elem *)trans->data; 9057 9058 nft_setelem_data_activate(net, te->set, &te->elem); 9059 nft_setelem_activate(net, te->set, &te->elem); 9060 if (!nft_setelem_is_catchall(te->set, &te->elem)) 9061 te->set->ndeact--; 9062 9063 nft_trans_destroy(trans); 9064 break; 9065 case NFT_MSG_NEWOBJ: 9066 if (nft_trans_obj_update(trans)) { 9067 nft_obj_destroy(&trans->ctx, nft_trans_obj_newobj(trans)); 9068 nft_trans_destroy(trans); 9069 } else { 9070 trans->ctx.table->use--; 9071 nft_obj_del(nft_trans_obj(trans)); 9072 } 9073 break; 9074 case NFT_MSG_DELOBJ: 9075 trans->ctx.table->use++; 9076 nft_clear(trans->ctx.net, nft_trans_obj(trans)); 9077 nft_trans_destroy(trans); 9078 break; 9079 case NFT_MSG_NEWFLOWTABLE: 9080 if (nft_trans_flowtable_update(trans)) { 9081 nft_unregister_flowtable_net_hooks(net, 9082 &nft_trans_flowtable_hooks(trans)); 9083 } else { 9084 trans->ctx.table->use--; 9085 list_del_rcu(&nft_trans_flowtable(trans)->list); 9086 nft_unregister_flowtable_net_hooks(net, 9087 &nft_trans_flowtable(trans)->hook_list); 9088 } 9089 break; 9090 case NFT_MSG_DELFLOWTABLE: 9091 if (nft_trans_flowtable_update(trans)) { 9092 list_for_each_entry(hook, &nft_trans_flowtable(trans)->hook_list, list) 9093 hook->inactive = false; 9094 } else { 9095 trans->ctx.table->use++; 9096 nft_clear(trans->ctx.net, nft_trans_flowtable(trans)); 9097 } 9098 nft_trans_destroy(trans); 9099 break; 9100 } 9101 } 9102 9103 synchronize_rcu(); 9104 9105 list_for_each_entry_safe_reverse(trans, next, 9106 &nft_net->commit_list, list) { 9107 list_del(&trans->list); 9108 nf_tables_abort_release(trans); 9109 } 9110 9111 if (action == NFNL_ABORT_AUTOLOAD) 9112 nf_tables_module_autoload(net); 9113 else 9114 nf_tables_module_autoload_cleanup(net); 9115 9116 return 0; 9117 } 9118
Hi Florian, kernel test robot noticed the following build errors: [auto build test ERROR on nf-next/master] url: https://github.com/intel-lab-lkp/linux/commits/Florian-Westphal/netfilter-nf_tables-mark-newset-as-dead-on-transaction-abort/20231127-180311 base: https://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next.git master patch link: https://lore.kernel.org/r/20231127100040.1944-1-fw%40strlen.de patch subject: [PATCH nf-next] netfilter: nf_tables: mark newset as dead on transaction abort config: x86_64-rhel-8.3-rust (https://download.01.org/0day-ci/archive/20231128/202311280656.UdPzWRXm-lkp@intel.com/config) compiler: clang version 16.0.4 (https://github.com/llvm/llvm-project.git ae42196bc493ffe877a7e3dff8be32035dea4d07) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231128/202311280656.UdPzWRXm-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202311280656.UdPzWRXm-lkp@intel.com/ All errors (new ones prefixed by >>): >> net/netfilter/nf_tables_api.c:9037:26: error: no member named 'dead' in 'struct nft_set' nft_trans_set(trans)->dead = 1; ~~~~~~~~~~~~~~~~~~~~ ^ 1 error generated. vim +9037 net/netfilter/nf_tables_api.c 8954 8955 static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) 8956 { 8957 struct nftables_pernet *nft_net = nft_pernet(net); 8958 struct nft_trans *trans, *next; 8959 struct nft_trans_elem *te; 8960 struct nft_hook *hook; 8961 8962 if (action == NFNL_ABORT_VALIDATE && 8963 nf_tables_validate(net) < 0) 8964 return -EAGAIN; 8965 8966 list_for_each_entry_safe_reverse(trans, next, &nft_net->commit_list, 8967 list) { 8968 switch (trans->msg_type) { 8969 case NFT_MSG_NEWTABLE: 8970 if (nft_trans_table_update(trans)) { 8971 if (!(trans->ctx.table->flags & __NFT_TABLE_F_UPDATE)) { 8972 nft_trans_destroy(trans); 8973 break; 8974 } 8975 if (trans->ctx.table->flags & __NFT_TABLE_F_WAS_DORMANT) { 8976 nf_tables_table_disable(net, trans->ctx.table); 8977 trans->ctx.table->flags |= NFT_TABLE_F_DORMANT; 8978 } else if (trans->ctx.table->flags & __NFT_TABLE_F_WAS_AWAKEN) { 8979 trans->ctx.table->flags &= ~NFT_TABLE_F_DORMANT; 8980 } 8981 trans->ctx.table->flags &= ~__NFT_TABLE_F_UPDATE; 8982 nft_trans_destroy(trans); 8983 } else { 8984 list_del_rcu(&trans->ctx.table->list); 8985 } 8986 break; 8987 case NFT_MSG_DELTABLE: 8988 nft_clear(trans->ctx.net, trans->ctx.table); 8989 nft_trans_destroy(trans); 8990 break; 8991 case NFT_MSG_NEWCHAIN: 8992 if (nft_trans_chain_update(trans)) { 8993 free_percpu(nft_trans_chain_stats(trans)); 8994 kfree(nft_trans_chain_name(trans)); 8995 nft_trans_destroy(trans); 8996 } else { 8997 if (nft_chain_is_bound(trans->ctx.chain)) { 8998 nft_trans_destroy(trans); 8999 break; 9000 } 9001 trans->ctx.table->use--; 9002 nft_chain_del(trans->ctx.chain); 9003 nf_tables_unregister_hook(trans->ctx.net, 9004 trans->ctx.table, 9005 trans->ctx.chain); 9006 } 9007 break; 9008 case NFT_MSG_DELCHAIN: 9009 trans->ctx.table->use++; 9010 nft_clear(trans->ctx.net, trans->ctx.chain); 9011 nft_trans_destroy(trans); 9012 break; 9013 case NFT_MSG_NEWRULE: 9014 trans->ctx.chain->use--; 9015 list_del_rcu(&nft_trans_rule(trans)->list); 9016 nft_rule_expr_deactivate(&trans->ctx, 9017 nft_trans_rule(trans), 9018 NFT_TRANS_ABORT); 9019 if (trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD) 9020 nft_flow_rule_destroy(nft_trans_flow_rule(trans)); 9021 break; 9022 case NFT_MSG_DELRULE: 9023 trans->ctx.chain->use++; 9024 nft_clear(trans->ctx.net, nft_trans_rule(trans)); 9025 nft_rule_expr_activate(&trans->ctx, nft_trans_rule(trans)); 9026 if (trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD) 9027 nft_flow_rule_destroy(nft_trans_flow_rule(trans)); 9028 9029 nft_trans_destroy(trans); 9030 break; 9031 case NFT_MSG_NEWSET: 9032 trans->ctx.table->use--; 9033 if (nft_trans_set_bound(trans)) { 9034 nft_trans_destroy(trans); 9035 break; 9036 } > 9037 nft_trans_set(trans)->dead = 1; 9038 list_del_rcu(&nft_trans_set(trans)->list); 9039 break; 9040 case NFT_MSG_DELSET: 9041 trans->ctx.table->use++; 9042 nft_clear(trans->ctx.net, nft_trans_set(trans)); 9043 nft_trans_destroy(trans); 9044 break; 9045 case NFT_MSG_NEWSETELEM: 9046 if (nft_trans_elem_set_bound(trans)) { 9047 nft_trans_destroy(trans); 9048 break; 9049 } 9050 te = (struct nft_trans_elem *)trans->data; 9051 nft_setelem_remove(net, te->set, &te->elem); 9052 if (!nft_setelem_is_catchall(te->set, &te->elem)) 9053 atomic_dec(&te->set->nelems); 9054 break; 9055 case NFT_MSG_DELSETELEM: 9056 te = (struct nft_trans_elem *)trans->data; 9057 9058 nft_setelem_data_activate(net, te->set, &te->elem); 9059 nft_setelem_activate(net, te->set, &te->elem); 9060 if (!nft_setelem_is_catchall(te->set, &te->elem)) 9061 te->set->ndeact--; 9062 9063 nft_trans_destroy(trans); 9064 break; 9065 case NFT_MSG_NEWOBJ: 9066 if (nft_trans_obj_update(trans)) { 9067 nft_obj_destroy(&trans->ctx, nft_trans_obj_newobj(trans)); 9068 nft_trans_destroy(trans); 9069 } else { 9070 trans->ctx.table->use--; 9071 nft_obj_del(nft_trans_obj(trans)); 9072 } 9073 break; 9074 case NFT_MSG_DELOBJ: 9075 trans->ctx.table->use++; 9076 nft_clear(trans->ctx.net, nft_trans_obj(trans)); 9077 nft_trans_destroy(trans); 9078 break; 9079 case NFT_MSG_NEWFLOWTABLE: 9080 if (nft_trans_flowtable_update(trans)) { 9081 nft_unregister_flowtable_net_hooks(net, 9082 &nft_trans_flowtable_hooks(trans)); 9083 } else { 9084 trans->ctx.table->use--; 9085 list_del_rcu(&nft_trans_flowtable(trans)->list); 9086 nft_unregister_flowtable_net_hooks(net, 9087 &nft_trans_flowtable(trans)->hook_list); 9088 } 9089 break; 9090 case NFT_MSG_DELFLOWTABLE: 9091 if (nft_trans_flowtable_update(trans)) { 9092 list_for_each_entry(hook, &nft_trans_flowtable(trans)->hook_list, list) 9093 hook->inactive = false; 9094 } else { 9095 trans->ctx.table->use++; 9096 nft_clear(trans->ctx.net, nft_trans_flowtable(trans)); 9097 } 9098 nft_trans_destroy(trans); 9099 break; 9100 } 9101 } 9102 9103 synchronize_rcu(); 9104 9105 list_for_each_entry_safe_reverse(trans, next, 9106 &nft_net->commit_list, list) { 9107 list_del(&trans->list); 9108 nf_tables_abort_release(trans); 9109 } 9110 9111 if (action == NFNL_ABORT_AUTOLOAD) 9112 nf_tables_module_autoload(net); 9113 else 9114 nf_tables_module_autoload_cleanup(net); 9115 9116 return 0; 9117 } 9118
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index c0a42989b982..0eafbc9f773c 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -10382,6 +10382,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) nft_trans_destroy(trans); break; } + nft_trans_set(trans)->dead = 1; list_del_rcu(&nft_trans_set(trans)->list); break; case NFT_MSG_DELSET:
If a transaction is aborted, we should mark the to-be-released NEWSET dead, just like commit path does for DEL and DESTROYSET commands. In both cases all remaining elements will be released via set->ops->destroy(). The existing abort code does NOT post the actual release to the work queue. Also the entire __nf_tables_abort() function is wrapped in gc_seq begin/end pair. Therefore, async gc worker will never try to release the pending set elements, as gc sequence is always stale. It might be possible to speed up transaction aborts via work queue too, this would result in a race and a possible use-after-free. So fix this before it becomes an issue. Fixes: 5f68718b34a5 ("netfilter: nf_tables: GC transaction API to avoid race with control plane") Signed-off-by: Florian Westphal <fw@strlen.de> --- net/netfilter/nf_tables_api.c | 1 + 1 file changed, 1 insertion(+)