Commit 1e4eb1ca98 for perl
commit 1e4eb1ca98a6d1277bad0c0deaf819d4dcdcf239
Author: Paul Evans <leonerd@leonerd.org.uk>
Date: Fri Oct 23 14:38:46 2026 +0100
Also permit SVt_PVLV in the SvOOK macros because substr() uses it (fixes #24933)
The added test is, as far as I can tell, the only test we have of
substr()-in-substr(), which is why this bug wasn't previously detected
by core tests.
diff --git a/pod/perldelta.pod b/pod/perldelta.pod
index a55eee3d5c..3478513a26 100644
--- a/pod/perldelta.pod
+++ b/pod/perldelta.pod
@@ -377,6 +377,23 @@ multiple branches of an C<if>/C<elsif>/C<else> chain call the same function.
L<B::Xref> is able to use line numbers stored within nulled COPs. [GH #7947]
+=item *
+
+Perl 5.44.0 introduced some internal changes that broke uses of C<substr> when
+the string argument it is operating on was itself a special lvalue scalar
+created by another C<substr> call. This would cause debugging assertions or
+invalid C<free()> crashes from code that had a structure such as the following
+examples:
+
+ my $ref = \substr($str, $offset);
+ substr($$ref, 0, 1, "replacement");
+
+Z<>
+
+ substr(substr($str, $offset), 0, 1, "replacement");
+
+This bug has now been fixed. [GH #24933]
+
=back
=head1 Known Problems
diff --git a/sv.h b/sv.h
index e49657ae3c..becc080e94 100644
--- a/sv.h
+++ b/sv.h
@@ -992,7 +992,7 @@ Set the size of the string buffer for the SV. See C<L</SvLEN>>.
SVp_IOK|SVp_NOK|SVf_IVisUV))
#define assert_scalar_or_IO_(sv) \
- assert_((SvTYPE(sv) <= SVt_PVMG) || (SvTYPE(sv) == SVt_PVIO))
+ assert_(SvTYPE(sv) <= SVt_PVMG || SvTYPE(sv) == SVt_PVLV || SvTYPE(sv) == SVt_PVIO)
#define assert_not_ROK(sv) assert_(!SvROK(sv) || !SvRV(sv))
#define assert_not_glob(sv) assert_(!isGV_with_GP(sv))
@@ -1118,6 +1118,7 @@ Returns the vstring magic, or NULL if none
* up using it despite being an SVt_PVIO. So we accept those cases as well.
*/
#define SvOOK(sv) ((SvTYPE(sv) <= SVt_PVMG || /* regular scalars */ \
+ SvTYPE(sv) == SVt_PVLV || /* PVLV is a regular scalar too */ \
SvTYPE(sv) == SVt_PVHV || /* hashes */ \
SvTYPE(sv) == SVt_PVIO) && /* IOs */ \
((SvFLAGS(sv) & (SVf_ROK|SVf_OOK)) == SVf_OOK))
diff --git a/t/op/substr.t b/t/op/substr.t
index c951fe27d6..8003d75aa6 100644
--- a/t/op/substr.t
+++ b/t/op/substr.t
@@ -22,7 +22,8 @@ $SIG{__WARN__} = sub {
}
};
-plan(400);
+# We can't use done_testing() at the end because of odd interactions with t/op/substr_thr.t
+plan(401);
run_tests() unless caller;
@@ -918,4 +919,14 @@ fresh_perl_is('$0 = "/usr/bin/perl"; substr($0, 0, 0, $0)', '', {}, "(perl #1293
is $bar, boz => 'first arg to 4-arg substr is loose lvalue context';
}
+# [perl GH24933]
+{
+ # Under this bug, this would cause an assertion failure or double-free().
+ # The mere fact it doesn't crash is sufficient to test for absence of the
+ # bug.
+ my $s = "abcdef";
+ substr(substr($s, 3), 0, 1, "");
+ pass "substr(substr()) does not crash";
+}
+
1;