Skip to content
Merged
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
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ PHP NEWS
filegroup(), fileatime(), filemtime(), filectime(), filetype(),
is_writable(), is_readable(), is_executable(), is_file(), is_dir(),
is_link(), file_exists(), lstat(), stat(). (Girgias)
. Fixed bug GH-22818 (stream_filter_register() orphaned user_filter_map on
shutdown re-registration). (David Carlier)

30 Jul 2026, PHP 8.6.0alpha3

Expand Down
5 changes: 3 additions & 2 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_count_error(uint32_t
}
/* }}} */

ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(int error_code, uint32_t num, char *name, zend_expected_type expected_type, const zval *arg) /* {{{ */
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(zpp_error error_code, uint32_t num, char *name, zend_expected_type expected_type, const zval *arg) /* {{{ */
{
switch (error_code) {
case ZPP_ERROR_WRONG_CALLBACK:
Expand Down Expand Up @@ -261,7 +261,8 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(int error_code,
case ZPP_ERROR_FAILURE:
ZEND_ASSERT(EG(exception) && "Should have produced an error already");
break;
default: ZEND_UNREACHABLE();
case ZPP_ERROR_OK:
ZEND_UNREACHABLE();
}
}
/* }}} */
Expand Down
33 changes: 17 additions & 16 deletions Zend/zend_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -1588,9 +1588,24 @@ typedef enum _zend_expected_type {
Z_EXPECTED_LAST
} zend_expected_type;

C23_ENUM(zpp_error, uint8_t) {
ZPP_ERROR_OK,
ZPP_ERROR_FAILURE,
ZPP_ERROR_WRONG_CALLBACK,
ZPP_ERROR_WRONG_CLASS,
ZPP_ERROR_WRONG_CLASS_OR_NULL,
ZPP_ERROR_WRONG_CLASS_OR_STRING,
ZPP_ERROR_WRONG_CLASS_OR_STRING_OR_NULL,
ZPP_ERROR_WRONG_CLASS_OR_LONG,
ZPP_ERROR_WRONG_CLASS_OR_LONG_OR_NULL,
ZPP_ERROR_WRONG_ARG,
ZPP_ERROR_UNEXPECTED_EXTRA_NAMED,
ZPP_ERROR_WRONG_CALLBACK_OR_NULL,
};

ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_none_error(void);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_count_error(uint32_t min_num_args, uint32_t max_num_args);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(int error_code, uint32_t num, char *name, zend_expected_type expected_type, const zval *arg);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(zpp_error error_code, uint32_t num, char *name, zend_expected_type expected_type, const zval *arg);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_type_error(uint32_t num, zend_expected_type expected_type, const zval *arg);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_error(uint32_t num, const char *name, const zval *arg);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_null_error(uint32_t num, const char *name, const zval *arg);
Expand All @@ -1612,20 +1627,6 @@ ZEND_API ZEND_COLD void zend_argument_must_not_be_empty_error(uint32_t arg_num);
ZEND_API ZEND_COLD void zend_class_redeclaration_error(int type, const zend_class_entry *old_ce);
ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string *new_name, const zend_class_entry *old_ce);

#define ZPP_ERROR_OK 0
#define ZPP_ERROR_FAILURE 1
#define ZPP_ERROR_WRONG_CALLBACK 2
#define ZPP_ERROR_WRONG_CLASS 3
#define ZPP_ERROR_WRONG_CLASS_OR_NULL 4
#define ZPP_ERROR_WRONG_CLASS_OR_STRING 5
#define ZPP_ERROR_WRONG_CLASS_OR_STRING_OR_NULL 6
#define ZPP_ERROR_WRONG_CLASS_OR_LONG 7
#define ZPP_ERROR_WRONG_CLASS_OR_LONG_OR_NULL 8
#define ZPP_ERROR_WRONG_ARG 9
#define ZPP_ERROR_WRONG_COUNT 10
#define ZPP_ERROR_UNEXPECTED_EXTRA_NAMED 11
#define ZPP_ERROR_WRONG_CALLBACK_OR_NULL 12

#define ZEND_PARSE_PARAMETERS_START_EX(flags, min_num_args, max_num_args) do { \
const int _flags = (flags); \
uint32_t _min_num_args = (min_num_args); \
Expand All @@ -1637,7 +1638,7 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string
char *_error = NULL; \
bool _dummy = 0; \
bool _optional = 0; \
int _error_code = ZPP_ERROR_OK; \
zpp_error _error_code = ZPP_ERROR_OK; \
((void)_i); \
((void)_real_arg); \
((void)_arg); \
Expand Down
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -1834,6 +1834,7 @@ AC_DEFINE([HAVE_BUILD_DEFS_H], [1],

PHP_ADD_BUILD_DIR([
main
main/io
main/poll
main/streams
scripts
Expand Down
28 changes: 28 additions & 0 deletions ext/standard/tests/gh22818.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--TEST--
Bug GH-22818: user_filter_factory_create assertion failure on shutdown re-registration
--FILE--
<?php

class rotate_filter_nw extends php_user_filter
{
public function filter($in, $out, &$consumed, $closing): int
{
$stream = fopen('php://memory', 'w+');
stream_filter_register("rotator_notWorking", rotate_filter_nw::class);
stream_filter_append($stream, "rotator_notWorking");

return PSFS_PASS_ON;
}
}

stream_filter_register("rotator_notWorking", rotate_filter_nw::class);

$stream = fopen('php://memory', 'w+');
stream_filter_append($stream, "rotator_notWorking");

echo "done\n";
?>
--EXPECTF--
done

Warning: stream_filter_append(): Unable to create or locate filter "rotator_notWorking" in %s on line %d
18 changes: 10 additions & 8 deletions ext/standard/user_filters.c
Original file line number Diff line number Diff line change
Expand Up @@ -601,20 +601,22 @@ PHP_FUNCTION(stream_filter_register)
RETURN_THROWS();
}

/* Register the factory first; if that fails, don't (re)create the map,
* which would leak during shutdown re-registration. */
if (php_stream_filter_register_factory_volatile(filtername, &user_filter_factory) == FAILURE) {
RETURN_FALSE;
}

if (!BG(user_filter_map)) {
BG(user_filter_map) = (HashTable*) emalloc(sizeof(HashTable));
/* We don't need a destructor as we are only storing a CE which should be never modified */
zend_hash_init(BG(user_filter_map), 8, NULL, NULL, 0);
}

if (zend_hash_add_ptr(BG(user_filter_map), filtername, ce) != NULL) {
if (php_stream_filter_register_factory_volatile(filtername, &user_filter_factory) == SUCCESS) {
RETURN_TRUE;
}

zend_hash_del(BG(user_filter_map), filtername);
}
/* The factory registration above already rejected a duplicate name, so the
* filter name cannot be present in the map either. */
zend_hash_add_new_ptr(BG(user_filter_map), filtername, ce);

RETURN_FALSE;
RETURN_TRUE;
}
/* }}} */
20 changes: 20 additions & 0 deletions ext/sysvshm/sysvshm.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ PHP_FUNCTION(shm_attach)
sysvshm_shm *shm_list_ptr;
char *shm_ptr;
sysvshm_chunk_head *chunk_ptr;
struct shmid_ds shm_desc;
zend_long shm_key_arg, shm_id, shm_size, shm_flag = 0666;
key_t shm_key;
bool shm_size_is_null = true;
Expand Down Expand Up @@ -171,6 +172,25 @@ PHP_FUNCTION(shm_attach)
RETURN_FALSE;
}

if (shmctl(shm_id, IPC_STAT, &shm_desc) < 0) {
php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", shm_key_arg, strerror(errno));
shmdt(shm_ptr);
if (created) {
shmctl(shm_id, IPC_RMID, NULL);
}
RETURN_FALSE;
}
shm_size = (zend_long)shm_desc.shm_segsz;

if (shm_size < (zend_long) sizeof(sysvshm_chunk_head)) {
php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": segment too small", shm_key_arg);
shmdt(shm_ptr);
if (created) {
shmctl(shm_id, IPC_RMID, NULL);
}
RETURN_FALSE;
}

/* check if shm is already initialized */
chunk_ptr = (sysvshm_chunk_head *) shm_ptr;
if (strcmp((char*) &(chunk_ptr->magic), "PHP_SM") != 0) {
Expand Down
36 changes: 36 additions & 0 deletions ext/sysvshm/tests/shm_attach_existing_segment_size.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
shm_attach() takes the size of an existing segment from the kernel, not from $size
--EXTENSIONS--
sysvshm
shmop
--FILE--
<?php
$key = 0x53484D31;

$raw = shmop_open($key, 'n', 0600, 4096);

$shm = shm_attach($key, 10 * 1024 * 1024);
var_dump($shm instanceof SysvSharedMemory);

var_dump(shm_put_var($shm, 1, str_repeat('A', 1024 * 1024)));

var_dump(shm_put_var($shm, 2, 'ok'));
var_dump(shm_get_var($shm, 2));

var_dump(shm_remove($shm));
?>
--EXPECTF--
bool(true)

Warning: shm_put_var(): Not enough shared memory left in %s on line %d
bool(false)
bool(true)
string(2) "ok"
bool(true)
--CLEAN--
<?php
$raw = @shmop_open(0x53484D31, 'w', 0, 0);
if ($raw) {
shmop_delete($raw);
}
?>
25 changes: 25 additions & 0 deletions ext/sysvshm/tests/shm_attach_existing_segment_too_small.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
shm_attach() rejects an existing segment too small to hold its header
--EXTENSIONS--
sysvshm
shmop
--FILE--
<?php
$key = 0x53484D32;

$raw = shmop_open($key, 'n', 0600, 8);

var_dump(shm_attach($key, 1024));

shmop_delete($raw);
?>
--EXPECTF--
Warning: shm_attach(): Failed for key 0x%x: segment too small in %s on line %d
bool(false)
--CLEAN--
<?php
$raw = @shmop_open(0x53484D32, 'w', 0, 0);
if ($raw) {
shmop_delete($raw);
}
?>