Thread (4 messages) 4 messages, 3 authors, 1h ago
HOTtoday

[PATCH net] net: openvswitch: fix stack exhaustion from unaccounted clone_execute() recursion

From: yang zhuorao <hidden>
Date: 2026-07-28 06:06:36
Subsystem: networking [general], openvswitch, the rest · Maintainers: "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Aaron Conole, Eelco Chaudron, Ilya Maximets, Linus Torvalds

clone_execute() only increments and decrements exec_level when
clone_flow_key is true.  When clone_flow_key is false the optimized
path reuses the original flow key and calls do_execute_actions()
directly without updating the recursion counter, making those layers
invisible to the OVS_RECURSION_LIMIT check in ovs_execute_actions().

A single action tree allows up to OVS_COPY_ACTIONS_MAX_DEPTH (16)
nested clone actions.  When the innermost clone contains a RECIRC
that selects another flow with its own 16-layer validation budget,
multiple trees of unaccounted recursion can be stacked.  The deferred
action threshold (OVS_DEFERRED_ACTION_THRESHOLD =
OVS_RECURSION_LIMIT - 2 = 3) allows three synchronous recirc levels
before clone_key() forces deferral, yielding 3 x 16 = 48 layers of
synchronous, unaccounted clone recursion.

Per-layer stack usage is approximately 296 bytes (from disassembly:
do_execute_actions() allocates 0x98 bytes, clone_execute() allocates
0x20 bytes, plus saved registers and return address).  48 layers
consume ~14,208 bytes before recirc, flow lookup, Netlink, and base
call frames, exceeding the 16 KiB x86_64 task stack and hitting the
guard page.

An unprivileged user can trigger this from a private user/net
namespace (clone(CLONE_NEWUSER | CLONE_NEWNET)) by installing three
flow rules each with 16 nested clone actions linked by RECIRC, then
injecting a single packet via OVS_PACKET_CMD_EXECUTE.  The result is
a kernel panic:

    BUG: TASK stack guard page was hit ...
    CPU: 0 UID: 1000 PID: 85 ... 7.2.0-rc5+
    RIP: 0010:clone_execute+0x5a/0x2c0 [openvswitch]
    [do_execute_actions / clone_execute alternating repeatedly]
    Kernel panic - not syncing: Fatal exception in interrupt

Fix this by unconditionally incrementing exec_level for all
synchronous clone recursion edges and checking OVS_RECURSION_LIMIT
before calling do_execute_actions().  When the limit is exceeded the
packet is dropped and -ENETDOWN is returned, consistent with the
existing handling in ovs_execute_actions().

Tested on Linux 7.2.0-rc5+ (mainline HEAD 62cc90241548), x86_64,
CONFIG_VMAP_STACK=y, CONFIG_OPENVSWITCH=m, isolated QEMU guest.
Confirmed that the PoC (./poc_clone_overflow 2 16 3, run as UID 1000)
no longer triggers a panic with this patch applied.

Fixes: b233504033db ("openvswitch: kernel datapath clone action")
Cc: stable@vger.kernel.org
Reported-by: yang zhuorao <redacted>
Signed-off-by: yang zhuorao <redacted>
---
Reproducer (C, single file, available upon request):

    gcc -O2 -Wall -o poc_clone_overflow poc_clone_overflow.c
    ./poc_clone_overflow 2 16 3   # as unprivileged user

The PoC creates a private user/net namespace, installs three flows
each with 16 nested clone actions linked by RECIRC, and injects a
triggering packet.  Prerequisites: CONFIG_OPENVSWITCH=m/y loaded,
unprivileged user namespace creation allowed.

Verified unfixed in Linus mainline (62cc90241548), net (97ac08560d23),
and net-next (a50eba1e778a) as of 2026-07-27.

 net/openvswitch/actions.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index 513fca6a8e8a..05fbf5c07200 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -1497,14 +1497,19 @@ static int clone_execute(struct datapath *dp, struct sk_buff *skb,
 	if (clone) {
 		int err = 0;
 		if (actions) { /* Sample action */
-			if (clone_flow_key)
-				__this_cpu_inc(ovs_pcpu_storage->exec_level);
+			__this_cpu_inc(ovs_pcpu_storage->exec_level);
+
+			if (unlikely(__this_cpu_read(ovs_pcpu_storage->exec_level) >
+				     OVS_RECURSION_LIMIT)) {
+				__this_cpu_dec(ovs_pcpu_storage->exec_level);
+				ovs_kfree_skb_reason(skb, OVS_DROP_RECURSION_LIMIT);
+				return -ENETDOWN;
+			}

 			err = do_execute_actions(dp, skb, clone,
 						 actions, len);

-			if (clone_flow_key)
-				__this_cpu_dec(ovs_pcpu_storage->exec_level);
+			__this_cpu_dec(ovs_pcpu_storage->exec_level);
 		} else { /* Recirc action */
 			clone->recirc_id = recirc_id;
 			ovs_dp_process_packet(skb, clone);
--
2.43.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help