Skip to content

Closure named argument unpacking between temporary closures can cause a crash #19653

Closed

Description

@ivan-u7n

Description

The following code:

<?php

function func1(string $a1 = 'a1', string $a2 = 'a2', string $a3 = 'a3') {
	echo __FUNCTION__ . "() a1=$a1 a2=$a2 a3=$a3\n";
}

function usage1(?Closure $func1 = null) {
	echo __FUNCTION__ . "() ";
	($func1 ?? func1(...))(a3: 'm3+');
}
usage1();

$func1 = function (string ...$args) {
	echo "[function] ";
	func1(...$args, a2: 'm2+');
};
usage1(func1: $func1);

Resulted in this output:

usage1() func1() a1=a1 a2=a2 a3=m3+
usage1() zend_mm_heap corrupted
Aborted

But I expected this output instead:

usage1() func1() a1=a1 a2=a2 a3=m3+
usage1() [function] func1() a1=a1 a2=m2+ a3=m3+

moreover, if I comment out the first call to usage1 with empty argument list, the second call succeeds

PHP Version

PHP 8.4.11 (cli) (built: Aug  3 2025 08:49:56) (NTS)
Copyright (c) The PHP Group
Built by Debian
Zend Engine v4.4.11, Copyright (c) Zend Technologies
    with Zend OPcache v8.4.11, Copyright (c), by Zend Technologies

Operating System

No response

Activity

  1. ndossche commented on Aug 31, 2025

    @ndossche
    Member

    Can repro, fails with opline->opcode == 63 in debug mode. Will make a look. Thanks for reporting.
    EDIT: also breaks on PHP 8.3.
    EDIT 2: it seems variadics are broken wrt the loop in zend_handle_undef_args() EDIT: (no actually that seems fine)
    EDIT 3: and cache slot priming in ZEND_SEND_VAL_EX assuming the argument number stays the same ...

  2. self-assigned this
    on Aug 31, 2025
  3. ivan-u7n commented on Aug 31, 2025

    @ivan-u7n
    Author

    @nielsdos

    EDIT 2: it seems variadics are broken wrt the loop in zend_handle_undef_args()

    yeah, I've got to heap corruption, but it all started with ridiculous ArgumentCountError: {closure:file.php:164}(): Argument #1 not passed in a larger codebase

  4. ndossche commented on Aug 31, 2025

    @ndossche
    Member

    The root cause is that zend_handle_named_arg stores the fbc and assumes its address is unique. However, that's not the case: for closures it's possible a memory address is reused leading to unexpected equality between functions that are not actually equal.

  5. changed the title [-]`zend_mm_heap corrupted` in closure named arguments unpacking?[/-] [+]Closure named argument unpacking between temporary closures can cause a crash[/+] on Aug 31, 2025
  6. added a commit that references this issue on Aug 31, 2025
    19ca448
  7. added 3 commits that reference this issue on Sep 14, 2025
    a3d27aa
    d6300a3
    2225295
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