netfilter pull request 24-12-05

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEjF9xRqF1emXiQiqU1w0aZmrPKyEFAmdQ7eIACgkQ1w0aZmrP
 KyEk+g//VdVM+7cVY+yqV+1gvST4yzCU7OkqGLhUZPYFsvARtLKqeSS2hygZAQzg
 bEqRuYCYShIh8zSCble9NxWd2u6h+QQWEkvxaWldsOHQ3olDBpzM0vWuqfbRV+GO
 TXtLwXXTzktMIW+itsZm2Dkz7MWhwcllKHuQRc6t5cu82Lm7vLjXRxEYyhcFbg7n
 2waSwhPsRKkNY3dLiIkJQS/4FXp7SQM+kRv0jyeQQ4oC9mmPYYyteVpjphTIPji7
 wpu1GgnmWEyMwNxYeoO7JZ69ESIc6wwUr712WB1FnGhjwQ1D0KdfgYnSxoc81TC4
 BGyP5i4yYHU9DmAKb04zve+LaF1gR6L51agi6sYcIsZQw3Cnr/YIY7w8O+EavwHb
 VDPvDouMtvDIKd4Hdtsna2o8d/UrkuE5/AuijHU40eEEEuMeuqiaby6Dj3ORYFrs
 edWjnkSqcvLGhx0bXzuFAWm/CxYqZsPcH0rWSUfuJu4wLevlrvKF0bkcA3NtcjDc
 bOEwXB1McKnWNBFeOnetf3PvA5Eu2HbYtt87rO1UeeBS+Bw8IO2uTu5X73suSb4f
 TJyWcQk51HMNa230tT2vEjsA5+FhXL/ZUYGu1ckVfghmoYdmWAEx8fuzec2Lqe/V
 hsn3Csl+JOPmqOEHyRvQ7t0UL4j3918hNP21RWyee6qTYK09YSM=
 =3N6k
 -----END PGP SIGNATURE-----

Merge tag 'nf-24-12-05' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf

Pablo Neira Ayuso says:

====================
Netfilter fixes for net

The following patchset contains Netfilter fixes for net:

1) Fix esoteric undefined behaviour due to uninitialized stack access
   in ip_vs_protocol_init(), from Jinghao Jia.

2) Fix iptables xt_LED slab-out-of-bounds due to incorrect sanitization
   of the led string identifier, reported by syzbot. Patch from
   Dmitry Antipov.

3) Remove WARN_ON_ONCE reachable from userspace to check for the maximum
   cgroup level, nft_socket cgroup matching is restricted to 255 levels,
   but cgroups allow for INT_MAX levels by default. Reported by syzbot.

4) Fix nft_inner incorrect use of percpu area to store tunnel parser
   context with softirqs, resulting in inconsistent inner header
   offsets that could lead to bogus rule mismatches, reported by syzbot.

5) Grab module reference on ipset core while requesting set type modules,
   otherwise kernel crash is possible by removing ipset core module,
   patch from Phil Sutter.

6) Fix possible double-free in nft_hash garbage collector due to unstable
   walk interator that can provide twice the same element. Use a sequence
   number to skip expired/dead elements that have been already scheduled
   for removal. Based on patch from Laurent Fasnach

netfilter pull request 24-12-05

* tag 'nf-24-12-05' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
  netfilter: nft_set_hash: skip duplicated elements pending gc run
  netfilter: ipset: Hold module reference while requesting a module
  netfilter: nft_inner: incorrect percpu area handling under softirq
  netfilter: nft_socket: remove WARN_ON_ONCE on maximum cgroup level
  netfilter: x_tables: fix LED ID check in led_tg_check()
  ipvs: fix UB due to uninitialized stack access in ip_vs_protocol_init()
====================

Link: https://patch.msgid.link/20241205002854.162490-1-pablo@netfilter.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2024-12-05 11:49:14 +01:00
commit 7b998e073f
7 changed files with 73 additions and 18 deletions

View File

@ -161,6 +161,7 @@ enum {
};
struct nft_inner_tun_ctx {
unsigned long cookie;
u16 type;
u16 inner_tunoff;
u16 inner_lloff;

View File

@ -104,14 +104,19 @@ find_set_type(const char *name, u8 family, u8 revision)
static bool
load_settype(const char *name)
{
if (!try_module_get(THIS_MODULE))
return false;
nfnl_unlock(NFNL_SUBSYS_IPSET);
pr_debug("try to load ip_set_%s\n", name);
if (request_module("ip_set_%s", name) < 0) {
pr_warn("Can't find ip_set type %s\n", name);
nfnl_lock(NFNL_SUBSYS_IPSET);
module_put(THIS_MODULE);
return false;
}
nfnl_lock(NFNL_SUBSYS_IPSET);
module_put(THIS_MODULE);
return true;
}

View File

@ -340,7 +340,7 @@ void __net_exit ip_vs_protocol_net_cleanup(struct netns_ipvs *ipvs)
int __init ip_vs_protocol_init(void)
{
char protocols[64];
char protocols[64] = { 0 };
#define REGISTER_PROTOCOL(p) \
do { \
register_ip_vs_protocol(p); \
@ -348,8 +348,6 @@ int __init ip_vs_protocol_init(void)
strcat(protocols, (p)->name); \
} while (0)
protocols[0] = '\0';
protocols[2] = '\0';
#ifdef CONFIG_IP_VS_PROTO_TCP
REGISTER_PROTOCOL(&ip_vs_protocol_tcp);
#endif

View File

@ -210,35 +210,66 @@ static int nft_inner_parse(const struct nft_inner *priv,
struct nft_pktinfo *pkt,
struct nft_inner_tun_ctx *tun_ctx)
{
struct nft_inner_tun_ctx ctx = {};
u32 off = pkt->inneroff;
if (priv->flags & NFT_INNER_HDRSIZE &&
nft_inner_parse_tunhdr(priv, pkt, &ctx, &off) < 0)
nft_inner_parse_tunhdr(priv, pkt, tun_ctx, &off) < 0)
return -1;
if (priv->flags & (NFT_INNER_LL | NFT_INNER_NH)) {
if (nft_inner_parse_l2l3(priv, pkt, &ctx, off) < 0)
if (nft_inner_parse_l2l3(priv, pkt, tun_ctx, off) < 0)
return -1;
} else if (priv->flags & NFT_INNER_TH) {
ctx.inner_thoff = off;
ctx.flags |= NFT_PAYLOAD_CTX_INNER_TH;
tun_ctx->inner_thoff = off;
tun_ctx->flags |= NFT_PAYLOAD_CTX_INNER_TH;
}
*tun_ctx = ctx;
tun_ctx->type = priv->type;
tun_ctx->cookie = (unsigned long)pkt->skb;
pkt->flags |= NFT_PKTINFO_INNER_FULL;
return 0;
}
static bool nft_inner_parse_needed(const struct nft_inner *priv,
const struct nft_pktinfo *pkt,
static bool nft_inner_restore_tun_ctx(const struct nft_pktinfo *pkt,
struct nft_inner_tun_ctx *tun_ctx)
{
struct nft_inner_tun_ctx *this_cpu_tun_ctx;
local_bh_disable();
this_cpu_tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx);
if (this_cpu_tun_ctx->cookie != (unsigned long)pkt->skb) {
local_bh_enable();
return false;
}
*tun_ctx = *this_cpu_tun_ctx;
local_bh_enable();
return true;
}
static void nft_inner_save_tun_ctx(const struct nft_pktinfo *pkt,
const struct nft_inner_tun_ctx *tun_ctx)
{
struct nft_inner_tun_ctx *this_cpu_tun_ctx;
local_bh_disable();
this_cpu_tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx);
if (this_cpu_tun_ctx->cookie != tun_ctx->cookie)
*this_cpu_tun_ctx = *tun_ctx;
local_bh_enable();
}
static bool nft_inner_parse_needed(const struct nft_inner *priv,
const struct nft_pktinfo *pkt,
struct nft_inner_tun_ctx *tun_ctx)
{
if (!(pkt->flags & NFT_PKTINFO_INNER_FULL))
return true;
if (!nft_inner_restore_tun_ctx(pkt, tun_ctx))
return true;
if (priv->type != tun_ctx->type)
return true;
@ -248,27 +279,29 @@ static bool nft_inner_parse_needed(const struct nft_inner *priv,
static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
struct nft_inner_tun_ctx *tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx);
const struct nft_inner *priv = nft_expr_priv(expr);
struct nft_inner_tun_ctx tun_ctx = {};
if (nft_payload_inner_offset(pkt) < 0)
goto err;
if (nft_inner_parse_needed(priv, pkt, tun_ctx) &&
nft_inner_parse(priv, (struct nft_pktinfo *)pkt, tun_ctx) < 0)
if (nft_inner_parse_needed(priv, pkt, &tun_ctx) &&
nft_inner_parse(priv, (struct nft_pktinfo *)pkt, &tun_ctx) < 0)
goto err;
switch (priv->expr_type) {
case NFT_INNER_EXPR_PAYLOAD:
nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx);
nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, &tun_ctx);
break;
case NFT_INNER_EXPR_META:
nft_meta_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx);
nft_meta_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, &tun_ctx);
break;
default:
WARN_ON_ONCE(1);
goto err;
}
nft_inner_save_tun_ctx(pkt, &tun_ctx);
return;
err:
regs->verdict.code = NFT_BREAK;

View File

@ -24,11 +24,13 @@
struct nft_rhash {
struct rhashtable ht;
struct delayed_work gc_work;
u32 wq_gc_seq;
};
struct nft_rhash_elem {
struct nft_elem_priv priv;
struct rhash_head node;
u32 wq_gc_seq;
struct nft_set_ext ext;
};
@ -338,6 +340,10 @@ static void nft_rhash_gc(struct work_struct *work)
if (!gc)
goto done;
/* Elements never collected use a zero gc worker sequence number. */
if (unlikely(++priv->wq_gc_seq == 0))
priv->wq_gc_seq++;
rhashtable_walk_enter(&priv->ht, &hti);
rhashtable_walk_start(&hti);
@ -355,6 +361,14 @@ static void nft_rhash_gc(struct work_struct *work)
goto try_later;
}
/* rhashtable walk is unstable, already seen in this gc run?
* Then, skip this element. In case of (unlikely) sequence
* wraparound and stale element wq_gc_seq, next gc run will
* just find this expired element.
*/
if (he->wq_gc_seq == priv->wq_gc_seq)
continue;
if (nft_set_elem_is_dead(&he->ext))
goto dead_elem;
@ -371,6 +385,8 @@ static void nft_rhash_gc(struct work_struct *work)
if (!gc)
goto try_later;
/* annotate gc sequence for this attempt. */
he->wq_gc_seq = priv->wq_gc_seq;
nft_trans_gc_elem_add(gc, he);
}

View File

@ -68,7 +68,7 @@ static noinline int nft_socket_cgroup_subtree_level(void)
cgroup_put(cgrp);
if (WARN_ON_ONCE(level > 255))
if (level > 255)
return -ERANGE;
if (WARN_ON_ONCE(level < 0))

View File

@ -96,7 +96,9 @@ static int led_tg_check(const struct xt_tgchk_param *par)
struct xt_led_info_internal *ledinternal;
int err;
if (ledinfo->id[0] == '\0')
/* Bail out if empty string or not a string at all. */
if (ledinfo->id[0] == '\0' ||
!memchr(ledinfo->id, '\0', sizeof(ledinfo->id)))
return -EINVAL;
mutex_lock(&xt_led_mutex);