diff options
author | David Mitchell <davem@iabyn.com> | 2014-12-21 00:40:13 +0000 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2014-12-31 11:28:51 +0000 |
commit | 2afa5dd07629c20c6931b8c5e4c1671520787a9d (patch) | |
tree | 4a00c51cd429f82d4cee295c264406698a83175c /av.c | |
parent | cbb52ffe38ad57101631b748bc04127568f728e2 (diff) | |
download | perl-2afa5dd07629c20c6931b8c5e4c1671520787a9d.tar.gz |
avoid integer overflow in Perl_av_extend_guts()
There were two issues; first the 'overextend' algorithm (add a fifth of
the current size to the requested size) could overflow,
and secondly MEM_WRAP_CHECK_1() was being called with newmax+1,
which could overflow if newmax happened to equal SSize_t_MAX.
e.g.
$a[0x7fffffffffffffff] = 1
$a[5] = 1; $a[0x7fffffffffffffff] = 1
could produce under ASan:
av.c:133:16: runtime error: signed integer overflow: 9223372036854775807 + 1 cannot be represented in type 'long'
av.c:170:7: runtime error: signed integer overflow: 9223372036854775807 + 1 cannot be represented in type 'long'
Diffstat (limited to 'av.c')
-rw-r--r-- | av.c | 16 |
1 files changed, 13 insertions, 3 deletions
@@ -130,14 +130,23 @@ Perl_av_extend_guts(pTHX_ AV *av, SSize_t key, SSize_t *maxp, SV ***allocp, if (key <= newmax) goto resized; #endif - newmax = key + *maxp / 5; + /* overflow-safe version of newmax = key + *maxp/5 */ + newmax = *maxp / 5; + newmax = (key > SSize_t_MAX - newmax) + ? SSize_t_MAX : key + newmax; resize: { #ifdef PERL_MALLOC_WRAP /* Duplicated in pp_hot.c */ static const char oom_array_extend[] = "Out of memory during array extend"; #endif - MEM_WRAP_CHECK_1(newmax+1, SV*, oom_array_extend); + /* it should really be newmax+1 here, but if newmax + * happens to equal SSize_t_MAX, then newmax+1 is + * undefined. This means technically we croak one + * index lower than we should in theory; in practice + * its unlikely the system has SSize_t_MAX/sizeof(SV*) + * bytes to spare! */ + MEM_WRAP_CHECK_1(newmax, SV*, oom_array_extend); } #ifdef STRESS_REALLOC { @@ -167,7 +176,8 @@ Perl_av_extend_guts(pTHX_ AV *av, SSize_t key, SSize_t *maxp, SV ***allocp, static const char oom_array_extend[] = "Out of memory during array extend"; #endif - MEM_WRAP_CHECK_1(newmax+1, SV*, oom_array_extend); + /* see comment above about newmax+1*/ + MEM_WRAP_CHECK_1(newmax, SV*, oom_array_extend); } Newx(*allocp, newmax+1, SV*); ary = *allocp + 1; |