Re: [PATCH nf v2 2/2] netfilter: nf_conntrack_tcp: defer tcp_in_window invalid logging until after unlock
Florian Westphal <[email protected]> Thu, 30 Jul 2026 22:28:23 +0200
| Newsgroups | gmane.comp.security.firewalls.netfilter.devel |
|---|---|
| Message-ID | <[email protected]> |
Zihan Xi <[email protected]> wrote: > tcp_in_window() can emit several invalid-packet logs while ct->lock is > still held. If invalid logging is routed to nfnetlink_log with > conntrack export enabled, this can re-enter conntrack netlink glue and > recurse into tcp_to_nlattr() on the same conntrack. > > Fix this by storing only the minimal invalid-log context for the > remaining tcp_in_window() cases and emitting the actual log after > releasing ct->lock. Rename the helper to reflect that it now records > context instead of logging immediately, and add an explicit lockdep > assertion plus comment to document that invalid TCP logs must not be > emitted while ct->lock is held. > > Fixes: d48668052b26 ("netfilter: fix nf_l4proto_log_invalid to log invalid packets") > Cc: [email protected] > Reported-by: Vega <[email protected]> > Assisted-by: Codex:gpt-5.4 > Signed-off-by: Zihan Xi <[email protected]> > --- > changes in v2: > - Keep only the tcp_in_window() invalid logging cases in this patch. > - Rename the helper to reflect that it stores invalid-log context. > - Add a lockdep assertion and comment documenting that invalid logs must not be emitted under ct->lock. > - v1 Link: https://lore.kernel.org/all/[email protected]/ > --- > net/netfilter/nf_conntrack_proto_tcp.c | 113 ++++++++++++++++++------- > 1 file changed, 82 insertions(+), 31 deletions(-) > > diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c > index ef31dcaffd19..e5875b7528fe 100644 > --- a/net/netfilter/nf_conntrack_proto_tcp.c > +++ b/net/netfilter/nf_conntrack_proto_tcp.c > @@ -480,37 +480,87 @@ static void tcp_init_sender(struct ip_ct_tcp_state *sender, > } > } > > -__printf(6, 7) > -static enum nf_ct_tcp_action nf_tcp_log_invalid(const struct sk_buff *skb, > - const struct nf_conn *ct, > - const struct nf_hook_state *state, > - const struct ip_ct_tcp_state *sender, > - enum nf_ct_tcp_action ret, > - const char *fmt, ...) > +enum nf_tcp_invalid_log_type { > + NF_TCP_LOG_NONE, > + NF_TCP_LOG_OVERSHOT, > + NF_TCP_LOG_SEQ_OVER, > + NF_TCP_LOG_ACK_OVER, > + NF_TCP_LOG_SEQ_UNDER, > + NF_TCP_LOG_ACK_UNDER, > +}; > + > +struct nf_tcp_invalid_log { > + enum nf_tcp_invalid_log_type type; > + u32 value; > +}; > + > +static enum nf_ct_tcp_action > +nf_tcp_store_invalid(const struct nf_conn *ct, > + const struct ip_ct_tcp_state *sender, > + struct nf_tcp_invalid_log *log, > + enum nf_ct_tcp_action ret, > + enum nf_tcp_invalid_log_type type, > + u32 value) > { > const struct nf_tcp_net *tn = nf_tcp_pernet(nf_ct_net(ct)); > - struct va_format vaf; > - va_list args; > bool be_liberal; > > be_liberal = sender->flags & IP_CT_TCP_FLAG_BE_LIBERAL || tn->tcp_be_liberal; > if (be_liberal) > return NFCT_TCP_ACCEPT; > > - va_start(args, fmt); > - vaf.fmt = fmt; > - vaf.va = &args; > - nf_ct_l4proto_log_invalid(skb, ct, state, "%pV", &vaf); > - va_end(args); > - > + log->type = type; > + log->value = value; > return ret; > } > > +static void nf_tcp_log_invalid(const struct sk_buff *skb, > + const struct nf_conn *ct, > + const struct nf_hook_state *state, > + const struct nf_tcp_invalid_log *log) > +{ > + /* nfnetlink_log may re-enter conntrack attribute dumping and try to > + * take ct->lock again via tcp_to_nlattr(), so invalid TCP logs must > + * only be emitted after dropping ct->lock. > + */ > + lockdep_assert_not_held(&ct->lock); > + > + switch (log->type) { > + case NF_TCP_LOG_OVERSHOT: > + nf_ct_l4proto_log_invalid(skb, ct, state, > + "%u bytes more than expected", > + log->value); Can you move the comment and the lockdep assert into nf_ct_l4proto_log_invalid() so we can catch offenders outside tcp as well? SCTP seems to be buggy as well. Other than that: Reviewed-by: Florian Westphal <[email protected]> You can keep this tag in next iteration.