04bfb0
From 027471cf1095f75f273df40310e4647fe1e8a9df Mon Sep 17 00:00:00 2001
04bfb0
From: Tony Cook <tony@develop-help.com>
04bfb0
Date: Wed, 20 Mar 2019 16:47:49 +1100
04bfb0
Subject: [PATCH] (perl #133913) limit numeric format results to INT_MAX
04bfb0
MIME-Version: 1.0
04bfb0
Content-Type: text/plain; charset=UTF-8
04bfb0
Content-Transfer-Encoding: 8bit
04bfb0
04bfb0
The return value of v?snprintf() is int, and we pay attention to that
04bfb0
return value, so limit the expected size of numeric formats to
04bfb0
INT_MAX.
04bfb0
04bfb0
Signed-off-by: Petr Písař <ppisar@redhat.com>
04bfb0
---
04bfb0
 pod/perldiag.pod | 6 ++++++
04bfb0
 sv.c             | 7 +++++++
04bfb0
 t/op/sprintf2.t  | 7 +++++++
04bfb0
 3 files changed, 20 insertions(+)
04bfb0
04bfb0
diff --git a/pod/perldiag.pod b/pod/perldiag.pod
04bfb0
index 1037215d44..166d29b4bb 100644
04bfb0
--- a/pod/perldiag.pod
04bfb0
+++ b/pod/perldiag.pod
04bfb0
@@ -4354,6 +4354,12 @@ the meantime, try using scientific notation (e.g. "1e6" instead of
04bfb0
 a number.  This happens, for example with C<\o{}>, with no number between
04bfb0
 the braces.
04bfb0
 
04bfb0
+=item Numeric format result too large
04bfb0
+
04bfb0
+(F) The length of the result of a numeric format supplied to sprintf()
04bfb0
+or printf() would have been too large for the underlying C function to
04bfb0
+report.  This limit is typically 2GB.
04bfb0
+
04bfb0
 =item Octal number > 037777777777 non-portable
04bfb0
 
04bfb0
 (W portable) The octal number you specified is larger than 2**32-1
04bfb0
diff --git a/sv.c b/sv.c
04bfb0
index 8fbca52eb2..8bc0af0c16 100644
04bfb0
--- a/sv.c
04bfb0
+++ b/sv.c
04bfb0
@@ -13085,6 +13085,13 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
04bfb0
 	    if (float_need < width)
04bfb0
 		float_need = width;
04bfb0
 
04bfb0
+            if (float_need > INT_MAX) {
04bfb0
+                /* snprintf() returns an int, and we use that return value,
04bfb0
+                   so die horribly if the expected size is too large for int
04bfb0
+                */
04bfb0
+                Perl_croak(aTHX_ "Numeric format result too large");
04bfb0
+            }
04bfb0
+
04bfb0
 	    if (PL_efloatsize <= float_need) {
04bfb0
                 /* PL_efloatbuf should be at least 1 greater than
04bfb0
                  * float_need to allow a trailing \0 to be returned by
04bfb0
diff --git a/t/op/sprintf2.t b/t/op/sprintf2.t
04bfb0
index 84259a4afd..5fee8efede 100644
04bfb0
--- a/t/op/sprintf2.t
04bfb0
+++ b/t/op/sprintf2.t
04bfb0
@@ -1153,4 +1153,11 @@ foreach(
04bfb0
     is sprintf("%.0f", $_), sprintf("%-.0f", $_), "special-case %.0f on $_";
04bfb0
 }
04bfb0
 
04bfb0
+# large uvsize needed so the large width is parsed properly
04bfb0
+# large sizesize needed so the STRLEN check doesn't
04bfb0
+if ($Config{intsize} == 4 && $Config{uvsize} > 4 && $Config{sizesize} > 4) {
04bfb0
+    eval { my $x = sprintf("%7000000000E", 0) };
04bfb0
+    like($@, qr/^Numeric format result too large at /,
04bfb0
+         "croak for very large numeric format results");
04bfb0
+}
04bfb0
 done_testing();
04bfb0
-- 
04bfb0
2.20.1
04bfb0