diff options
author | David Mitchell <davem@iabyn.com> | 2011-05-29 19:27:56 +0100 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2011-05-29 20:21:54 +0100 |
commit | 26e935cfa6e70de1bd5258e0cb0f22ee976909f3 (patch) | |
tree | c1da6bd5bd35e1114dc50c1fa5db7f38b0c6d85c | |
parent | f5ada144f34d75c136b6780e10ca13d18d44c557 (diff) | |
download | perl-26e935cfa6e70de1bd5258e0cb0f22ee976909f3.tar.gz |
pp_formline: handle growing better
We initially grow PL_formtarget by the largest amount a formline can
append (since formats are fixed width). The only thing that can exceed
that is @*; but also, if PL_formtarget gets upgraded to utf8, then some of
the extra buffer we allocated can get eaten up by the upgrade.
Codify this so we only grow when necessary, but always enough.
Prior to this commit, we were growing FF_ITEM/FF_LITERAL too much, and
FF_LINEGLOB too little (the latter being my mistake from a few commits
ago).
Also rename 'fudge' to 'linemax', to give a better idea what it's for.
-rw-r--r-- | pp_ctl.c | 23 | ||||
-rw-r--r-- | t/op/write.t | 10 |
2 files changed, 27 insertions, 6 deletions
@@ -539,7 +539,7 @@ PP(pp_formline) NV value; bool gotsome = FALSE; /* seen at least one non-blank item on this line */ STRLEN len; - STRLEN fudge; /* estimate of output size in bytes */ + STRLEN linemax; /* estimate of output size in bytes */ bool item_is_utf8 = FALSE; bool targ_is_utf8 = FALSE; const char *fmt; @@ -561,8 +561,9 @@ PP(pp_formline) SvTAINTED_on(PL_formtarget); if (DO_UTF8(PL_formtarget)) targ_is_utf8 = TRUE; - fudge = (SvCUR(formsv) * (IN_BYTES ? 1 : 3) + 1); - t = SvGROW(PL_formtarget, len + fudge + 1); /* XXX SvCUR bad */ + linemax = (SvCUR(formsv) * (IN_BYTES ? 1 : 3) + 1); + t = SvGROW(PL_formtarget, len + linemax + 1); + /* XXX from now onwards, SvCUR(PL_formtarget) is invalid */ t += len; f = SvPV_const(formsv, len); @@ -842,6 +843,7 @@ PP(pp_formline) * if trans, translate certain characters during the copy */ { U8 *tmp = NULL; + STRLEN grow = 0; SvCUR_set(PL_formtarget, t - SvPVX_const(PL_formtarget)); @@ -858,6 +860,11 @@ PP(pp_formline) targ_is_utf8 = TRUE; /* re-calculate linemark */ s = (U8*)SvPVX(PL_formtarget); + /* the bytes we initially allocated to append the + * whole line may have been gobbled up during the + * upgrade, so allocate a whole new line's worth + * for safety */ + grow = linemax; while (linemark--) s += UTF8SKIP(s); linemark = s - (U8*)SvPVX(PL_formtarget); @@ -865,8 +872,14 @@ PP(pp_formline) /* Easy. They agree. */ assert (item_is_utf8 == targ_is_utf8); } - SvGROW(PL_formtarget, - SvCUR(PL_formtarget) + to_copy + 1); + if (!trans) + /* @* and ^* are the only things that can exceed + * the linemax, so grow by the output size, plus + * a whole new form's worth in case of any further + * output */ + grow = linemax + to_copy; + if (grow) + SvGROW(PL_formtarget, SvCUR(PL_formtarget) + grow + 1); t = SvPVX(PL_formtarget) + SvCUR(PL_formtarget); Copy(source, t, to_copy, char); diff --git a/t/op/write.t b/t/op/write.t index 27effdeb41..c2e3399305 100644 --- a/t/op/write.t +++ b/t/op/write.t @@ -61,7 +61,7 @@ for my $tref ( @NumTests ){ my $bas_tests = 20; # number of tests in section 3 -my $bug_tests = 4 + 3 * 3 * 5 * 2 * 3 + 2 + 66 + 2 + 2 + 1 + 1; +my $bug_tests = 4 + 3 * 3 * 5 * 2 * 3 + 2 + 66 + 3 + 2 + 1 + 1; # number of tests in section 4 my $hmb_tests = 35; @@ -704,6 +704,14 @@ ok defined *{$::{CmT}}{FORMAT}, "glob assign"; $^A = $orig; formline $format, " "; is $^A, "$orig\n", "end-of-line blanks and realloc"; + + # and check this doesn't overflow the buffer + + local $^A = ''; + $format = "@* @####\n"; + $orig = "x" x 100 . "\n"; + formline $format, $orig, 12345; + is $^A, ("x" x 100) . " 12345\n", "\@* doesn't overflow"; } |