Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions Zend/zend.c
Original file line number Diff line number Diff line change
Expand Up @@ -1366,9 +1366,6 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
EG(num_errors)++;
EG(errors) = erealloc(EG(errors), sizeof(zend_error_info) * EG(num_errors));
EG(errors)[EG(num_errors)-1] = info;
if (EG(record_errors_without_emitting)) {
return;
}
}

/* Report about uncaught exception in case of fatal errors */
Expand Down Expand Up @@ -1622,25 +1619,14 @@ ZEND_API ZEND_COLD void zend_error_zstr(int type, zend_string *message) {
zend_error_zstr_at(type, filename, lineno, message);
}

static zend_always_inline void zend_begin_record_errors_ex(bool no_emmitting)
ZEND_API void zend_begin_record_errors(void)
{
ZEND_ASSERT(!EG(record_errors) && "Error recording already enabled");
EG(record_errors) = true;
EG(record_errors_without_emitting) = no_emmitting;
EG(num_errors) = 0;
EG(errors) = NULL;
}

ZEND_API void zend_begin_record_errors(void)
{
zend_begin_record_errors_ex(false);
}

ZEND_API void zend_begin_record_errors_without_emitting(void)
{
zend_begin_record_errors_ex(true);
}

ZEND_API void zend_emit_recorded_errors(void)
{
EG(record_errors) = false;
Expand Down
1 change: 0 additions & 1 deletion Zend/zend.h
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,6 @@ ZEND_API void zend_save_error_handling(zend_error_handling *current);
ZEND_API void zend_replace_error_handling(zend_error_handling_t error_handling, zend_class_entry *exception_class, zend_error_handling *current);
ZEND_API void zend_restore_error_handling(zend_error_handling *saved);
ZEND_API void zend_begin_record_errors(void);
ZEND_API void zend_begin_record_errors_without_emitting(void);
ZEND_API void zend_emit_recorded_errors(void);
ZEND_API void zend_free_recorded_errors(void);
END_EXTERN_C()
Expand Down
4 changes: 1 addition & 3 deletions Zend/zend_globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,8 @@ struct _zend_executor_globals {
zend_long fiber_stack_size;

/* If record_errors is enabled, all emitted diagnostics will be recorded,
* in addition to being processed as usual unless record_errors_without_emitting
* is enabled which supresses processing when the errors are recorded. */
* in addition to being processed as usual. */
bool record_errors;
bool record_errors_without_emitting;
uint32_t num_errors;
zend_error_info **errors;

Expand Down
19 changes: 12 additions & 7 deletions ext/standard/tests/streams/gh8409.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,24 @@
GH-8409: Error in socket creation when error handler does not clean persistent connection
--FILE--
<?php
set_error_handler(function($errno, $errstring, $errfile, $errline) {
set_error_handler(function (int $errno, string $errstring): never {
trigger_error($errstring, E_USER_ERROR);
});

register_shutdown_function(function (): void {
foreach (get_resources() as $res) {
if (get_resource_type($res) === "persistent stream") {
if (get_resource_type($res) === 'persistent stream') {
echo "ERROR: persistent stream not closed\n";
return;
}
}
echo "DONE\n";
exit(1);
echo "OK: persistent stream closed\n";
});

stream_socket_client("tcp://9999.9999.9999.9999:9999", $error_code, $error_message, 0.2, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT);
stream_socket_client('tcp://9999.9999.9999.9999:9999', $error_code, $error_message, 0.2, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT);

echo "ERROR: this should not be visible\n";
?>
--EXPECT--
DONE
--EXPECTF--
Fatal error: stream_socket_client(): %s in %sgh8409.php on line %d
OK: persistent stream closed
70 changes: 37 additions & 33 deletions main/streams/transports.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in
php_stream_transport_factory factory = NULL;
const char *p, *protocol = NULL;
size_t n = 0;
int failed = 0;
bool failed = false;
bool bailout = false;
zend_string *error_text = NULL;
struct timeval default_timeout = { 0, 0 };

Expand Down Expand Up @@ -131,60 +132,63 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in
(char*)name, namelen, persistent_id, options, flags, timeout,
context STREAMS_REL_CC);

zend_begin_record_errors_without_emitting();

if (stream) {
php_stream_context_set(stream, context);
zend_try {
php_stream_context_set(stream, context);

if ((flags & STREAM_XPORT_SERVER) == 0) {
/* client */
if ((flags & STREAM_XPORT_SERVER) == 0) {
/* client */

if (flags & (STREAM_XPORT_CONNECT|STREAM_XPORT_CONNECT_ASYNC)) {
if (-1 == php_stream_xport_connect(stream, name, namelen,
flags & STREAM_XPORT_CONNECT_ASYNC ? 1 : 0,
timeout, &error_text, error_code)) {
if (flags & (STREAM_XPORT_CONNECT|STREAM_XPORT_CONNECT_ASYNC)) {
if (-1 == php_stream_xport_connect(stream, name, namelen,
flags & STREAM_XPORT_CONNECT_ASYNC ? 1 : 0,
timeout, &error_text, error_code)) {

ERR_RETURN(error_string, error_text, "connect() failed: %s");
ERR_RETURN(error_string, error_text, "connect() failed: %s");

failed = 1;
}
}

} else {
/* server */
if (flags & STREAM_XPORT_BIND) {
if (0 != php_stream_xport_bind(stream, name, namelen, &error_text)) {
ERR_RETURN(error_string, error_text, "bind() failed: %s");
failed = 1;
} else if (flags & STREAM_XPORT_LISTEN) {
zval *zbacklog = NULL;
int backlog = 32;

if (PHP_STREAM_CONTEXT(stream) && (zbacklog = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "backlog")) != NULL) {
backlog = zval_get_long(zbacklog);
failed = 1;
}
}

if (0 != php_stream_xport_listen(stream, backlog, &error_text)) {
ERR_RETURN(error_string, error_text, "listen() failed: %s");
} else {
/* server */
if (flags & STREAM_XPORT_BIND) {
if (0 != php_stream_xport_bind(stream, name, namelen, &error_text)) {
ERR_RETURN(error_string, error_text, "bind() failed: %s");
failed = 1;
} else if (flags & STREAM_XPORT_LISTEN) {
zval *zbacklog = NULL;
int backlog = 32;

if (PHP_STREAM_CONTEXT(stream) && (zbacklog = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "backlog")) != NULL) {
backlog = zval_get_long(zbacklog);
}

if (0 != php_stream_xport_listen(stream, backlog, &error_text)) {
ERR_RETURN(error_string, error_text, "listen() failed: %s");
failed = 1;
}
}
}
}
}
} zend_catch {
bailout = 1;
} zend_end_try();
}

if (failed) {
if (failed || bailout) {
/* failure means that they don't get a stream to play with */
if (persistent_id) {
php_stream_pclose(stream);
} else {
php_stream_close(stream);
}
stream = NULL;
if (bailout) {
zend_bailout();
}
}

zend_emit_recorded_errors();
zend_free_recorded_errors();

return stream;
}
Expand Down