Skip to content

Commit 7b998e0

Browse files
author
Paolo Abeni
committed
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/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
2 parents 0e21a47 + 7ffc748 commit 7b998e0

File tree

7 files changed

+72
-17
lines changed

7 files changed

+72
-17
lines changed

include/net/netfilter/nf_tables_core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ enum {
161161
};
162162

163163
struct nft_inner_tun_ctx {
164+
unsigned long cookie;
164165
u16 type;
165166
u16 inner_tunoff;
166167
u16 inner_lloff;

net/netfilter/ipset/ip_set_core.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,19 @@ find_set_type(const char *name, u8 family, u8 revision)
104104
static bool
105105
load_settype(const char *name)
106106
{
107+
if (!try_module_get(THIS_MODULE))
108+
return false;
109+
107110
nfnl_unlock(NFNL_SUBSYS_IPSET);
108111
pr_debug("try to load ip_set_%s\n", name);
109112
if (request_module("ip_set_%s", name) < 0) {
110113
pr_warn("Can't find ip_set type %s\n", name);
111114
nfnl_lock(NFNL_SUBSYS_IPSET);
115+
module_put(THIS_MODULE);
112116
return false;
113117
}
114118
nfnl_lock(NFNL_SUBSYS_IPSET);
119+
module_put(THIS_MODULE);
115120
return true;
116121
}
117122

net/netfilter/ipvs/ip_vs_proto.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,16 +340,14 @@ void __net_exit ip_vs_protocol_net_cleanup(struct netns_ipvs *ipvs)
340340

341341
int __init ip_vs_protocol_init(void)
342342
{
343-
char protocols[64];
343+
char protocols[64] = { 0 };
344344
#define REGISTER_PROTOCOL(p) \
345345
do { \
346346
register_ip_vs_protocol(p); \
347347
strcat(protocols, ", "); \
348348
strcat(protocols, (p)->name); \
349349
} while (0)
350350

351-
protocols[0] = '\0';
352-
protocols[2] = '\0';
353351
#ifdef CONFIG_IP_VS_PROTO_TCP
354352
REGISTER_PROTOCOL(&ip_vs_protocol_tcp);
355353
#endif

net/netfilter/nft_inner.c

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -210,35 +210,66 @@ static int nft_inner_parse(const struct nft_inner *priv,
210210
struct nft_pktinfo *pkt,
211211
struct nft_inner_tun_ctx *tun_ctx)
212212
{
213-
struct nft_inner_tun_ctx ctx = {};
214213
u32 off = pkt->inneroff;
215214

216215
if (priv->flags & NFT_INNER_HDRSIZE &&
217-
nft_inner_parse_tunhdr(priv, pkt, &ctx, &off) < 0)
216+
nft_inner_parse_tunhdr(priv, pkt, tun_ctx, &off) < 0)
218217
return -1;
219218

220219
if (priv->flags & (NFT_INNER_LL | NFT_INNER_NH)) {
221-
if (nft_inner_parse_l2l3(priv, pkt, &ctx, off) < 0)
220+
if (nft_inner_parse_l2l3(priv, pkt, tun_ctx, off) < 0)
222221
return -1;
223222
} else if (priv->flags & NFT_INNER_TH) {
224-
ctx.inner_thoff = off;
225-
ctx.flags |= NFT_PAYLOAD_CTX_INNER_TH;
223+
tun_ctx->inner_thoff = off;
224+
tun_ctx->flags |= NFT_PAYLOAD_CTX_INNER_TH;
226225
}
227226

228-
*tun_ctx = ctx;
229227
tun_ctx->type = priv->type;
228+
tun_ctx->cookie = (unsigned long)pkt->skb;
230229
pkt->flags |= NFT_PKTINFO_INNER_FULL;
231230

232231
return 0;
233232
}
234233

234+
static bool nft_inner_restore_tun_ctx(const struct nft_pktinfo *pkt,
235+
struct nft_inner_tun_ctx *tun_ctx)
236+
{
237+
struct nft_inner_tun_ctx *this_cpu_tun_ctx;
238+
239+
local_bh_disable();
240+
this_cpu_tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx);
241+
if (this_cpu_tun_ctx->cookie != (unsigned long)pkt->skb) {
242+
local_bh_enable();
243+
return false;
244+
}
245+
*tun_ctx = *this_cpu_tun_ctx;
246+
local_bh_enable();
247+
248+
return true;
249+
}
250+
251+
static void nft_inner_save_tun_ctx(const struct nft_pktinfo *pkt,
252+
const struct nft_inner_tun_ctx *tun_ctx)
253+
{
254+
struct nft_inner_tun_ctx *this_cpu_tun_ctx;
255+
256+
local_bh_disable();
257+
this_cpu_tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx);
258+
if (this_cpu_tun_ctx->cookie != tun_ctx->cookie)
259+
*this_cpu_tun_ctx = *tun_ctx;
260+
local_bh_enable();
261+
}
262+
235263
static bool nft_inner_parse_needed(const struct nft_inner *priv,
236264
const struct nft_pktinfo *pkt,
237-
const struct nft_inner_tun_ctx *tun_ctx)
265+
struct nft_inner_tun_ctx *tun_ctx)
238266
{
239267
if (!(pkt->flags & NFT_PKTINFO_INNER_FULL))
240268
return true;
241269

270+
if (!nft_inner_restore_tun_ctx(pkt, tun_ctx))
271+
return true;
272+
242273
if (priv->type != tun_ctx->type)
243274
return true;
244275

@@ -248,27 +279,29 @@ static bool nft_inner_parse_needed(const struct nft_inner *priv,
248279
static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs,
249280
const struct nft_pktinfo *pkt)
250281
{
251-
struct nft_inner_tun_ctx *tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx);
252282
const struct nft_inner *priv = nft_expr_priv(expr);
283+
struct nft_inner_tun_ctx tun_ctx = {};
253284

254285
if (nft_payload_inner_offset(pkt) < 0)
255286
goto err;
256287

257-
if (nft_inner_parse_needed(priv, pkt, tun_ctx) &&
258-
nft_inner_parse(priv, (struct nft_pktinfo *)pkt, tun_ctx) < 0)
288+
if (nft_inner_parse_needed(priv, pkt, &tun_ctx) &&
289+
nft_inner_parse(priv, (struct nft_pktinfo *)pkt, &tun_ctx) < 0)
259290
goto err;
260291

261292
switch (priv->expr_type) {
262293
case NFT_INNER_EXPR_PAYLOAD:
263-
nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx);
294+
nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, &tun_ctx);
264295
break;
265296
case NFT_INNER_EXPR_META:
266-
nft_meta_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx);
297+
nft_meta_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, &tun_ctx);
267298
break;
268299
default:
269300
WARN_ON_ONCE(1);
270301
goto err;
271302
}
303+
nft_inner_save_tun_ctx(pkt, &tun_ctx);
304+
272305
return;
273306
err:
274307
regs->verdict.code = NFT_BREAK;

net/netfilter/nft_set_hash.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@
2424
struct nft_rhash {
2525
struct rhashtable ht;
2626
struct delayed_work gc_work;
27+
u32 wq_gc_seq;
2728
};
2829

2930
struct nft_rhash_elem {
3031
struct nft_elem_priv priv;
3132
struct rhash_head node;
33+
u32 wq_gc_seq;
3234
struct nft_set_ext ext;
3335
};
3436

@@ -338,6 +340,10 @@ static void nft_rhash_gc(struct work_struct *work)
338340
if (!gc)
339341
goto done;
340342

343+
/* Elements never collected use a zero gc worker sequence number. */
344+
if (unlikely(++priv->wq_gc_seq == 0))
345+
priv->wq_gc_seq++;
346+
341347
rhashtable_walk_enter(&priv->ht, &hti);
342348
rhashtable_walk_start(&hti);
343349

@@ -355,6 +361,14 @@ static void nft_rhash_gc(struct work_struct *work)
355361
goto try_later;
356362
}
357363

364+
/* rhashtable walk is unstable, already seen in this gc run?
365+
* Then, skip this element. In case of (unlikely) sequence
366+
* wraparound and stale element wq_gc_seq, next gc run will
367+
* just find this expired element.
368+
*/
369+
if (he->wq_gc_seq == priv->wq_gc_seq)
370+
continue;
371+
358372
if (nft_set_elem_is_dead(&he->ext))
359373
goto dead_elem;
360374

@@ -371,6 +385,8 @@ static void nft_rhash_gc(struct work_struct *work)
371385
if (!gc)
372386
goto try_later;
373387

388+
/* annotate gc sequence for this attempt. */
389+
he->wq_gc_seq = priv->wq_gc_seq;
374390
nft_trans_gc_elem_add(gc, he);
375391
}
376392

net/netfilter/nft_socket.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ static noinline int nft_socket_cgroup_subtree_level(void)
6868

6969
cgroup_put(cgrp);
7070

71-
if (WARN_ON_ONCE(level > 255))
71+
if (level > 255)
7272
return -ERANGE;
7373

7474
if (WARN_ON_ONCE(level < 0))

net/netfilter/xt_LED.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ static int led_tg_check(const struct xt_tgchk_param *par)
9696
struct xt_led_info_internal *ledinternal;
9797
int err;
9898

99-
if (ledinfo->id[0] == '\0')
99+
/* Bail out if empty string or not a string at all. */
100+
if (ledinfo->id[0] == '\0' ||
101+
!memchr(ledinfo->id, '\0', sizeof(ledinfo->id)))
100102
return -EINVAL;
101103

102104
mutex_lock(&xt_led_mutex);

0 commit comments

Comments
 (0)