This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
fix panic caused by printf with %n with utf8 length magic
authorTony Cook <tony@develop-help.com>
Wed, 30 Oct 2019 02:06:34 +0000 (13:06 +1100)
committerNicolas R <nicolas@atoomic.org>
Thu, 31 Oct 2019 15:00:05 +0000 (09:00 -0600)
fix the reported issue with gh #17221

sv.c
t/op/sprintf2.t

diff --git a/sv.c b/sv.c
index 4bd42b4..c1e9346 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -13284,7 +13284,9 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
                         Perl_croak_nocontext(
                             "Missing argument for %%n in %s",
                                 PL_op ? OP_DESC(PL_op) : "sv_vcatpvfn()");
-                    sv_setuv_mg(argsv, has_utf8 ? (UV)sv_len_utf8(sv) : (UV)len);
+                    sv_setuv_mg(argsv, has_utf8
+                        ? (UV)utf8_length((U8*)SvPVX(sv), (U8*)SvEND(sv))
+                        : (UV)len);
                 }
                 goto done_valid_conversion;
             }
@@ -13443,6 +13445,13 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
                PL_op ? OP_DESC(PL_op) : "sv_vcatpvfn()");
     }
 
+    if (SvTYPE(sv) >= SVt_PVMG && SvMAGIC(sv)) {
+        /* while we shouldn't set the cache, it may have been previously
+           set in the caller, so clear it */
+        MAGIC *mg = mg_find(sv, PERL_MAGIC_utf8);
+        if (mg)
+            magic_setutf8(sv,mg); /* clear UTF8 cache */
+    }
     SvTAINT(sv);
 }
 
index 5fee8ef..af448cd 100644 (file)
@@ -1160,4 +1160,15 @@ if ($Config{intsize} == 4 && $Config{uvsize} > 4 && $Config{sizesize} > 4) {
     like($@, qr/^Numeric format result too large at /,
          "croak for very large numeric format results");
 }
+
+{
+    # gh #17221
+    my ($off1, $off2);
+    my $x = eval { sprintf "%n0%n\x{100}", $off1, $off2 };
+    is($@, "", "no exception");
+    is($x, "0\x{100}", "reasonable result");
+    is($off1, 0, "offset at start");
+    is($off2, 1, "offset after 0");
+}
+
 done_testing();