Skip to content

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

Closed
ThePHPF/thephp.foundation
#90

Description

@bwoebi

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

Activity

  1. self-assigned this
    on May 5, 2023
  2. added a commit that references this issue on May 5, 2023
    05bd142
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions