summaryrefslogtreecommitdiff
path: root/util.c
diff options
context:
space:
mode:
authorFather Chrysostomos <sprout@cpan.org>2012-04-16 20:24:45 -0700
committerFather Chrysostomos <sprout@cpan.org>2012-04-16 20:24:45 -0700
commit78e230aef16bcd45dba3b038bb0883d528a495bd (patch)
tree65ca2bc430be792fa554a2d7ecabc92de9d70337 /util.c
parent04bd644859054f7d9eeb94b6c7a3e2b76872a6ab (diff)
downloadperl-78e230aef16bcd45dba3b038bb0883d528a495bd.tar.gz
[perl #112478] Avoid buffer overflow in upg_version
On most systems, this is actually a panic, rather than an overflow, because the overflow is detected before it can happen. upg_version needs to use the equivalent of sprintf "%.9f" on a numeric input before parsing it. For speed’s sake, I assume, it was done using my_snprintf, with a C auto for the buffer, declared with a fixed size of 64. There is no guarantee that the number passed in will not overflow that buffer, so upg_version should use an SV and sv_catpvf in those cases where it would overflow.
Diffstat (limited to 'util.c')
-rw-r--r--util.c18
1 files changed, 14 insertions, 4 deletions
diff --git a/util.c b/util.c
index e6b5fa582e..c7ddd7373b 100644
--- a/util.c
+++ b/util.c
@@ -4959,18 +4959,28 @@ Perl_upg_version(pTHX_ SV *ver, bool qv)
/* may get too much accuracy */
char tbuf[64];
+ SV *sv = SvNVX(ver) > 10e50 ? newSV(64) : 0;
+ char *buf;
#ifdef USE_LOCALE_NUMERIC
char *loc = savepv(setlocale(LC_NUMERIC, NULL));
setlocale(LC_NUMERIC, "C");
#endif
- len = my_snprintf(tbuf, sizeof(tbuf), "%.9"NVff, SvNVX(ver));
+ if (sv) {
+ Perl_sv_catpvf(aTHX_ sv, "%.9"NVff, SvNVX(ver));
+ buf = SvPV(sv, len);
+ }
+ else {
+ len = my_snprintf(tbuf, sizeof(tbuf), "%.9"NVff, SvNVX(ver));
+ buf = tbuf;
+ }
#ifdef USE_LOCALE_NUMERIC
setlocale(LC_NUMERIC, loc);
Safefree(loc);
#endif
- while (tbuf[len-1] == '0' && len > 0) len--;
- if ( tbuf[len-1] == '.' ) len--; /* eat the trailing decimal */
- version = savepvn(tbuf, len);
+ while (buf[len-1] == '0' && len > 0) len--;
+ if ( buf[len-1] == '.' ) len--; /* eat the trailing decimal */
+ version = savepvn(buf, len);
+ SvREFCNT_dec(sv);
}
#ifdef SvVOK
else if ( (mg = SvVSTRING_mg(ver)) ) { /* already a v-string */