Skip to content

Commit 34457de

Browse files
veritas501gregkh
authored andcommitted
net/sched: act_mirred: Fix blockcast recursion bypass leading to stack overflow
[ Upstream commit a005fa5 ] tcf_mirred_act() checks sched_mirred_nest against MIRRED_NEST_LIMIT (4) to prevent deep recursion. However, when the action uses blockcast (tcfm_blockid != 0), the function returns at the tcf_blockcast() call BEFORE reaching the counter increment. As a result, the recursion counter never advances and the limit check is entirely bypassed. When two devices share a TC egress block with a mirred blockcast rule, a packet egressing on device A is mirrored to device B via blockcast; device B's egress TC re-enters tcf_mirred_act() via blockcast and mirrors back to A, creating an unbounded recursion loop: tcf_mirred_act -> tcf_blockcast -> tcf_mirred_to_dev -> dev_queue_xmit -> sch_handle_egress -> tcf_classify -> tcf_mirred_act -> (repeat) This recursion continues until the kernel stack overflows. The bug is reachable from an unprivileged user via unshare(CLONE_NEWUSER | CLONE_NEWNET): user namespaces grant CAP_NET_ADMIN in the new network namespace, which is sufficient to create dummy devices, attach clsact qdiscs with shared blocks, and install mirred blockcast filters. BUG: TASK stack guard page was hit at ffffc90000b7fff8 Oops: stack guard page: 0000 [#1] SMP KASAN NOPTI CPU: 2 UID: 1000 PID: 169 Comm: poc Not tainted 7.0.0-rc7-next-20260410 RIP: 0010:xas_find+0x17/0x480 Call Trace: xa_find+0x17b/0x1d0 tcf_mirred_act+0x640/0x1060 tcf_action_exec+0x400/0x530 basic_classify+0x128/0x1d0 tcf_classify+0xd83/0x1150 tc_run+0x328/0x620 __dev_queue_xmit+0x797/0x3100 tcf_mirred_to_dev+0x7b1/0xf70 tcf_mirred_act+0x68a/0x1060 [repeating ~30+ times until stack overflow] Kernel panic - not syncing: Fatal exception in interrupt Fix this by incrementing sched_mirred_nest before calling tcf_blockcast() and decrementing it on return, mirroring the non-blockcast path. This ensures subsequent recursive entries see the updated counter and are correctly limited by MIRRED_NEST_LIMIT. Fixes: fe946a7 ("net/sched: act_mirred: add loop detection") Signed-off-by: Kito Xu (veritas501) <hxzene@gmail.com> Link: https://patch.msgid.link/20260525122556.973584-7-jhs@mojatatu.com Signed-off-by: Paolo Abeni <pabeni@redhat.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 45ac526 commit 34457de

1 file changed

Lines changed: 11 additions & 7 deletions

File tree

net/sched/act_mirred.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -396,14 +396,12 @@ static int tcf_blockcast_mirror(struct sk_buff *skb, struct tcf_mirred *m,
396396

397397
static int tcf_blockcast(struct sk_buff *skb, struct tcf_mirred *m,
398398
const u32 blockid, struct tcf_result *res,
399-
int retval)
399+
int m_eaction, int retval)
400400
{
401401
const u32 exception_ifindex = skb->dev->ifindex;
402402
struct tcf_block *block;
403403
bool is_redirect;
404-
int m_eaction;
405404

406-
m_eaction = READ_ONCE(m->tcfm_eaction);
407405
is_redirect = tcf_mirred_is_act_redirect(m_eaction);
408406

409407
/* we are already under rcu protection, so can call block lookup
@@ -453,8 +451,16 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
453451
tcf_action_update_bstats(&m->common, skb);
454452

455453
blockid = READ_ONCE(m->tcfm_blockid);
456-
if (blockid)
457-
return tcf_blockcast(skb, m, blockid, res, retval);
454+
m_eaction = READ_ONCE(m->tcfm_eaction);
455+
want_ingress = tcf_mirred_act_wants_ingress(m_eaction);
456+
if (blockid) {
457+
if (!want_ingress)
458+
xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = NULL;
459+
retval = tcf_blockcast(skb, m, blockid, res, m_eaction, retval);
460+
if (!want_ingress)
461+
xmit->sched_mirred_nest--;
462+
return retval;
463+
}
458464

459465
dev = rcu_dereference_bh(m->tcfm_dev);
460466
if (unlikely(!dev)) {
@@ -463,8 +469,6 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
463469
return retval;
464470
}
465471

466-
m_eaction = READ_ONCE(m->tcfm_eaction);
467-
want_ingress = tcf_mirred_act_wants_ingress(m_eaction);
468472
if (!want_ingress) {
469473
for (i = 0; i < xmit->sched_mirred_nest; i++) {
470474
if (xmit->sched_mirred_dev[i] != dev)

0 commit comments

Comments
 (0)