From nobody Tue Feb 17 14:29:45 2026 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4fFhqY48mhz6QHFW for ; Tue, 17 Feb 2026 14:29:45 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R12" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4fFhqY2Btgz3sdF for ; Tue, 17 Feb 2026 14:29:45 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1771338585; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=QrWHrPoTis1m8pFWGHA+7KVcrCh9f5MgVkGtEUMaCzA=; b=ix3WzgXWa0kZFROLGK0+9TZ7BwEIiS/XwjkmuUqo/WV3H3FD92NLjpSdeooMx/hVrjjlIs Mff4p+KPLLitx2grXpvN8sGYe3RkckLqLWjcmIwmqclsuHwwxpoEYobCHJhSxFbR/rXUxA GmrvD15WtzBozeikD49kfEKDj0U0775Zys1s/pxBv4P0F85rOeAYDPG/a71NwiJB1Cm8Jq ZCYEaDvYfZjlqlXDxHBq+AdLU3dEV8c5pNTKOGTrGuQpP1HjIZUwwKRW1aAE1j4Effb/xx cp2Zy1eHChVGGzyXBx1cXB1xIo4WRJM5Px2JsdUkvCJ3cP2ztioN0HGlk2BqBw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1771338585; a=rsa-sha256; cv=none; b=HF1GepH+/8y5aT08Tqo6wqRVjdUV/x2+jj3ATIGi/rMiRSQOFITgQ12PB2+ni1JCM5l5Dq a5sYYCVXQeIv5pDtEisNMM3rAUnEqBir/bE3DNrftq0Ld+lTmqwcZ72/Uji/hY0anVDA5Q 3RZJZePBw8KioOnf5++fuqAOmfILAbRSp1amorLQfNICMtF2to/BhK/nwXfAXQArhz3iA7 gCdt4l9NvUjfslUv53h7xPvKi0WMzBRFn0HysSNdfwGA/hYvGN666nadC5Nd9B9S2x8i8Q wYtSjjNLdc8R7cNFuRcjA28MVcFmNlkMWDIZL0JWTmUNmVxf0nYk9DqzSh9xwQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1771338585; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=QrWHrPoTis1m8pFWGHA+7KVcrCh9f5MgVkGtEUMaCzA=; b=QlZsnIZnVkFFNuRjRfqIZmoBDnew0Vwd0bG+iHbw3KQv4er3LWfcuor2+Nq1DG8pkTdU6w ht6F+5rhr0hEgDZnqTj9bCviH7j1mm6AC9/CJ6Xb2XLTncurGicq9G7Ix70U9QlABtk5Ej 8RVKV8L0Qvi6BmkXQDRanNbfI/79m/4+dK2VbhAG6E998nd+Uyi2P4y+AdESVLLjePxxrb VPYoW95Gs5F2FS6FO8cxqnhJJ+jQvLxThyIy7o/EqYyjvGbrAy7v6dExGAK8kZ6GOK5qPs mSq3DsiotCN3ysZAoId+7hninoU1NJEte5pmkAUjtBl/Qy0ENLYy1E9O9pAmQA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4fFhqY0xHFz1C0G for ; Tue, 17 Feb 2026 14:29:45 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 4613e by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 17 Feb 2026 14:29:45 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Kristof Provost Subject: git: f3a0e54656cc - stable/15 - pf: fix use of uninitialised variable List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kp X-Git-Repository: src X-Git-Refname: refs/heads/stable/15 X-Git-Reftype: branch X-Git-Commit: f3a0e54656ccec66ae94af0d717918eafa7c632b Auto-Submitted: auto-generated Date: Tue, 17 Feb 2026 14:29:45 +0000 Message-Id: <69947b59.4613e.7fe2e0a8@gitrepo.freebsd.org> The branch stable/15 has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=f3a0e54656ccec66ae94af0d717918eafa7c632b commit f3a0e54656ccec66ae94af0d717918eafa7c632b Author: Kristof Provost AuthorDate: 2026-02-03 12:17:08 +0000 Commit: Kristof Provost CommitDate: 2026-02-17 14:21:14 +0000 pf: fix use of uninitialised variable In pf_match_rule() we attempt to append matching rules to the end of 'match_rules'. We want to preserve the order to make the multiple pflog entries easier to understand. So we keep track of the last added rule item in 'rt'. However, that assumed that 'match_rules' was only ever added to in that one call to pf_match_rules(). This isn't always the case, for example if we have match rules in different anchors. In that case we'd end up using the uninitialised 'rt' variable in the SLIST_INSERT_AFTER call. Instead track the match rules and the last matching rule (to enable easy appending) in the struct pf_test_ctx. This also allows us to reduce the number of arguments for some functions, because we passed a ctx to most functions that needed 'match_rules'. While here also make pf_match_rules() static, because it's only ever used in pf.c Add a test case to exercise the relevant code path. MFC after: 2 weeks Sponsored by: Rubicon Communications, LLC ("Netgate") (cherry picked from commit fe9e4eb6f38ae004efb576bf44aded08852f9e6b) --- sys/net/pfvar.h | 7 +++--- sys/netpfil/pf/pf.c | 41 ++++++++++++++---------------- tests/sys/netpfil/pf/match.sh | 58 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 26 deletions(-) diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index a6b2f8f11e0f..9e0917b501e7 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -1165,6 +1165,8 @@ struct pf_test_ctx { int rewrite; u_short reason; struct pf_src_node *sns[PF_SN_MAX]; + struct pf_krule_slist *match_rules; + struct pf_krule_item *last_match_rule; struct pf_krule *nr; struct pf_krule *tr; struct pf_krule **rm; @@ -2724,10 +2726,7 @@ int pf_osfp_match(struct pf_osfp_enlist *, pf_osfp_t); #ifdef _KERNEL void pf_print_host(struct pf_addr *, u_int16_t, sa_family_t); -enum pf_test_status pf_step_into_anchor(struct pf_test_ctx *, struct pf_krule *, - struct pf_krule_slist *match_rules); -enum pf_test_status pf_match_rule(struct pf_test_ctx *, struct pf_kruleset *, - struct pf_krule_slist *); +enum pf_test_status pf_step_into_anchor(struct pf_test_ctx *, struct pf_krule *); void pf_step_into_keth_anchor(struct pf_keth_anchor_stackframe *, int *, struct pf_keth_ruleset **, struct pf_keth_rule **, struct pf_keth_rule **, diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index dee02d296f1f..882c7f4cc0dc 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -342,14 +342,14 @@ static int pf_dummynet_route(struct pf_pdesc *, struct ifnet *, const struct sockaddr *, struct mbuf **); static int pf_test_eth_rule(int, struct pfi_kkif *, struct mbuf **); +static enum pf_test_status pf_match_rule(struct pf_test_ctx *, struct pf_kruleset *); static int pf_test_rule(struct pf_krule **, struct pf_kstate **, struct pf_pdesc *, struct pf_krule **, struct pf_kruleset **, u_short *, struct inpcb *, struct pf_krule_slist *); static int pf_create_state(struct pf_krule *, struct pf_test_ctx *, - struct pf_kstate **, u_int16_t, u_int16_t, - struct pf_krule_slist *match_rules); + struct pf_kstate **, u_int16_t, u_int16_t); static int pf_state_key_addr_setup(struct pf_pdesc *, struct pf_state_key_cmp *, int); static int pf_tcp_track_full(struct pf_kstate *, @@ -4758,8 +4758,7 @@ pf_tag_packet(struct pf_pdesc *pd, int tag) } while (0) enum pf_test_status -pf_step_into_anchor(struct pf_test_ctx *ctx, struct pf_krule *r, - struct pf_krule_slist *match_rules) +pf_step_into_anchor(struct pf_test_ctx *ctx, struct pf_krule *r) { enum pf_test_status rv; @@ -4777,7 +4776,7 @@ pf_step_into_anchor(struct pf_test_ctx *ctx, struct pf_krule *r, struct pf_kanchor *child; rv = PF_TEST_OK; RB_FOREACH(child, pf_kanchor_node, &r->anchor->children) { - rv = pf_match_rule(ctx, &child->ruleset, match_rules); + rv = pf_match_rule(ctx, &child->ruleset); if ((rv == PF_TEST_QUICK) || (rv == PF_TEST_FAIL)) { /* * we either hit a rule with quick action @@ -4788,7 +4787,7 @@ pf_step_into_anchor(struct pf_test_ctx *ctx, struct pf_krule *r, } } } else { - rv = pf_match_rule(ctx, &r->anchor->ruleset, match_rules); + rv = pf_match_rule(ctx, &r->anchor->ruleset); /* * Unless errors occured, stop iff any rule matched * within quick anchors. @@ -5637,10 +5636,9 @@ pf_rule_apply_nat(struct pf_test_ctx *ctx, struct pf_krule *r) } enum pf_test_status -pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset *ruleset, - struct pf_krule_slist *match_rules) +pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset *ruleset) { - struct pf_krule_item *ri, *rt; + struct pf_krule_item *ri; struct pf_krule *r; struct pf_krule *save_a; struct pf_kruleset *save_aruleset; @@ -5780,12 +5778,12 @@ pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset *ruleset, } ri->r = r; - if (SLIST_EMPTY(match_rules)) { - SLIST_INSERT_HEAD(match_rules, ri, entry); + if (SLIST_EMPTY(ctx->match_rules)) { + SLIST_INSERT_HEAD(ctx->match_rules, ri, entry); } else { - SLIST_INSERT_AFTER(rt, ri, entry); + SLIST_INSERT_AFTER(ctx->last_match_rule, ri, entry); } - rt = ri; + ctx->last_match_rule = ri; pf_rule_to_actions(r, &pd->act); if (r->log) @@ -5810,7 +5808,7 @@ pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset *ruleset, ctx->arsm = ctx->aruleset; } if (pd->act.log & PF_LOG_MATCHES) - pf_log_matches(pd, r, ctx->a, ruleset, match_rules); + pf_log_matches(pd, r, ctx->a, ruleset, ctx->match_rules); if (r->quick) { ctx->test_status = PF_TEST_QUICK; break; @@ -5827,7 +5825,7 @@ pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset *ruleset, * Note: we don't need to restore if we are not going * to continue with ruleset evaluation. */ - if (pf_step_into_anchor(ctx, r, match_rules) != PF_TEST_OK) { + if (pf_step_into_anchor(ctx, r) != PF_TEST_OK) { break; } ctx->a = save_a; @@ -5863,6 +5861,7 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, ctx.rsm = rsm; ctx.th = &pd->hdr.tcp; ctx.reason = *reason; + ctx.match_rules = match_rules; pf_addrcpy(&pd->nsaddr, pd->src, pd->af); pf_addrcpy(&pd->ndaddr, pd->dst, pd->af); @@ -5960,7 +5959,7 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, ruleset = *ctx.rsm; } else { ruleset = &pf_main_ruleset; - rv = pf_match_rule(&ctx, ruleset, match_rules); + rv = pf_match_rule(&ctx, ruleset); if (rv == PF_TEST_FAIL) { /* * Reason has been set in pf_match_rule() already. @@ -5997,7 +5996,7 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, PFLOG_PACKET(r->action, ctx.reason, r, ctx.a, ruleset, pd, 1, NULL); } if (pd->act.log & PF_LOG_MATCHES) - pf_log_matches(pd, r, ctx.a, ruleset, match_rules); + pf_log_matches(pd, r, ctx.a, ruleset, ctx.match_rules); if (pd->virtual_proto != PF_VPROTO_FRAGMENT && (r->action == PF_DROP) && ((r->rule_flag & PFRULE_RETURNRST) || @@ -6042,8 +6041,7 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, (pd->flags & PFDESC_TCP_NORM)))) { bool nat64; - action = pf_create_state(r, &ctx, sm, bproto_sum, bip_sum, - match_rules); + action = pf_create_state(r, &ctx, sm, bproto_sum, bip_sum); ctx.sk = ctx.nk = NULL; if (action != PF_PASS) { pf_udp_mapping_release(ctx.udp_mapping); @@ -6126,8 +6124,7 @@ cleanup: static int pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx, - struct pf_kstate **sm, u_int16_t bproto_sum, u_int16_t bip_sum, - struct pf_krule_slist *match_rules) + struct pf_kstate **sm, u_int16_t bproto_sum, u_int16_t bip_sum) { struct pf_pdesc *pd = ctx->pd; struct pf_kstate *s = NULL; @@ -6192,7 +6189,7 @@ pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx, s->rule = r; s->nat_rule = ctx->nr; s->anchor = ctx->a; - s->match_rules = *match_rules; + s->match_rules = *ctx->match_rules; memcpy(&s->act, &pd->act, sizeof(struct pf_rule_actions)); if (pd->act.allow_opts) diff --git a/tests/sys/netpfil/pf/match.sh b/tests/sys/netpfil/pf/match.sh index 58c1e021310a..992e32d9f887 100644 --- a/tests/sys/netpfil/pf/match.sh +++ b/tests/sys/netpfil/pf/match.sh @@ -177,9 +177,67 @@ allow_opts_cleanup() pft_cleanup } +atf_test_case "double_match" "cleanup" +double_match_head() +{ + atf_set descr 'Test two match statements in separate anchors' + atf_set require.user root +} + +double_match_body() +{ + pft_init + + epair_one=$(vnet_mkepair) + epair_two=$(vnet_mkepair) + vnet_mkjail rtr ${epair_one}a ${epair_two}a + vnet_mkjail srv ${epair_two}b + + ifconfig ${epair_one}b 192.0.2.2/24 up + + jexec rtr ifconfig ${epair_one}a 192.0.2.1/24 up + jexec rtr ifconfig ${epair_two}a 198.51.100.1/24 up + jexec rtr sysctl net.inet.ip.forwarding=1 + + jexec srv ifconfig ${epair_two}b 198.51.100.2/24 up + + route add default 192.0.2.1 + + # Sanity check + atf_check -s exit:0 -o ignore \ + ping -c 1 192.0.2.1 + + jexec rtr pfctl -e + pft_set_rules rtr \ + "nat on ${epair_two}a from 192.0.2.0/24 to any -> (${epair_two}a)" \ + "block all" \ + "anchor \"userrules\" all {\n \ + anchor \"one\" all { \n\ + match in tag \"allow\"\n\ + }\n\ + anchor \"two\" all { \n\ + match tag \"allow\"\n\ + }\n\ + }\n" \ + "pass quick tagged \"allow\"" + + atf_check -s exit:0 -o ignore \ + ping -c 1 198.51.100.2 + + jexec rtr pfctl -ss -vv + jexec rtr pfctl -sr -vv -a "*" + jexec rtr pfctl -sr -a "*" +} + +double_match_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "dummynet" atf_add_test_case "quick" atf_add_test_case "allow_opts" + atf_add_test_case "double_match" }