netfilter: nfnetlink_cthelper: use {READ,WRITE}_ONCE for accessing helper flags

Conntrack helper flags are accessed from packet and netlink dump path.
Concurrent update of userspace helper flags is not possible, because the
nfnl_mutex in held on updates. These flags are only used by userspace
helpers. Use {READ,WRITE}_ONCE() to access this flags from lockless
paths.

Fixes: 12f7a50533 ("netfilter: add user-space connection tracking helper infrastructure")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Pablo Neira Ayuso 2026-05-26 18:40:44 +02:00
parent a625c94144
commit f8bf5edf71
2 changed files with 16 additions and 8 deletions

View File

@ -2213,6 +2213,7 @@ static int nf_confirm_cthelper(struct sk_buff *skb, struct nf_conn *ct,
{
const struct nf_conntrack_helper *helper;
const struct nf_conn_help *help;
unsigned int helper_flags;
int protoff;
help = nfct_help(ct);
@ -2223,7 +2224,8 @@ static int nf_confirm_cthelper(struct sk_buff *skb, struct nf_conn *ct,
if (!helper)
return NF_ACCEPT;
if (!(helper->flags & NF_CT_HELPER_F_USERSPACE))
helper_flags = READ_ONCE(helper->flags);
if (!(helper_flags & NF_CT_HELPER_F_USERSPACE))
return NF_ACCEPT;
switch (nf_ct_l3num(ct)) {

View File

@ -41,8 +41,9 @@ static int
nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff,
struct nf_conn *ct, enum ip_conntrack_info ctinfo)
{
const struct nf_conn_help *help;
struct nf_conntrack_helper *helper;
const struct nf_conn_help *help;
unsigned int helper_flags;
help = nfct_help(ct);
if (help == NULL)
@ -53,8 +54,10 @@ nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff,
if (helper == NULL)
return NF_DROP;
helper_flags = READ_ONCE(helper->flags);
/* This is a user-space helper not yet configured, skip. */
if ((helper->flags &
if ((helper_flags &
(NF_CT_HELPER_F_USERSPACE | NF_CT_HELPER_F_CONFIGURED)) ==
NF_CT_HELPER_F_USERSPACE)
return NF_ACCEPT;
@ -404,10 +407,10 @@ nfnl_cthelper_update(const struct nlattr * const tb[],
switch(status) {
case NFCT_HELPER_STATUS_ENABLED:
helper->flags |= NF_CT_HELPER_F_CONFIGURED;
WRITE_ONCE(helper->flags, helper->flags | NF_CT_HELPER_F_CONFIGURED);
break;
case NFCT_HELPER_STATUS_DISABLED:
helper->flags &= ~NF_CT_HELPER_F_CONFIGURED;
WRITE_ONCE(helper->flags, helper->flags & ~NF_CT_HELPER_F_CONFIGURED);
break;
}
}
@ -529,8 +532,8 @@ static int
nfnl_cthelper_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
int event, struct nf_conntrack_helper *helper)
{
struct nlmsghdr *nlh;
unsigned int flags = portid ? NLM_F_MULTI : 0;
struct nlmsghdr *nlh;
int status;
event = nfnl_msg_type(NFNL_SUBSYS_CTHELPER, event);
@ -554,7 +557,7 @@ nfnl_cthelper_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
if (nla_put_be32(skb, NFCTH_PRIV_DATA_LEN, htonl(helper->data_len)))
goto nla_put_failure;
if (helper->flags & NF_CT_HELPER_F_CONFIGURED)
if (READ_ONCE(helper->flags) & NF_CT_HELPER_F_CONFIGURED)
status = NFCT_HELPER_STATUS_ENABLED;
else
status = NFCT_HELPER_STATUS_DISABLED;
@ -575,6 +578,7 @@ static int
nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
{
struct nf_conntrack_helper *cur, *last;
unsigned int helper_flags;
rcu_read_lock();
last = (struct nf_conntrack_helper *)cb->args[1];
@ -583,8 +587,10 @@ nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
hlist_for_each_entry_rcu(cur,
&nf_ct_helper_hash[cb->args[0]], hnode) {
helper_flags = READ_ONCE(cur->flags);
/* skip non-userspace conntrack helpers. */
if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
if (!(helper_flags & NF_CT_HELPER_F_USERSPACE))
continue;
if (cb->args[1]) {