Skip to content

Commit

Permalink
Merge tag 'nf-24-12-05' of git://git.kernel.org/pub/scm/linux/kernel/…
Browse files Browse the repository at this point in the history
…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>
  • Loading branch information
Paolo Abeni committed Dec 5, 2024
2 parents 0e21a47 + 7ffc748 commit 7b998e0
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 17 deletions.
1 change: 1 addition & 0 deletions include/net/netfilter/nf_tables_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ enum {
};

struct nft_inner_tun_ctx {
unsigned long cookie;
u16 type;
u16 inner_tunoff;
u16 inner_lloff;
Expand Down
5 changes: 5 additions & 0 deletions net/netfilter/ipset/ip_set_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
4 changes: 1 addition & 3 deletions net/netfilter/ipvs/ip_vs_proto.c
Original file line number Diff line number Diff line change
Expand Up @@ -340,16 +340,14 @@ 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); \
strcat(protocols, ", "); \
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
Expand Down
57 changes: 45 additions & 12 deletions net/netfilter/nft_inner.c
Original file line number Diff line number Diff line change
Expand Up @@ -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_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,
const struct nft_inner_tun_ctx *tun_ctx)
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;

Expand All @@ -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;
Expand Down
16 changes: 16 additions & 0 deletions net/netfilter/nft_set_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down Expand Up @@ -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);

Expand All @@ -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;

Expand All @@ -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);
}

Expand Down
2 changes: 1 addition & 1 deletion net/netfilter/nft_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
4 changes: 3 additions & 1 deletion net/netfilter/xt_LED.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 7b998e0

Please sign in to comment.