Skip to content

Exceeding memory limit in zend_hash_do_resize leaves the array in an invalid state #11189

Closed
ThePHPF/thephp.foundation
#90
@bwoebi

Description

Description

Cross-posting a report we got on our extension: DataDog/dd-trace-php#2030 (comment)

php -dmemory_limit=10M -r 'ob_start(function() { global $a; for ($i = count($a); $i > 0; --$i) { $a[] = 2; } }); $a = []; while (1) { $a[] = 1; }'

Getting a memory limit exceeded error in zend_hash_do_resize crashes when the array is manipulated afterwards, with a following backtrace:

#0  _zend_hash_index_add_or_update_i (flag=18, pData=0xfffff4690150, h=262399, ht=0xfffff4655230) at /usr/local/src/php/Zend/zend_hash.c:1051
#1  zend_hash_next_index_insert (ht=ht@entry=0xfffff4655230, pData=0xfffff4690150) at /usr/local/src/php/Zend/zend_hash.c:1142
#2  0x0000aaaaaaf7c8bc in ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_DATA_CONST_HANDLER () at /usr/local/src/php/Zend/zend_vm_execute.h:47376
#3  0x0000aaaaaaf98558 in execute_ex (ex=0xfffff3c00ff8) at /usr/local/src/php/Zend/zend_vm_execute.h:59995
#4  0x0000aaaaaaf17464 in zend_call_function (fci_cache=0xfffff4654120, fci=0xfffff46540e0) at /usr/local/src/php/Zend/zend_execute_API.c:930
#5  zend_call_function (fci=0xfffff46540e0, fci_cache=<optimized out>) at /usr/local/src/php/Zend/zend_execute_API.c:732
#6  0x0000aaaaaaf2f964 in zend_fcall_info_call (fci=0xfffff46540e0, fcc=0xfffff4654120, retval_ptr=retval_ptr@entry=0xffffffffd218, args=args@entry=0x0) at /usr/local/src/php/Zend/zend_API.c:4137
#7  0x0000aaaaaaed60f4 in php_output_handler_op (context=0xffffffffd248, handler=0xfffff466f050) at /usr/local/src/php/main/output.c:970
#8  php_output_stack_pop (flags=flags@entry=17) at /usr/local/src/php/main/output.c:1231
#9  0x0000aaaaaaed6598 in php_output_discard_all () at /usr/local/src/php/main/output.c:365
#10 0x0000aaaaaabde368 in php_error_cb (orig_type=1, error_filename=0xfffff465b360, error_lineno=1, message=0xfffff4654150) at /usr/local/src/php/main/main.c:1274
#11 0x0000aaaaaabdfbe8 in zend_error_zstr_at (orig_type=orig_type@entry=1, error_filename=error_filename@entry=0xfffff465b360, error_lineno=error_lineno@entry=1, message=message@entry=0xfffff4654150) at /usr/local/src/php/Zend/zend.c:1390
#12 0x0000aaaaaabdff48 in zend_error_va_list (orig_type=1, error_filename=0xfffff465b360, error_lineno=1, format=<optimized out>, args=<error reading variable: Cannot access memory at address 0x80000>) at /usr/local/src/php/Zend/zend.c:1483
#13 0x0000aaaaaabe02a0 in zend_error_noreturn (type=type@entry=1, format=format@entry=0xaaaaab87f740 "Allowed memory size of %zu bytes exhausted (tried to allocate %zu bytes)") at /usr/local/src/php/Zend/zend.c:1590
#14 0x0000aaaaaabdf594 in zend_mm_safe_error (format=format@entry=0xaaaaab87f740 "Allowed memory size of %zu bytes exhausted (tried to allocate %zu bytes)", limit=10485760, size=size@entry=8388616, heap=<optimized out>) at /usr/local/src/php/Zend/zend_alloc.c:383
#15 0x0000aaaaaaefb094 in zend_mm_realloc_huge (heap=0xfffff4600040, ptr=0xfffff3800000, size=8388616, copy_size=4194312) at /usr/local/src/php/Zend/zend_alloc.c:1542
#16 0x0000aaaaaaf327b8 in zend_hash_packed_grow (ht=0xfffff4655230) at /usr/local/src/php/Zend/zend_types.h:648
#17 0x0000aaaaaaf35490 in _zend_hash_index_add_or_update_i (flag=18, pData=0xfffff4693160, h=262144, ht=0xfffff4655230) at /usr/local/src/php/Zend/zend_hash.c:1057
#18 zend_hash_next_index_insert (ht=ht@entry=0xfffff4655230, pData=0xfffff4693160) at /usr/local/src/php/Zend/zend_hash.c:1142
#19 0x0000aaaaaaf7c8bc in ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_DATA_CONST_HANDLER () at /usr/local/src/php/Zend/zend_vm_execute.h:47376
#20 0x0000aaaaaaf98558 in execute_ex (ex=0xfffff3c00ff8) at /usr/local/src/php/Zend/zend_vm_execute.h:59995
#21 0x0000aaaaaafa2648 in zend_execute (op_array=0xfffff467d100, return_value=0xffffffffd968) at /usr/local/src/php/Zend/zend_vm_execute.h:60380
#22 0x0000aaaaaaf16aa8 in zend_eval_stringl (str=<optimized out>, str_len=<optimized out>, retval_ptr=0x0, string_name=<optimized out>) at /usr/local/src/php/Zend/zend_execute_API.c:1279
#23 0x0000aaaaaaf16c44 in zend_eval_stringl_ex (str=<optimized out>, str_len=<optimized out>, retval_ptr=<optimized out>, string_name=<optimized out>, handle_exceptions=true) at /usr/local/src/php/Zend/zend_execute_API.c:1321
#24 0x0000aaaaab0102f4 in do_cli (argc=4, argv=0xaaaaaba52870) at /usr/local/src/php/sapi/cli/php_cli.c:995
#25 0x0000aaaaaabf76f8 in main (argc=4, argv=<optimized out>) at /usr/local/src/php/sapi/cli/php_cli.c:1333

This happens due to the order of operations being the wrong way round in zend_hash_do_resize:

php-src/Zend/zend_hash.c

Lines 1250 to 1251 in a65cdd9

ht->nTableSize = nSize;
new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);

It's setting the nTableSize before attempting to do the allocation, leaving the array in an unstable state.

PHP Version

PHP 8.1+

Operating System

No response

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions