This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
pp_formline(): avoid buffer overrun
authorDavid Mitchell <davem@iabyn.com>
Sat, 18 Feb 2017 10:20:00 +0000 (10:20 +0000)
committerDavid Mitchell <davem@iabyn.com>
Sat, 18 Feb 2017 10:20:00 +0000 (10:20 +0000)
RT #130703

My recent commit v5.25.9-77-g90c3aa0 attempted to simplify buffer growth
in pp_formline(), but missed the operators which append data to
PL_formtarget *without* doing 'goto append'. These ops either append a
fieldsize's worth of bytes, or a \n (FF_NEWLINE). So grow by fieldsize
whenever we fetch something new, and for each FF_NEWLINE.

pp_ctl.c
t/op/write.t

index 2fcceb2..c4c8bcd 100644 (file)
--- a/pp_ctl.c
+++ b/pp_ctl.c
@@ -566,6 +566,13 @@ PP(pp_formline)
            arg = *fpc++;
            f += arg;
            fieldsize = arg;
+            {
+                STRLEN cur = t - SvPVX_const(PL_formtarget);
+                /* ensure there's always space for the ops which don't
+                 * 'goto append' to unconditionally append a field's
+                 * worth, e.g. FF_SPACE, FF_DECIMAL */
+                t = SvGROW(PL_formtarget, cur + fieldsize + 1) + cur;
+            }
 
            if (MARK < SP)
                sv = *++MARK;
@@ -872,6 +879,10 @@ PP(pp_formline)
        case FF_NEWLINE: /* delete trailing spaces, then append \n */
            f++;
            while (t-- > (SvPVX(PL_formtarget) + linemark) && *t == ' ') ;
+            {
+                STRLEN cur = t - SvPVX_const(PL_formtarget);
+                t = SvGROW(PL_formtarget, cur + 1 + 1) + cur;
+            }
            t++;
            *t++ = '\n';
            break;
index 99f827f..d528a8e 100644 (file)
@@ -98,7 +98,7 @@ for my $tref ( @NumTests ){
 my $bas_tests = 21;
 
 # number of tests in section 3
-my $bug_tests = 66 + 3 * 3 * 5 * 2 * 3 + 2 + 66 + 6 + 2 + 3 + 96 + 11 + 14;
+my $bug_tests = 66 + 3 * 3 * 5 * 2 * 3 + 2 + 66 + 6 + 2 + 3 + 96 + 11 + 15;
 
 # number of tests in section 4
 my $hmb_tests = 37;
@@ -1577,6 +1577,19 @@ ok  defined *{$::{CmT}}{FORMAT}, "glob assign";
         is $^A, $expected, "RT #130703";
     }
 
+    # further buffer overflows with RT #130703
+
+    {
+        local $^A = '';
+        my $n = 200;
+        my $long = 'x' x 300;
+        my $numf = ('@###' x $n);
+        my $expected = $long . "\n" . ("   1" x $n);
+        formline("@*\n$numf", $long, ('1') x $n);
+
+        is $^A, $expected, "RT #130703 part 2";
+    }
+
 
     # make sure it can cope with formats > 64k