Skip to content

Closing a userspace stream inside a userspace handler causes heap corruption #14506

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
nielsdos opened this issue Jun 8, 2024 · 1 comment · May be fixed by #18797
Open

Closing a userspace stream inside a userspace handler causes heap corruption #14506

nielsdos opened this issue Jun 8, 2024 · 1 comment · May be fixed by #18797

Comments

@nielsdos
Copy link
Member

nielsdos commented Jun 8, 2024

Description

The following code:

<?php

class Bomb {

    public $context;

    function stream_open($path, $mode, $options, &$opened_path): bool
    {
        return true;
    }

    function stream_read(int $count): false|string|null
    {
        global $readStream;
        fclose($readStream);
        return "";
    }
}

stream_register_wrapper('bomb', Bomb::class);
$readStream = fopen('bomb://1', 'r');
fread($readStream, 1);

Resulted in this output:

==21643==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x7fda60e161dd bp 0x7ffda947edf0 sp 0x7ffda947e578 T0)
==21643==The signal is caused by a READ memory access.
==21643==Hint: this fault was caused by a dereference of a high value address (see register values below).  Disassemble the provided pc to learn which register was used.
    #0 0x7fda60e161dd  (/usr/lib/libc.so.6+0x16a1dd) (BuildId: 32a656aa5562eece8c59a585f5eacd6cf5e2307b)
    #1 0x7fda618766bc in strlen /usr/src/debug/gcc/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:389
    #2 0x5d49d745d0f0 in xbuf_format_converter /run/media/niels/MoreData/php-src/main/spprintf.c:555
    #3 0x5d49d745edd9 in php_printf_to_smart_str /run/media/niels/MoreData/php-src/main/spprintf.c:784
    #4 0x5d49d75cd6ab in zend_vstrpprintf /run/media/niels/MoreData/php-src/Zend/zend.c:330
    #5 0x5d49d7449f5c in php_verror /run/media/niels/MoreData/php-src/main/main.c:965
    #6 0x5d49d744ae7e in php_error_docref /run/media/niels/MoreData/php-src/main/main.c:1133
    #7 0x5d49d74b6c10 in php_userstreamop_read /run/media/niels/MoreData/php-src/main/streams/userspace.c:678
    #8 0x5d49d7497985 in _php_stream_fill_read_buffer /run/media/niels/MoreData/php-src/main/streams/streams.c:684
    #9 0x5d49d74980ff in _php_stream_read /run/media/niels/MoreData/php-src/main/streams/streams.c:747
    #10 0x5d49d7498411 in php_stream_read_to_str /run/media/niels/MoreData/php-src/main/streams/streams.c:795
    #11 0x5d49d7282eeb in zif_fread /run/media/niels/MoreData/php-src/ext/standard/file.c:1636
    #12 0x5d49d767e8ae in ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER /run/media/niels/MoreData/php-src/Zend/zend_vm_execute.h:1287
    #13 0x5d49d77d9a6d in execute_ex /run/media/niels/MoreData/php-src/Zend/zend_vm_execute.h:57364
    #14 0x5d49d77ed958 in zend_execute /run/media/niels/MoreData/php-src/Zend/zend_vm_execute.h:62776
    #15 0x5d49d75d6252 in zend_execute_script /run/media/niels/MoreData/php-src/Zend/zend.c:1899
    #16 0x5d49d7450e8e in php_execute_script_ex /run/media/niels/MoreData/php-src/main/main.c:2512
    #17 0x5d49d7451296 in php_execute_script /run/media/niels/MoreData/php-src/main/main.c:2552
    #18 0x5d49d79d2f9d in do_cli /run/media/niels/MoreData/php-src/sapi/cli/php_cli.c:966
    #19 0x5d49d79d4d6a in main /run/media/niels/MoreData/php-src/sapi/cli/php_cli.c:1340
    #20 0x7fda60cd1c87  (/usr/lib/libc.so.6+0x25c87) (BuildId: 32a656aa5562eece8c59a585f5eacd6cf5e2307b)
    #21 0x7fda60cd1d4b in __libc_start_main (/usr/lib/libc.so.6+0x25d4b) (BuildId: 32a656aa5562eece8c59a585f5eacd6cf5e2307b)
    #22 0x5d49d6a058c4 in _start (/run/media/niels/MoreData/php-src/sapi/cli/php+0x6058c4) (BuildId: 6fe72432c731063b9117e84b7b9c658178936dc0)

But I expected this output instead:

No crash

PHP Version

8.2+

Operating System

Linux

@lucasnetau
Copy link
Contributor

To add to the reproducer, this only occurs if stream_eof() function is not defined in the stream wrapper class.

nielsdos added a commit to nielsdos/php-src that referenced this issue Jun 7, 2025
…r causes heap corruption

Use the PHP_STREAM_FLAG_NO_FCLOSE flag to prevent closing a stream while
a handler is running. We already do this in some other places as well.
Only handlers that do something with the stream afterwards need changes.
nielsdos added a commit to nielsdos/php-src that referenced this issue Jun 8, 2025
…r causes heap corruption

Use the PHP_STREAM_FLAG_NO_FCLOSE flag to prevent closing a stream while
a handler is running. We already do this in some other places as well.
Only handlers that do something with the stream afterwards need changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants