From 7dbc795b44eb54d41e1c6b30d8796525d65d52b5 Mon Sep 17 00:00:00 2001
Message-ID: <7dbc795b44eb54d41e1c6b30d8796525d65d52b5.1763833678.git.sam@gentoo.org>
From: Lukas Mai <lukasmai.403@gmail.com>
Date: Fri, 11 Jul 2025 09:07:00 +0200
Subject: [PATCH 1/4] newFOROP: fix crash when optimizing 2-var for over
 builtin::indexed

OP_ENTERSUB isn't necessarily a LISTOP, apparently, so we can't just
grab its op_last. Instead, copy/paste logic from elsewhere in op.c to
find the cvop.

Also, avoid crashing on "fake" pad entries that represent lexical subs
from outer scopes by climbing up the scope chain until we reach a real
pad entry.

Fixes #23405.

(cherry picked from commit 96673a4bb36a973a9a4c5cd0e5727a799789a32c)
Signed-off-by: Sam James <sam@gentoo.org>
---
 op.c            | 14 +++++++++++---
 t/op/for-many.t | 13 +++++++++++++
 2 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/op.c b/op.c
index f616532c49..3c316ea8b4 100644
--- a/op.c
+++ b/op.c
@@ -9665,7 +9665,7 @@ S_op_is_cv_xsub(pTHX_ OP *o, XSUBADDR_t xsub)
         }
 
         case OP_PADCV:
-            cv = (CV *)PAD_SVl(o->op_targ);
+            cv = find_lexical_cv(o->op_targ);
             assert(cv && SvTYPE(cv) == SVt_PVCV);
             break;
 
@@ -9683,10 +9683,18 @@ S_op_is_cv_xsub(pTHX_ OP *o, XSUBADDR_t xsub)
 static bool
 S_op_is_call_to_cv_xsub(pTHX_ OP *o, XSUBADDR_t xsub)
 {
-    if(o->op_type != OP_ENTERSUB)
+    if (o->op_type != OP_ENTERSUB)
         return false;
 
-    OP *cvop = cLISTOPx(cUNOPo->op_first)->op_last;
+    /* entersub may be a UNOP, not a LISTOP, so we can't just use op_last */
+    OP *aop = cUNOPo->op_first;
+    if (!OpHAS_SIBLING(aop)) {
+        aop = cUNOPx(aop)->op_first;
+    }
+    aop = OpSIBLING(aop);
+    OP *cvop;
+    for (cvop = aop; OpHAS_SIBLING(cvop); cvop = OpSIBLING(cvop)) ;
+
     return op_is_cv_xsub(cvop, xsub);
 }
 
diff --git a/t/op/for-many.t b/t/op/for-many.t
index 2f6790aee7..035d1da07e 100644
--- a/t/op/for-many.t
+++ b/t/op/for-many.t
@@ -498,4 +498,17 @@ is($continue, 'xx', 'continue reached twice');
     is("@have", "Pointy end Up Flamey end Down", 'for my ($one, $two)');
 }
 
+# GH #23405 - segfaults when compiling 2-var for loops
+{
+    my $dummy = sub {};
+    for my ($x, $y) (main->$dummy) {}
+    pass '2-var for does not crash on method calls';
+
+    my sub dummy {}
+    sub {
+        for my ($x, $y) (dummy) {}
+    }->();
+    pass '2-var for does not crash on lexical sub calls';
+}
+
 done_testing();
-- 
2.52.0

