netfilter pull request 26-06-01

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEjF9xRqF1emXiQiqU1w0aZmrPKyEFAmodcTIACgkQ1w0aZmrP
 KyGSnA/+Khp694VPEoIX1gS1ZXgdh86zNpi3IXDlYze8cedZbgsvt5Dfz/hpKd7s
 gXwJsocxRPNjh4MYxZq/TVnpo5WcI+hmRVBtKRCiwjsBZ/ejL4cnyXu0suJ4qHOe
 RQWF24jNu3U1A/qfCcpBA8plYH6SC8MjIarXemkxFd5AHlb4JRyfZgCjg0L2CLi9
 nipV5TTpeP4dMlXCORPjOginFdXpwUgpxWZjJAnJ9SbI5a/qDwSrOT40UmxDMFTL
 9RRLO8DzuRYdy56tGbu3pS3QaBDbC966IyJQi0X89nsMSJQt3ipG6jxxe0Kzc3Ma
 ft+RsJKqrdcFdUQDhMY38Tm/Osj6L/yd4FJYCEkA9yx+nsoNh9IMoN7hXgqz9GHC
 ujtyKI0hOJdsFyl3ZjssC/MYtEL/R9e7n4oKwdp8QSLkjUAXCQ+nPvbYky8qVG68
 toTMi0B2TndmSIExR2kg9eGf5/n0oXEoRkgxQ9JXiDZiRBRLF8UgErIKpbL3SRF5
 rZL/Vavq3uTEVI3PEHnbIg4d2/z574tvSguBtNRW93pyJIgnq2duoI55Lzpp7n51
 pcREDPCKvkIDGdcWmX8eUNs+2eGw6EI1L1BsSoikUYrPTFO9H5tIV5V4CGlNsQoM
 QvleKA8RwQAyVUd0C7dEDifKfbwEBACcw/Rz5uZLGHC2Z/9gHq0=
 =+q/K
 -----END PGP SIGNATURE-----

Merge tag 'nf-26-06-01' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf

Pablo Neira Ayuso says:

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

The following patchset contains Netfilter/IPVS fixes for net:

1) Fix splat with PREEMPT_RCU because smp_processor_id() in nfqueue,
   from Fernando Fernandez Mancera.

2) Fix possible use of pointer to old IPVS scheduler after RCU grace
   period when editing service, from Julian Anastasov.

3) Fix possible forever RCU walk over rt->fib6_siblings in nft_fib6,
   if rt is unlinked mid-iteration, apparently same issue happens in
   the fib6 core. From Jiayuan Chen.

4) Add mutex to guard refcount in synproxy infrastructure, since
   concurrent hook {un}registration can happen.
   From Fernando Fernandez Mancera.

5) Bail out if IRC conntrack helper fails to parse a command, do not
   try parsing using other command handlers, from Florian Westphal.
   This fixes a possible out-of-bound read.

6) Possible use-after-free in nft_tunnel by releasing template dst
   after all references has been dropped, from Tristan Madani.

7) Ignore conntrack template in nft_ct, from Jiayuan Chen.

8) Missing skb_ensure_writable() in ebt_snat, Yiming Qian.

9) Remove multi-register byteorder support, this allows for kernel
   stack info leak, from Florian Westphal.

* tag 'nf-26-06-01' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
  netfilter: nft_byteorder: remove multi-register support
  netfilter: bridge: make ebt_snat ARP rewrite writable
  netfilter: nft_ct: bail out on template ct in get eval
  netfilter: nft_tunnel: fix use-after-free on object destroy
  netfilter: conntrack_irc: fix possible out-of-bounds read
  netfilter: synproxy: add mutex to guard hook reference counting
  netfilter: nft_fib_ipv6: bail out of sibling walk if rt got unlinked
  ipvs: clear the svc scheduler ptr early on edit
  netfilter: xt_NFQUEUE: prefer raw_smp_processor_id
====================

Link: https://patch.msgid.link/20260601115923.433946-1-pablo@netfilter.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-06-02 11:57:21 -07:00
commit 9de5cbbe70
12 changed files with 68 additions and 61 deletions

View File

@ -1824,8 +1824,7 @@ int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler);
int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler);
int ip_vs_bind_scheduler(struct ip_vs_service *svc,
struct ip_vs_scheduler *scheduler);
void ip_vs_unbind_scheduler(struct ip_vs_service *svc,
struct ip_vs_scheduler *sched);
void ip_vs_unbind_scheduler(struct ip_vs_service *svc);
struct ip_vs_scheduler *ip_vs_scheduler_get(const char *sched_name);
void ip_vs_scheduler_put(struct ip_vs_scheduler *scheduler);
struct ip_vs_conn *

View File

@ -31,6 +31,9 @@ ebt_snat_tg(struct sk_buff *skb, const struct xt_action_param *par)
const struct arphdr *ap;
struct arphdr _ah;
if (skb_ensure_writable(skb, sizeof(_ah) + ETH_ALEN))
return EBT_DROP;
ap = skb_header_pointer(skb, 0, sizeof(_ah), &_ah);
if (ap == NULL)
return EBT_DROP;

View File

@ -191,6 +191,9 @@ static bool nft_fib6_info_nh_uses_dev(struct fib6_info *rt,
if (nft_fib6_info_nh_dev_match(nh_dev, dev))
return true;
if (!READ_ONCE(rt->fib6_nsiblings))
return false;
}
return false;

View File

@ -1898,7 +1898,7 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u,
if (ret_hooks >= 0)
ip_vs_unregister_hooks(ipvs, u->af);
if (svc != NULL) {
ip_vs_unbind_scheduler(svc, sched);
ip_vs_unbind_scheduler(svc);
ip_vs_service_free(svc);
}
ip_vs_scheduler_put(sched);
@ -1962,9 +1962,8 @@ ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user_kern *u)
old_sched = rcu_dereference_protected(svc->scheduler, 1);
if (sched != old_sched) {
if (old_sched) {
ip_vs_unbind_scheduler(svc, old_sched);
RCU_INIT_POINTER(svc->scheduler, NULL);
/* Wait all svc->sched_data users */
ip_vs_unbind_scheduler(svc);
/* Wait all svc->scheduler/sched_data users */
synchronize_rcu();
}
/* Bind the new scheduler */
@ -1972,6 +1971,10 @@ ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user_kern *u)
ret = ip_vs_bind_scheduler(svc, sched);
if (ret) {
ip_vs_scheduler_put(sched);
/* Try to restore the old_sched */
if (old_sched &&
!ip_vs_bind_scheduler(svc, old_sched))
old_sched = NULL;
goto out;
}
}
@ -2027,7 +2030,7 @@ static void __ip_vs_del_service(struct ip_vs_service *svc, bool cleanup)
/* Unbind scheduler */
old_sched = rcu_dereference_protected(svc->scheduler, 1);
ip_vs_unbind_scheduler(svc, old_sched);
ip_vs_unbind_scheduler(svc);
ip_vs_scheduler_put(old_sched);
/* Unbind persistence engine, keep svc->pe */

View File

@ -56,19 +56,19 @@ int ip_vs_bind_scheduler(struct ip_vs_service *svc,
/*
* Unbind a service with its scheduler
*/
void ip_vs_unbind_scheduler(struct ip_vs_service *svc,
struct ip_vs_scheduler *sched)
void ip_vs_unbind_scheduler(struct ip_vs_service *svc)
{
struct ip_vs_scheduler *cur_sched;
struct ip_vs_scheduler *sched;
cur_sched = rcu_dereference_protected(svc->scheduler, 1);
/* This check proves that old 'sched' was installed */
if (!cur_sched)
sched = rcu_dereference_protected(svc->scheduler, 1);
if (!sched)
return;
/* Reset the scheduler before initiating any RCU callbacks */
rcu_assign_pointer(svc->scheduler, NULL);
smp_wmb(); /* paired with smp_rmb() in ip_vs_schedule() */
if (sched->done_service)
sched->done_service(svc);
/* svc->scheduler can be set to NULL only by caller */
}

View File

@ -203,7 +203,7 @@ static int help(struct sk_buff *skb, unsigned int protoff,
if (parse_dcc(data, data_limit, &dcc_ip,
&dcc_port, &addr_beg_p, &addr_end_p)) {
pr_debug("unable to parse dcc command\n");
continue;
goto out;
}
pr_debug("DCC bound ip/port: %pI4:%u\n",
@ -217,7 +217,7 @@ static int help(struct sk_buff *skb, unsigned int protoff,
net_warn_ratelimited("Forged DCC command from %pI4: %pI4:%u\n",
&tuple->src.u3.ip,
&dcc_ip, dcc_port);
continue;
goto out;
}
exp = nf_ct_expect_alloc(ct);

View File

@ -22,6 +22,8 @@
#include <net/netfilter/nf_conntrack_zones.h>
#include <net/netfilter/nf_synproxy.h>
static DEFINE_MUTEX(synproxy_mutex);
unsigned int synproxy_net_id;
EXPORT_SYMBOL_GPL(synproxy_net_id);
@ -769,26 +771,31 @@ static const struct nf_hook_ops ipv4_synproxy_ops[] = {
int nf_synproxy_ipv4_init(struct synproxy_net *snet, struct net *net)
{
int err;
int err = 0;
mutex_lock(&synproxy_mutex);
if (snet->hook_ref4 == 0) {
err = nf_register_net_hooks(net, ipv4_synproxy_ops,
ARRAY_SIZE(ipv4_synproxy_ops));
if (err)
return err;
goto out;
}
snet->hook_ref4++;
return 0;
out:
mutex_unlock(&synproxy_mutex);
return err;
}
EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_init);
void nf_synproxy_ipv4_fini(struct synproxy_net *snet, struct net *net)
{
mutex_lock(&synproxy_mutex);
snet->hook_ref4--;
if (snet->hook_ref4 == 0)
nf_unregister_net_hooks(net, ipv4_synproxy_ops,
ARRAY_SIZE(ipv4_synproxy_ops));
mutex_unlock(&synproxy_mutex);
}
EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_fini);
@ -1193,27 +1200,32 @@ static const struct nf_hook_ops ipv6_synproxy_ops[] = {
int
nf_synproxy_ipv6_init(struct synproxy_net *snet, struct net *net)
{
int err;
int err = 0;
mutex_lock(&synproxy_mutex);
if (snet->hook_ref6 == 0) {
err = nf_register_net_hooks(net, ipv6_synproxy_ops,
ARRAY_SIZE(ipv6_synproxy_ops));
if (err)
return err;
goto out;
}
snet->hook_ref6++;
return 0;
out:
mutex_unlock(&synproxy_mutex);
return err;
}
EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_init);
void
nf_synproxy_ipv6_fini(struct synproxy_net *snet, struct net *net)
{
mutex_lock(&synproxy_mutex);
snet->hook_ref6--;
if (snet->hook_ref6 == 0)
nf_unregister_net_hooks(net, ipv6_synproxy_ops,
ARRAY_SIZE(ipv6_synproxy_ops));
mutex_unlock(&synproxy_mutex);
}
EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_fini);
#endif /* CONFIG_IPV6 */

View File

@ -19,7 +19,6 @@ struct nft_byteorder {
u8 sreg;
u8 dreg;
enum nft_byteorder_ops op:8;
u8 len;
u8 size;
};
@ -28,13 +27,8 @@ void nft_byteorder_eval(const struct nft_expr *expr,
const struct nft_pktinfo *pkt)
{
const struct nft_byteorder *priv = nft_expr_priv(expr);
u32 *src = &regs->data[priv->sreg];
const u32 *src = &regs->data[priv->sreg];
u32 *dst = &regs->data[priv->dreg];
u16 *s16, *d16;
unsigned int i;
s16 = (void *)src;
d16 = (void *)dst;
switch (priv->size) {
case 8: {
@ -43,18 +37,14 @@ void nft_byteorder_eval(const struct nft_expr *expr,
switch (priv->op) {
case NFT_BYTEORDER_NTOH:
for (i = 0; i < priv->len / 8; i++) {
src64 = nft_reg_load64(&src[i]);
nft_reg_store64(&dst64[i],
be64_to_cpu((__force __be64)src64));
}
src64 = nft_reg_load64(src);
nft_reg_store64(dst64, be64_to_cpu((__force __be64)src64));
break;
case NFT_BYTEORDER_HTON:
for (i = 0; i < priv->len / 8; i++) {
src64 = (__force __u64)
cpu_to_be64(nft_reg_load64(&src[i]));
nft_reg_store64(&dst64[i], src64);
}
src64 = (__force __u64)cpu_to_be64(nft_reg_load64(src));
nft_reg_store64(dst64, src64);
break;
}
break;
@ -62,24 +52,20 @@ void nft_byteorder_eval(const struct nft_expr *expr,
case 4:
switch (priv->op) {
case NFT_BYTEORDER_NTOH:
for (i = 0; i < priv->len / 4; i++)
dst[i] = ntohl((__force __be32)src[i]);
*dst = ntohl((__force __be32)*src);
break;
case NFT_BYTEORDER_HTON:
for (i = 0; i < priv->len / 4; i++)
dst[i] = (__force __u32)htonl(src[i]);
*dst = (__force __u32)htonl(*src);
break;
}
break;
case 2:
switch (priv->op) {
case NFT_BYTEORDER_NTOH:
for (i = 0; i < priv->len / 2; i++)
d16[i] = ntohs((__force __be16)s16[i]);
nft_reg_store16(dst, ntohs(nft_reg_load_be16(src)));
break;
case NFT_BYTEORDER_HTON:
for (i = 0; i < priv->len / 2; i++)
d16[i] = (__force __u16)htons(s16[i]);
nft_reg_store_be16(dst, htons(nft_reg_load16(src)));
break;
}
break;
@ -137,20 +123,22 @@ static int nft_byteorder_init(const struct nft_ctx *ctx,
if (err < 0)
return err;
priv->len = len;
/* no longer support multi-reg conversions */
if (len != size)
return -EOPNOTSUPP;
err = nft_parse_register_load(ctx, tb[NFTA_BYTEORDER_SREG], &priv->sreg,
priv->len);
len);
if (err < 0)
return err;
err = nft_parse_register_store(ctx, tb[NFTA_BYTEORDER_DREG],
&priv->dreg, NULL, NFT_DATA_VALUE,
priv->len);
len);
if (err < 0)
return err;
if (nft_reg_overlap(priv->sreg, priv->dreg, priv->len))
if (nft_reg_overlap(priv->sreg, priv->dreg, len))
return -EINVAL;
return 0;
@ -167,10 +155,11 @@ static int nft_byteorder_dump(struct sk_buff *skb,
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_BYTEORDER_OP, htonl(priv->op)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_BYTEORDER_LEN, htonl(priv->len)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_BYTEORDER_SIZE, htonl(priv->size)))
goto nla_put_failure;
/* compatibility for old userspace which permitted size != len */
if (nla_put_be32(skb, NFTA_BYTEORDER_LEN, htonl(priv->size)))
goto nla_put_failure;
return 0;
nla_put_failure:

View File

@ -78,7 +78,7 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
break;
}
if (ct == NULL)
if (!ct || nf_ct_is_template(ct))
goto err;
switch (priv->key) {
@ -180,12 +180,10 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
tuple = &ct->tuplehash[priv->dir].tuple;
switch (priv->key) {
case NFT_CT_SRC:
memcpy(dest, tuple->src.u3.all,
nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
memcpy(dest, tuple->src.u3.all, priv->len);
return;
case NFT_CT_DST:
memcpy(dest, tuple->dst.u3.all,
nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
memcpy(dest, tuple->dst.u3.all, priv->len);
return;
case NFT_CT_PROTO_SRC:
nft_reg_store16(dest, (__force u16)tuple->src.u.all);

View File

@ -30,7 +30,7 @@ void nft_ct_get_fast_eval(const struct nft_expr *expr,
break;
}
if (!ct) {
if (!ct || nf_ct_is_template(ct)) {
regs->verdict.code = NFT_BREAK;
return;
}

View File

@ -676,7 +676,7 @@ static void nft_tunnel_obj_destroy(const struct nft_ctx *ctx,
{
struct nft_tunnel_obj *priv = nft_obj_data(obj);
metadata_dst_free(priv->md);
dst_release(&priv->md->dst);
}
static struct nft_object_type nft_tunnel_obj_type;

View File

@ -91,7 +91,7 @@ nfqueue_tg_v3(struct sk_buff *skb, const struct xt_action_param *par)
if (info->queues_total > 1) {
if (info->flags & NFQ_FLAG_CPU_FANOUT) {
int cpu = smp_processor_id();
int cpu = raw_smp_processor_id();
queue = info->queuenum + cpu % info->queues_total;
} else {