Projects
home:rottame:vhosts-ng:php73
php7
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 12
View file
php7.spec
Changed
@@ -49,6 +49,13 @@ License: PHP-3.01 Group: Development/Languages/Other URL: https://secure.php.net +# Source0 is NOT the php.net tarball: it is a git archive of +# repo: https://github.com/remicollet/php-src-security +# branch: PHP-7.3-security-backports +# commit: f9e099c02fc0d8c37bd538eeaf48f4e0f3671d91 (2026-08-07) +# with the generated files missing from git copied from the original +# php.net php-7.3.33.tar.xz: aclocal.m4, Zend/zend_{language,ini}_parser.{c,h,output}, +# generated_lists, install-sh, missing, mkinstalldirs, pear/install-pear-nozlib.phar Source0: https://secure.php.net/distributions/php-%{version}.tar.xz Source1: php-suse-addons.tar.bz2 Source5: README.macros
View file
php7-CVE-2026-109618,109619.patch
Added
@@ -0,0 +1,1674 @@ +diff -ruN php-7.3.33-src/Zend/zend_API.h php-7.3.33-enh-src/Zend/zend_API.h +--- php-7.3.33/Zend/zend_API.h 2021-11-16 14:18:41.000000000 +0300 ++++ php-7.3.33/Zend/zend_API.h 2026-10-22 02:04:53.231578113 +0300 +@@ -376,6 +376,7 @@ + + #define array_init(arg) ZVAL_ARR((arg), zend_new_array(0)) + #define array_init_size(arg, size) ZVAL_ARR((arg), zend_new_array(size)) ++#define array_init_singleton(arg) ZVAL_ARR((arg), zend_new_array_singleton()) + ZEND_API int object_init(zval *arg); + ZEND_API int object_init_ex(zval *arg, zend_class_entry *ce); + ZEND_API int object_and_properties_init(zval *arg, zend_class_entry *ce, HashTable *properties); +diff -ruN php-7.3.33-src/Zend/zend_alloc.c php-7.3.33-enh-src/Zend/zend_alloc.c +--- php-7.3.33/Zend/zend_alloc.c 2021-11-16 14:18:41.000000000 +0300 ++++ php-7.3.33/Zend/zend_alloc.c 2026-10-22 02:04:53.231578113 +0300 +@@ -57,6 +57,7 @@ + #include "zend_operators.h" + #include "zend_multiply.h" + #include "zend_bitset.h" ++#include "zend_execute.h" + + #ifdef HAVE_SIGNAL_H + # include <signal.h> +@@ -358,7 +359,42 @@ + #if ZEND_DEBUG && defined(HAVE_KILL) && defined(HAVE_GETPID) + kill(getpid(), SIGSEGV); + #endif +- exit(1); ++ _exit(1); ++} ++ ++ZEND_API ZEND_COLD ZEND_NORETURN void zend_mm_safe_error_ex(const char *format, ...) ++{ ++ zend_string *script_filename = NULL; ++ uint32_t script_lineno = 0; ++ ++ if (zend_is_executing()) { ++ script_filename = zend_get_executed_filename_ex(); ++ script_lineno = zend_get_executed_lineno(); ++ } else if (zend_is_compiling()) { ++ script_filename = zend_get_compiled_filename(); ++ script_lineno = zend_get_compiled_lineno(); ++ } ++ ++ va_list args; ++ va_start(args, format); ++ ++ char buf1024; ++ vsnprintf(buf, sizeof(buf), format, args); ++ ++ va_end(args); ++ ++ fprintf(stderr, ++ "\nFatal error: %s in %s on line %d\n", ++ buf, ++ script_filename ? ZSTR_VAL(script_filename) : "Unknown", ++ script_lineno); ++ ++ /* See http://support.microsoft.com/kb/190351 */ ++# ifdef ZEND_WIN32 ++ fflush(stderr); ++# endif ++ ++ _exit(1); + } + + static ZEND_COLD ZEND_NORETURN void zend_mm_safe_error(zend_mm_heap *heap, +@@ -370,22 +406,14 @@ + #endif + size_t size) + { ++ (void) heap; + +- heap->overflow = 1; +- zend_try { +- zend_error_noreturn(E_ERROR, +- format, +- limit, +-#if ZEND_DEBUG +- filename, +- lineno, +-#endif +- size); +- } zend_catch { +- } zend_end_try(); +- heap->overflow = 0; +- zend_bailout(); +- exit(1); ++ zend_mm_safe_error_ex(format, limit, ++#if ZEND_DEBUG ++ filename, ++ lineno, ++#endif ++ size); + } + + #ifdef _WIN32 +@@ -1352,7 +1380,7 @@ + size = MAX(size, 1); + size = ZEND_MM_ALIGNED_SIZE(size) + ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info)); + if (UNEXPECTED(size < real_size)) { +- zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu + %zu)", ZEND_MM_ALIGNED_SIZE(real_size), ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info))); ++ zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%zu + %zu)", ZEND_MM_ALIGNED_SIZE(real_size), ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info))); + return NULL; + } + #endif +@@ -1800,7 +1828,7 @@ + void *ptr; + + if (UNEXPECTED(new_size < size)) { +- zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu + %zu)", size, alignment); ++ zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%zu + %zu)", size, alignment); + } + + #if ZEND_MM_LIMIT +@@ -2596,7 +2624,7 @@ + + length = strlen(s); + if (UNEXPECTED(length + 1 == 0)) { +- zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (1 * %zu + 1)", length); ++ zend_mm_safe_error_ex("Possible integer overflow in memory allocation (1 * %zu + 1)", length); + } + p = (char *) _emalloc(length + 1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + memcpy(p, s, length+1); +@@ -2608,7 +2636,7 @@ + char *p; + + if (UNEXPECTED(length + 1 == 0)) { +- zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (1 * %zu + 1)", length); ++ zend_mm_safe_error_ex("Possible integer overflow in memory allocation (1 * %zu + 1)", length); + } + p = (char *) _emalloc(length + 1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + memcpy(p, s, length); +@@ -2622,7 +2650,7 @@ + char *p; + + if (UNEXPECTED(length + 1 == 0)) { +- zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (1 * %zu + 1)", length); ++ zend_mm_safe_error_ex("Possible integer overflow in memory allocation (1 * %zu + 1)", length); + } + p = (char *) malloc(length + 1); + if (UNEXPECTED(p == NULL)) { +@@ -2901,7 +2929,7 @@ + static ZEND_COLD ZEND_NORETURN void zend_out_of_memory(void) + { + fprintf(stderr, "Out of memory\n"); +- exit(1); ++ _exit(1); + } + + ZEND_API void * __zend_malloc(size_t len) +diff -ruN php-7.3.33-src/Zend/zend_alloc.h php-7.3.33-enh-src/Zend/zend_alloc.h +--- php-7.3.33/Zend/zend_alloc.h 2021-11-16 14:18:41.000000000 +0300 ++++ php-7.3.33/Zend/zend_alloc.h 2026-10-22 02:04:53.231578113 +0300 +@@ -397,6 +397,8 @@ + + */ + ++ZEND_API ZEND_COLD ZEND_NORETURN void zend_mm_safe_error_ex(const char *format, ...); ++ + END_EXTERN_C() + + #endif +diff -ruN php-7.3.33-src/Zend/zend_execute_API.c php-7.3.33-enh-src/Zend/zend_execute_API.c +--- php-7.3.33/Zend/zend_execute_API.c 2021-11-16 14:18:41.000000000 +0300 ++++ php-7.3.33/Zend/zend_execute_API.c 2026-10-22 02:04:53.231578113 +0300 +@@ -267,6 +267,10 @@ + + zend_objects_store_free_object_storage(&EG(objects_store), fast_shutdown); + ++ /* may be used by zend_error_zstr_at before init_executor */ ++ EG(error_handling) = EH_NORMAL; ++ EG(exception) = NULL; ++ + /* All resources and objects are destroyed. */ + /* No PHP callback functions may be called after this point. */ + EG(active) = 0; +@@ -285,6 +289,8 @@ + zend_hash_discard(EG(function_table), EG(persistent_functions_count)); + zend_hash_discard(EG(class_table), EG(persistent_classes_count)); + zend_cleanup_internal_classes(); ++ EG(objects_store).object_buckets = NULL; ++ EG(objects_store).top = 1; + } else { + zend_hash_graceful_reverse_destroy(&EG(symbol_table)); + +diff -ruN php-7.3.33-src/Zend/zend_hash.c php-7.3.33-enh-src/Zend/zend_hash.c +--- php-7.3.33/Zend/zend_hash.c 2021-11-16 14:18:41.000000000 +0300 ++++ php-7.3.33/Zend/zend_hash.c 2026-10-22 02:04:53.231578113 +0300 +@@ -93,7 +93,7 @@ + if (nSize <= HT_MIN_SIZE) { + return HT_MIN_SIZE; + } else if (UNEXPECTED(nSize >= HT_MAX_SIZE)) { +- zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%u * %zu + %zu)", nSize, sizeof(Bucket), sizeof(Bucket)); ++ zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%u * %zu + %zu)", nSize, sizeof(Bucket), sizeof(Bucket)); + } + + #if defined(ZEND_WIN32) +@@ -219,6 +219,13 @@ + return ht; + } + ++ZEND_API HashTable* ZEND_FASTCALL zend_new_array_singleton(void) ++{ ++ HashTable *ht = _zend_new_array_0(); ++ ht->nTableSize = 1; ++ return ht; ++} ++ + ZEND_API HashTable* ZEND_FASTCALL _zend_new_array(uint32_t nSize) + { + HashTable *ht = emalloc(sizeof(HashTable)); +@@ -230,7 +237,7 @@ + { + HT_ASSERT_RC1(ht); + if (ht->nTableSize >= HT_MAX_SIZE) { +- zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%u * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket), sizeof(Bucket)); ++ zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%u * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket), sizeof(Bucket)); + } + ht->nTableSize += ht->nTableSize; + HT_SET_DATA_ADDR(ht, perealloc2(HT_GET_DATA_ADDR(ht), HT_SIZE_EX(ht->nTableSize, HT_MIN_MASK), HT_USED_SIZE(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)); +@@ -1041,7 +1048,7 @@ + pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT); + zend_hash_rehash(ht); + } else { +- zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%u * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket) + sizeof(uint32_t), sizeof(Bucket)); ++ zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%u * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket) + sizeof(uint32_t), sizeof(Bucket)); + } + } + +diff -ruN php-7.3.33-src/Zend/zend_hash.h php-7.3.33-enh-src/Zend/zend_hash.h +--- php-7.3.33/Zend/zend_hash.h 2021-11-16 14:18:41.000000000 +0300 ++++ php-7.3.33/Zend/zend_hash.h 2026-10-22 02:04:53.231578113 +0300 +@@ -276,6 +276,7 @@ + #endif + + ZEND_API HashTable* ZEND_FASTCALL _zend_new_array_0(void); ++ZEND_API HashTable* ZEND_FASTCALL zend_new_array_singleton(void); + ZEND_API HashTable* ZEND_FASTCALL _zend_new_array(uint32_t size); + ZEND_API uint32_t zend_array_count(HashTable *ht); + ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source); +diff -ruN php-7.3.33-src/Zend/zend_llist.c php-7.3.33-enh-src/Zend/zend_llist.c +--- php-7.3.33/Zend/zend_llist.c 2021-11-16 14:18:41.000000000 +0300 ++++ php-7.3.33/Zend/zend_llist.c 2026-10-22 02:04:53.231578113 +0300 +@@ -112,6 +112,8 @@ + current = next; + } + ++ l->head = NULL; ++ l->tail = NULL; + l->count = 0; + } + +diff -ruN php-7.3.33-src/Zend/zend_multiply.h php-7.3.33-enh-src/Zend/zend_multiply.h +--- php-7.3.33/Zend/zend_multiply.h 2021-11-16 14:18:41.000000000 +0300 ++++ php-7.3.33/Zend/zend_multiply.h 2026-10-22 02:04:53.231578113 +0300 +@@ -321,7 +321,7 @@ + size_t ret = zend_safe_address(nmemb, size, offset, &overflow); + + if (UNEXPECTED(overflow)) { +- zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset); ++ zend_mm_safe_error_ex("Possible integer overflow in memory allocation (%zu * %zu + %zu)", nmemb, size, offset); + return 0; + } + return ret; +@@ -334,7 +334,7 @@ + size_t ret = zend_safe_address(nmemb, size, offset, &overflow); + + if (UNEXPECTED(overflow)) { +- zend_error_noreturn(E_ERROR, "Possible integer overflow in %s (%zu * %zu + %zu)", message, nmemb, size, offset); ++ zend_mm_safe_error_ex("Possible integer overflow in %s (%zu * %zu + %zu)", message, nmemb, size, offset); + return 0; + } + return ret; +diff -ruN php-7.3.33-src/Zend/zend_strtod.c php-7.3.33-enh-src/Zend/zend_strtod.c +--- php-7.3.33/Zend/zend_strtod.c 2021-11-16 14:18:41.000000000 +0300 ++++ php-7.3.33/Zend/zend_strtod.c 2026-10-22 02:04:53.231578113 +0300 +@@ -600,7 +600,7 @@ + rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); + if (!rv) { + FREE_DTOA_LOCK(0); +- zend_error_noreturn(E_ERROR, "Balloc() failed to allocate memory"); ++ zend_mm_safe_error_ex("Balloc() failed to allocate memory"); + } + #else + len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) +@@ -613,7 +613,7 @@ + rv = (Bigint*)MALLOC(len*sizeof(double)); + if (!rv) { + FREE_DTOA_LOCK(0); +- zend_error_noreturn(E_ERROR, "Balloc() failed to allocate memory"); ++ zend_mm_safe_error_ex("Balloc() failed to allocate memory"); + } + #endif + rv->k = k; +diff -ruN php-7.3.33-src/Zend/zend_types.h php-7.3.33-enh-src/Zend/zend_types.h +--- php-7.3.33/Zend/zend_types.h 2021-11-16 14:18:41.000000000 +0300 ++++ php-7.3.33/Zend/zend_types.h 2026-10-22 02:04:53.231578113 +0300 +@@ -317,17 +317,21 @@ + # define HT_HASH_RESET(ht) do { \ + char *p = (char*)&HT_HASH(ht, (ht)->nTableMask); \ + size_t size = HT_HASH_SIZE((ht)->nTableMask); \ +- __m128i xmm0 = _mm_setzero_si128(); \ +- xmm0 = _mm_cmpeq_epi8(xmm0, xmm0); \ +- ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); \ +- do { \ +- _mm_storeu_si128((__m128i*)p, xmm0); \ +- _mm_storeu_si128((__m128i*)(p+16), xmm0); \ +- _mm_storeu_si128((__m128i*)(p+32), xmm0); \ +- _mm_storeu_si128((__m128i*)(p+48), xmm0); \ +- p += 64; \ +- size -= 64; \ +- } while (size != 0); \ ++ if (UNEXPECTED(size < 64)) { \ ++ memset(p, HT_INVALID_IDX, size); \ ++ } else { \ ++ __m128i xmm0 = _mm_setzero_si128(); \ ++ xmm0 = _mm_cmpeq_epi8(xmm0, xmm0); \ ++ ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); \ ++ do { \ ++ _mm_storeu_si128((__m128i*)p, xmm0); \ ++ _mm_storeu_si128((__m128i*)(p+16), xmm0); \ ++ _mm_storeu_si128((__m128i*)(p+32), xmm0); \ ++ _mm_storeu_si128((__m128i*)(p+48), xmm0); \ ++ p += 64; \ ++ size -= 64; \ ++ } while (size != 0); \ ++ } \ + } while (0) + #else + # define HT_HASH_RESET(ht) \ +diff -ruN php-7.3.33-src/ext/session/php_session.h php-7.3.33-enh-src/ext/session/php_session.h +--- php-7.3.33/ext/session/php_session.h 2021-11-16 14:18:32.000000000 +0300 ++++ php-7.3.33/ext/session/php_session.h 2026-10-22 02:04:53.235578054 +0300 +@@ -138,10 +138,10 @@ + size_t content_length; + + zval data; /* the array exported to session data */ +- zval *post_bytes_processed; /* data"bytes_processed" */ ++ zend_long post_bytes_processed; + zval files; /* data"files" array */ + zval current_file; /* array of currently uploading file */ +- zval *current_file_bytes_processed; ++ zend_long current_file_bytes_processed; + } php_session_rfc1867_progress; + + typedef struct _php_ps_globals { +@@ -206,6 +206,8 @@ + zend_bool in_save_handler; /* state if session is in save handler or not */ + zend_bool set_handler; /* state if session module i setting handler or not */ + zend_string *session_vars; /* serialized original session data */ ++ zend_bool in_rfc1867_update; ++ zend_bool rfc1867_decode_refused; + } php_ps_globals; + + typedef php_ps_globals zend_ps_globals; +diff -ruN php-7.3.33-src/ext/session/session.c php-7.3.33-enh-src/ext/session/session.c +--- php-7.3.33/ext/session/session.c 2021-11-16 14:18:32.000000000 +0300 ++++ php-7.3.33/ext/session/session.c 2026-10-22 03:02:32.239891940 +0300 +@@ -117,6 +117,7 @@ + PS(set_handler) = 0; + PS(mod_data) = NULL; + PS(mod_user_is_open) = 0; ++ PS(in_rfc1867_update) = 0; /* php2shell: never leak the upload-progress window across requests */ + PS(define_sid) = 1; + PS(session_vars) = NULL; + PS(module_number) = my_module_number; +@@ -249,6 +250,7 @@ + return FAILURE; + } + if (PS(serializer)->decode(ZSTR_VAL(data), ZSTR_LEN(data)) == FAILURE) { ++ PS(rfc1867_decode_refused) = 1; /* php2shell: must survive the destroy->rinit cycle below */ + php_session_destroy(); + php_session_track_init(); + php_error_docref(NULL, E_WARNING, "Failed to decode session object. Session has been destroyed"); +@@ -835,13 +837,35 @@ + /* *************** + * Serializers * + *************** */ ++ ++/* php2shell (official PHP 8.2.35 fix, CVE-2026-109619): the upload progress ++ * handler (php_session_rfc1867_update()) decodes and re-encodes the session ++ * during request startup, before user code may run. Objects are then kept as ++ * __PHP_Incomplete_Class: no autoloading, no unserialize_callback_func, no ++ * __wakeup(), and they are written back unchanged. */ ++static void php_session_unserialize_init(php_unserialize_data_t *var_hash) ++{ ++ PHP_VAR_UNSERIALIZE_INIT(*var_hash); ++ if (PS(in_rfc1867_update)) { ++ php_var_unserialize_set_incomplete_classes_only(*var_hash, 1); ++ } ++} ++ ++static void php_session_serialize_init(php_serialize_data_t *var_hash) ++{ ++ PHP_VAR_SERIALIZE_INIT(*var_hash); ++ if (PS(in_rfc1867_update)) { ++ php_var_serialize_set_incomplete_classes_only(*var_hash, 1); ++ } ++} ++ + PS_SERIALIZER_ENCODE_FUNC(php_serialize) /* {{{ */ + { + smart_str buf = {0}; + php_serialize_data_t var_hash; + + IF_SESSION_VARS() { +- PHP_VAR_SERIALIZE_INIT(var_hash); ++ php_session_serialize_init(&var_hash); + php_var_serialize(&buf, Z_REFVAL(PS(http_session_vars)), &var_hash); + PHP_VAR_SERIALIZE_DESTROY(var_hash); + } +@@ -858,7 +882,7 @@ + zend_string *var_name = zend_string_init("_SESSION", sizeof("_SESSION") - 1, 0); + + ZVAL_NULL(&session_vars); +- PHP_VAR_UNSERIALIZE_INIT(var_hash); ++ php_session_unserialize_init(&var_hash); + result = php_var_unserialize( + &session_vars, (const unsigned char **)&val, (const unsigned char *)endptr, &var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); +@@ -891,7 +915,7 @@ + php_serialize_data_t var_hash; + PS_ENCODE_VARS; + +- PHP_VAR_SERIALIZE_INIT(var_hash); ++ php_session_serialize_init(&var_hash); + + PS_ENCODE_LOOP( + if (ZSTR_LEN(key) > PS_BIN_MAX) continue; +@@ -916,7 +940,7 @@ + php_unserialize_data_t var_hash; + zval *current, rv; + +- PHP_VAR_UNSERIALIZE_INIT(var_hash); ++ php_session_unserialize_init(&var_hash); + + for (p = val; p < endptr; ) { + namelen = ((unsigned char)(*p)) & (~PS_BIN_UNDEF); +@@ -957,7 +981,7 @@ + php_serialize_data_t var_hash; + PS_ENCODE_VARS; + +- PHP_VAR_SERIALIZE_INIT(var_hash); ++ php_session_serialize_init(&var_hash); + + PS_ENCODE_LOOP( + smart_str_appendl(&buf, ZSTR_VAL(key), ZSTR_LEN(key)); +@@ -987,7 +1011,7 @@ + php_unserialize_data_t var_hash; + zval *current, rv; + +- PHP_VAR_UNSERIALIZE_INIT(var_hash); ++ php_session_unserialize_init(&var_hash); + + p = val; + +@@ -2916,6 +2940,8 @@ + ps_globals->mod = NULL; + ps_globals->serializer = NULL; + ps_globals->mod_data = NULL; ++ ps_globals->in_rfc1867_update = 0; ++ ps_globals->rfc1867_decode_refused = 0; + ps_globals->session_status = php_session_none; + ps_globals->default_mod = NULL; + ps_globals->mod_user_implemented = 0; +@@ -3106,10 +3132,41 @@ + return Z_TYPE_P(cancel_upload) == IS_TRUE; + } /* }}} */ + ++ ++static void php_session_rfc1867_sync_counters(php_session_rfc1867_progress *progress) /* {{{ */ ++{ ++ zval *entry, *files, *file; ++ ++ if (Z_ISUNDEF(progress->data) || Z_TYPE(progress->data) != IS_ARRAY) { ++ return; ++ } ++ ++ /* php2shell: never retain HashTable bucket pointers — publishing progress ++ * data may separate/replace the exported array (eness root-cause fix). ++ * Resolve each counter afresh from the live array. */ ++ if ((entry = zend_hash_str_find(Z_ARRVAL(progress->data), "bytes_processed", sizeof("bytes_processed") - 1)) != NULL) { ++ ZVAL_LONG(entry, progress->post_bytes_processed); ++ } ++ if ((files = zend_hash_str_find(Z_ARRVAL(progress->data), "files", sizeof("files") - 1)) == NULL ++ || Z_TYPE_P(files) != IS_ARRAY) { ++ return; ++ } ++ if (zend_hash_num_elements(Z_ARRVAL_P(files)) == 0 ++ || (file = zend_hash_index_find(Z_ARRVAL_P(files), zend_hash_num_elements(Z_ARRVAL_P(files)) - 1)) == NULL ++ || Z_TYPE_P(file) != IS_ARRAY) { ++ return; ++ } ++ if ((entry = zend_hash_str_find(Z_ARRVAL_P(file), "bytes_processed", sizeof("bytes_processed") - 1)) != NULL) { ++ ZVAL_LONG(entry, progress->current_file_bytes_processed); ++ } ++} /* }}} */ ++ + static void php_session_rfc1867_update(php_session_rfc1867_progress *progress, int force_update) /* {{{ */ + { ++ php_session_rfc1867_sync_counters(progress); ++ + if (!force_update) { +- if (Z_LVAL_P(progress->post_bytes_processed) < progress->next_update) { ++ if (progress->post_bytes_processed < progress->next_update) { + return; + } + #ifdef HAVE_GETTIMEOFDAY +@@ -3124,11 +3181,24 @@ + progress->next_update_time = dtv + PS(rfc1867_min_freq); + } + #endif +- progress->next_update = Z_LVAL_P(progress->post_bytes_processed) + progress->update_step; ++ progress->next_update = progress->post_bytes_processed + progress->update_step; + } + +- php_session_initialize(); +- PS(session_status) = php_session_active; ++ PS(in_rfc1867_update) = 1; ++ PS(rfc1867_decode_refused) = 0; ++ if (php_session_initialize() == FAILURE) { ++ /* A refused decode stops the handler — the destroyed session record ++ * must stay gone. Plain storage failures (e.g. an invalid sid) ++ * keep the stock behavior: force the session active and continue. */ ++ if (PS(rfc1867_decode_refused)) { ++ PS(in_rfc1867_update) = 0; ++ return; ++ } ++ PS(session_status) = php_session_active; ++ } else if (PS(session_status) != php_session_active) { ++ PS(in_rfc1867_update) = 0; ++ return; ++ } + IF_SESSION_VARS() { + zval *sess_var = Z_REFVAL(PS(http_session_vars)); + SEPARATE_ARRAY(sess_var); +@@ -3138,18 +3208,30 @@ + zend_hash_update(Z_ARRVAL_P(sess_var), progress->key.s, &progress->data); + } + php_session_flush(1); ++ PS(in_rfc1867_update) = 0; + } /* }}} */ + + static void php_session_rfc1867_cleanup(php_session_rfc1867_progress *progress) /* {{{ */ + { +- php_session_initialize(); +- PS(session_status) = php_session_active; ++ PS(in_rfc1867_update) = 1; ++ PS(rfc1867_decode_refused) = 0; ++ if (php_session_initialize() == FAILURE) { ++ if (PS(rfc1867_decode_refused)) { ++ PS(in_rfc1867_update) = 0; ++ return; ++ } ++ PS(session_status) = php_session_active; ++ } else if (PS(session_status) != php_session_active) { ++ PS(in_rfc1867_update) = 0; ++ return; ++ } + IF_SESSION_VARS() { + zval *sess_var = Z_REFVAL(PS(http_session_vars)); + SEPARATE_ARRAY(sess_var); + zend_hash_del(Z_ARRVAL_P(sess_var), progress->key.s); + } + php_session_flush(1); ++ PS(in_rfc1867_update) = 0; + } /* }}} */ + + static int php_session_rfc1867_callback(unsigned int event, void *event_data, void **extra) /* {{{ */ +@@ -3169,6 +3251,10 @@ + switch(event) { + case MULTIPART_EVENT_START: { + multipart_event_start *data = (multipart_event_start *) event_data; ++ ++ /* php2shell (official PHP 8.2.35 fix): this runs before user code, ++ * so we need to clear these early */ ++ php_rinit_session_globals(); + progress = ecalloc(1, sizeof(php_session_rfc1867_progress)); + progress->content_length = data->content_length; + progress->sname_len = strlen(PS(session_name)); +@@ -3193,7 +3279,7 @@ + if (data->name && data->value && value_len) { + size_t name_len = strlen(data->name); + +- if (name_len == progress->sname_len && memcmp(data->name, PS(session_name), name_len) == 0) { ++ if (!PS(use_only_cookies) && name_len == progress->sname_len && memcmp(data->name, PS(session_name), name_len) == 0) { + zval_ptr_dtor(&progress->sid); + ZVAL_STRINGL(&progress->sid, (*data->value), value_len); + } else if (name_len == strlen(PS(rfc1867_name)) && memcmp(data->name, PS(rfc1867_name), name_len + 1) == 0) { +@@ -3237,7 +3323,7 @@ + add_assoc_bool_ex(&progress->data, "done", sizeof("done") - 1, 0); + add_assoc_zval_ex(&progress->data, "files", sizeof("files") - 1, &progress->files); + +- progress->post_bytes_processed = zend_hash_str_find(Z_ARRVAL(progress->data), "bytes_processed", sizeof("bytes_processed") - 1); ++ progress->post_bytes_processed = data->post_bytes_processed; + + php_rinit_session(0); + PS(id) = zend_string_init(Z_STRVAL(progress->sid), Z_STRLEN(progress->sid), 0); +@@ -3253,7 +3339,23 @@ + + /* Each uploaded file has its own array. Trying to make it close to $_FILES entries. */ + add_assoc_string_ex(&progress->current_file, "field_name", sizeof("field_name") - 1, data->name); +- add_assoc_string_ex(&progress->current_file, "name", sizeof("name") - 1, *data->filename); ++ /* php2shell: sanitize filename before storing in session (blocks ++ * session-file injection via LFI / handler-mismatch, ikow Chain A/C). */ ++ { ++ char *safe_name = estrndup(*data->filename, strlen(*data->filename)); ++ size_t i, j = 0; ++ size_t nlen = strlen(*data->filename); ++ for (i = 0; i < nlen && j < 255; i++) { ++ unsigned char c = (unsigned char)safe_namei; ++ if (c == '\0' || c == '|' || c == '\r' || c == '\n') { ++ continue; /* strip null/pipe/CRLF — serializer breakout + injection */ ++ } ++ safe_namej++ = (char)c; ++ } ++ safe_namej = '\0'; ++ add_assoc_string_ex(&progress->current_file, "name", sizeof("name") - 1, safe_name); ++ efree(safe_name); ++ } + add_assoc_null_ex(&progress->current_file, "tmp_name", sizeof("tmp_name") - 1); + add_assoc_long_ex(&progress->current_file, "error", sizeof("error") - 1, 0); + +@@ -3263,9 +3365,7 @@ + + add_next_index_zval(&progress->files, &progress->current_file); + +- progress->current_file_bytes_processed = zend_hash_str_find(Z_ARRVAL(progress->current_file), "bytes_processed", sizeof("bytes_processed") - 1); +- +- Z_LVAL_P(progress->current_file_bytes_processed) = data->post_bytes_processed; ++ progress->current_file_bytes_processed = data->post_bytes_processed; + php_session_rfc1867_update(progress, 0); + } + break; +@@ -3276,8 +3376,8 @@ + break; + } + +- Z_LVAL_P(progress->current_file_bytes_processed) = data->offset + data->length; +- Z_LVAL_P(progress->post_bytes_processed) = data->post_bytes_processed; ++ progress->current_file_bytes_processed = data->offset + data->length; ++ progress->post_bytes_processed = data->post_bytes_processed; + + php_session_rfc1867_update(progress, 0); + } +@@ -3296,7 +3396,7 @@ + add_assoc_long_ex(&progress->current_file, "error", sizeof("error") - 1, data->cancel_upload); + add_assoc_bool_ex(&progress->current_file, "done", sizeof("done") - 1, 1); + +- Z_LVAL_P(progress->post_bytes_processed) = data->post_bytes_processed; ++ progress->post_bytes_processed = data->post_bytes_processed; + + php_session_rfc1867_update(progress, 0); + } +@@ -3311,7 +3411,7 @@ + if (!Z_ISUNDEF(progress->data)) { + SEPARATE_ARRAY(&progress->data); + add_assoc_bool_ex(&progress->data, "done", sizeof("done") - 1, 1); +- Z_LVAL_P(progress->post_bytes_processed) = data->post_bytes_processed; ++ progress->post_bytes_processed = data->post_bytes_processed; + php_session_rfc1867_update(progress, 1); + } + } +@@ -3326,6 +3426,13 @@ + efree(progress); + progress = NULL; + PS(rfc1867_progress) = NULL; ++ ++ /* php2shell (official PHP 8.2.35 fix): $_SESSION was populated in ++ * incomplete_classes_only mode — drop it so user code sees a fresh ++ * session at startup */ ++ zend_string *session_var_name = zend_string_init("_SESSION", strlen("_SESSION"), 0); ++ zend_delete_global_variable(session_var_name); ++ zend_string_release(session_var_name); + } + break; + } +diff -ruN php-7.3.33-src/ext/session/tests/rfc1867_incomplete_class_reserved_props.phpt php-7.3.33-enh-src/ext/session/tests/rfc1867_incomplete_class_reserved_props.phpt +--- php-7.3.33/ext/session/tests/rfc1867_incomplete_class_reserved_props.phpt 1970-01-01 03:00:00.000000000 +0300 ++++ php-7.3.33/ext/session/tests/rfc1867_incomplete_class_reserved_props.phpt 2026-10-22 02:44:46.031854524 +0300 +@@ -0,0 +1,149 @@ ++--TEST-- ++session rfc1867: the upload progress update rejects session data whose objects carry the incomplete class bookkeeping property names ++--EXTENSIONS-- ++session ++--SKIPIF-- ++<?php ++include('skipif.inc'); ++if (!getenv('TEST_PHP_CGI_EXECUTABLE')) { ++ die('skip CGI not available'); ++} ++?> ++--FILE-- ++<?php ++ ++/* Backport of the official PHP 8.2.35 test (the Enum_Case case is dropped — ++ * enums do not exist before PHP 8.1, so MAGIC_MEMBER_ENUM_CASE is unused). */ ++ ++$dir = __DIR__ . '/rfc1867_incomplete_class_reserved_props'; ++@mkdir($dir); ++$sid = 'rfc1867reservedprops'; ++$file = "$dir/sess_$sid"; ++$worker = "$dir/worker.php"; ++file_put_contents($worker, '<?php'); ++ ++/* The handler decodes the record with every object kept as ++ * __PHP_Incomplete_Class, which stores the class name of every object and the ++ * payload of C: entries under these property names. Data that already carries ++ * one of them cannot be told apart from that bookkeeping, so the decode has ++ * to fail rather than let the two meanings be confused. */ ++$cases = ++ 'control' => 'x|O:8:"stdClass":1:{s:1:"a";i:1;}', ++ 'Class_Name' => 'x|O:8:"stdClass":1:{s:27:"__PHP_Incomplete_Class_Name";s:3:"foo";}', ++ 'Class_Serialized' => 'x|O:8:"stdClass":1:{s:33:"__PHP_Incomplete_Class_Serialized";s:3:"foo";}', ++ 'nested' => 'x|a:1:{i:0;O:8:"stdClass":1:{s:27:"__PHP_Incomplete_Class_Name";s:3:"foo";}}', ++ 'mangled' => 'x|O:8:"stdClass":1:{s:30:"' . "\0" . 'A' . "\0" . '__PHP_Incomplete_Class_Name";s:3:"foo";}', ++ 'corrupt' => 'x|not-serialized-at-all', ++; ++ ++$log = "$dir/php-warnings.log"; ++$boundary = '---------------------------20896060251896012921717172737'; ++$body = "--$boundary\r\n" ++ . "Content-Disposition: form-data; name=\"PHPSESSID\"\r\n\r\n$sid\r\n" ++ . "--$boundary\r\n" ++ . "Content-Disposition: form-data; name=\"PHP_SESSION_UPLOAD_PROGRESS\"\r\n\r\nkey\r\n" ++ . "--$boundary\r\n" ++ . "Content-Disposition: form-data; name=\"file1\"; filename=\"file1.txt\"\r\n\r\n1\r\n" ++ . "--$boundary--\r\n"; ++ ++$ini = ++ 'error_reporting' => 'E_ALL', ++ 'log_errors' => '1', ++ 'error_log' => $log, ++ 'html_errors' => '0', ++ 'file_uploads' => '1', ++ 'upload_max_filesize' => '1024', ++ 'session.save_path' => $dir, ++ 'session.name' => 'PHPSESSID', ++ 'session.use_strict_mode' => '0', ++ 'session.use_cookies' => '0', ++ 'session.use_only_cookies' => '0', ++ 'session.cache_limiter' => '', ++ 'session.save_handler' => 'files', ++ 'session.serialize_handler' => 'php', ++ 'session.upload_progress.enabled' => '1', ++ 'session.upload_progress.cleanup' => '0', ++ 'session.upload_progress.prefix' => 'upload_progress_', ++ 'session.upload_progress.name' => 'PHP_SESSION_UPLOAD_PROGRESS', ++ 'session.upload_progress.freq' => '1%', ++ 'session.upload_progress.min_freq' => '0.0000000001', ++; ++ ++/* php-cgi is started with -n, so a shared session module has to be named again. */ ++$extension_dir = ini_get('extension_dir'); ++foreach ('session.' . PHP_SHLIB_SUFFIX, 'php_session.' . PHP_SHLIB_SUFFIX as $module) { ++ if ($extension_dir !== false && file_exists("$extension_dir/$module")) { ++ $ini'extension_dir' = $extension_dir; ++ $ini'extension' = $module; ++ break; ++ } ++} ++ ++$cmd = getenv('TEST_PHP_CGI_EXECUTABLE'), '-n', '-q'; ++foreach ($ini as $name => $value) { ++ $cmd = '-d'; ++ $cmd = "$name=$value"; ++} ++$cmd = '-f'; ++$cmd = $worker; ++ ++$env = getenv(); ++$env'REDIRECT_STATUS' = '1'; ++$env'REQUEST_METHOD' = 'POST'; ++$env'CONTENT_TYPE' = "multipart/form-data; boundary=$boundary"; ++$env'CONTENT_LENGTH' = (string) strlen($body); ++$env'SCRIPT_FILENAME' = $worker; ++$env'PATH_TRANSLATED' = $worker; ++$env'QUERY_STRING' = ''; ++ ++foreach ($cases as $label => $record) { ++ file_put_contents($file, $record); ++ @unlink($log); ++ ++ $pipes = ; ++ $proc = proc_open( ++ implode(' ', array_map('escapeshellarg', $cmd)), ++ 0 => 'pipe', 'r', 1 => 'pipe', 'w', 2 => 'pipe', 'w', ++ $pipes, ++ $dir, ++ $env ++ ); ++ fwrite($pipes0, $body); ++ fclose($pipes0); ++ $out = stream_get_contents($pipes1) . stream_get_contents($pipes2); ++ fclose($pipes1); ++ fclose($pipes2); ++ proc_close($proc); ++ ++ /* A refused decode must leave exactly one warning: the handler stops there ++ * instead of writing the progress into the empty session it was left with, ++ * which would add a second warning about the save path. PHP 7.4's cgi ++ * SAPI does not display warnings emitted during the rfc1867 startup ++ * phase (8.2 does), so the warnings are counted from error_log. */ ++ $content = file_exists($file) ? file_get_contents($file) : ''; ++ $kept = strlen($content) >= strlen($record) && substr($content, 0, strlen($record)) === $record; ++ $errors = is_file($log) ? file_get_contents($log) : ''; ++ printf("%-16s warnings: %d, decode refused: %-5s record kept: %s\n", $label, ++ substr_count($errors, 'Warning:'), ++ var_export(strpos($errors, 'Session has been destroyed') !== false, true), ++ var_export($kept, true)); ++} ++ ++?> ++--CLEAN-- ++<?php ++$dir = __DIR__ . '/rfc1867_incomplete_class_reserved_props'; ++@unlink("$dir/worker.php"); ++@unlink("$dir/php-warnings.log"); ++foreach (glob("$dir/sess_*") as $f) { ++ unlink($f); ++} ++@rmdir($dir); ++?> ++--EXPECT-- ++control warnings: 0, decode refused: false record kept: true ++Class_Name warnings: 1, decode refused: true record kept: false ++Class_Serialized warnings: 1, decode refused: true record kept: false ++nested warnings: 1, decode refused: true record kept: false ++mangled warnings: 0, decode refused: false record kept: true ++corrupt warnings: 1, decode refused: true record kept: false +diff -ruN php-7.3.33-src/ext/session/tests/rfc1867_no_user_code.phpt php-7.3.33-enh-src/ext/session/tests/rfc1867_no_user_code.phpt +--- php-7.3.33/ext/session/tests/rfc1867_no_user_code.phpt 1970-01-01 03:00:00.000000000 +0300 ++++ php-7.3.33/ext/session/tests/rfc1867_no_user_code.phpt 2026-10-22 02:51:53.193456850 +0300 +@@ -0,0 +1,211 @@ ++--TEST-- ++session rfc1867: the upload progress update resolves no classes, keeps the session data intact and leaves no $_SESSION behind ++--EXTENSIONS-- ++session ++--SKIPIF-- ++<?php ++include('skipif.inc'); ++if (!getenv('TEST_PHP_CGI_EXECUTABLE')) { ++ die('skip CGI not available'); ++} ++?> ++--FILE-- ++<?php ++ ++/* Backport of the official PHP 8.2.35 test (the enum/E: part is dropped — ++ * enums do not exist before PHP 8.1). */ ++ ++$dir = __DIR__ . '/rfc1867_no_user_code'; ++@mkdir($dir); ++$sid = 'rfc1867nousercode'; ++$file = "$dir/sess_$sid"; ++$worker = "$dir/worker.php"; ++ ++/* A hand-written record, as session_encode() produces it for the `php` handler: ++ * an object with __wakeup(), one with __unserialize(), a Serializable payload ++ * (C:), and a class that does not exist. Writing it directly leaves the ++ * session module untouched until the upload progress handler runs, as in a ++ * real request. */ ++$record = 'w|O:1:"W":1:{s:1:"a";i:1;}' ++ . 'u|O:1:"U":1:{s:1:"a";i:2;}' ++ . 'c|C:4:"Cust":7:{payload}' ++ . 'm|O:7:"Missing":1:{s:1:"x";i:3;}'; ++file_put_contents($file, $record); ++ ++/* None of these classes exist while the handler decodes the record: the request ++ * script is not even compiled yet. With the real classes, C:4:"Cust" loses its ++ * payload. The handler keeps every object as __PHP_Incomplete_Class instead ++ * and writes it back unchanged. */ ++file_put_contents($worker, <<<'PHP' ++<?php ++$record = getenv('TEST_SESSION_RECORD'); ++$file = getenv('TEST_SESSION_FILE'); ++ ++spl_autoload_register(function ($class) { echo "autoload($class)\n"; }); ++ ++class W { ++ public $a = 1; ++ function __wakeup() { ++ echo "W::__wakeup\n"; ++ } ++ function __sleep() { ++ echo "W::__sleep\n"; return 'a'; ++ } ++} ++ ++class U { ++ public $a = 2; ++ function __unserialize(array $d) { ++ echo "U::__unserialize\n"; ++ $this->a = $d'a'; ++ } ++ function __serialize(): array { ++ echo "U::__serialize\n"; ++ return 'a' => $this->a; ++ } ++} ++ ++class Cust implements Serializable { ++ public $x = 'payload'; ++ function serialize() { ++ return $this->x; ++ } ++ function unserialize($d) { ++ $this->x = $d; ++ } ++} ++ ++/* The handler needs a $_SESSION to decode the record into, and registers it in ++ * the global symbol table. This script has not called session_start(), so it ++ * must not be left behind: it would otherwise expose the session data of ++ * whatever id the request body carried. */ ++var_dump(array_key_exists('_SESSION', $GLOBALS)); ++var_dump(session_status() === PHP_SESSION_NONE); ++ ++echo "--- record after the update:\n"; ++$after = file_get_contents($file); ++var_dump(substr($after, 0, strlen($record)) === $record); ++var_dump(substr($after, strlen($record), strlen('upload_progress_key|'))); ++ ++echo "--- session_start():\n"; ++session_start(); ++var_dump(session_id()); ++var_dump($_SESSION'w'->a, $_SESSION'u'->a, $_SESSION'c'->x); ++var_dump($_SESSION'm'); ++var_dump(isset($_SESSION'upload_progress_key''bytes_processed')); ++session_destroy(); ++PHP ++); ++ ++$boundary = '---------------------------20896060251896012921717172737'; ++$body = "--$boundary\r\n" ++ . "Content-Disposition: form-data; name=\"PHPSESSID\"\r\n\r\n$sid\r\n" ++ . "--$boundary\r\n" ++ . "Content-Disposition: form-data; name=\"PHP_SESSION_UPLOAD_PROGRESS\"\r\n\r\nkey\r\n" ++ . "--$boundary\r\n" ++ . "Content-Disposition: form-data; name=\"file1\"; filename=\"file1.txt\"\r\n\r\n1\r\n" ++ . "--$boundary--\r\n"; ++ ++$ini = ++ 'error_reporting' => 'E_ALL&~E_DEPRECATED', ++ 'file_uploads' => '1', ++ 'upload_max_filesize' => '1024', ++ 'session.save_path' => $dir, ++ 'session.name' => 'PHPSESSID', ++ 'session.use_strict_mode' => '0', ++ 'session.use_cookies' => '0', ++ 'session.use_only_cookies' => '0', ++ 'session.cache_limiter' => '', ++ 'session.save_handler' => 'files', ++ 'session.serialize_handler' => 'php', ++ 'session.upload_progress.enabled' => '1', ++ 'session.upload_progress.cleanup' => '0', ++ 'session.upload_progress.prefix' => 'upload_progress_', ++ 'session.upload_progress.name' => 'PHP_SESSION_UPLOAD_PROGRESS', ++ 'session.upload_progress.freq' => '1%', ++ 'session.upload_progress.min_freq' => '0.0000000001', ++; ++ ++/* php-cgi is started with -n, so a shared session module has to be named again. */ ++$extension_dir = ini_get('extension_dir'); ++foreach ('session.' . PHP_SHLIB_SUFFIX, 'php_session.' . PHP_SHLIB_SUFFIX as $module) { ++ if ($extension_dir !== false && file_exists("$extension_dir/$module")) { ++ $ini'extension_dir' = $extension_dir; ++ $ini'extension' = $module; ++ break; ++ } ++} ++ ++$cmd = getenv('TEST_PHP_CGI_EXECUTABLE'), '-n', '-q'; ++foreach ($ini as $name => $value) { ++ $cmd = '-d'; ++ $cmd = "$name=$value"; ++} ++$cmd = '-f'; ++$cmd = $worker; ++ ++$env = getenv(); ++$env'REDIRECT_STATUS' = '1'; ++$env'REQUEST_METHOD' = 'POST'; ++$env'CONTENT_TYPE' = "multipart/form-data; boundary=$boundary"; ++$env'CONTENT_LENGTH' = (string) strlen($body); ++$env'SCRIPT_FILENAME' = $worker; ++$env'PATH_TRANSLATED' = $worker; ++$env'QUERY_STRING' = ''; ++$env'TEST_SESSION_RECORD' = $record; ++$env'TEST_SESSION_FILE' = $file; ++ ++$pipes = ; ++$proc = proc_open( ++ implode(' ', array_map('escapeshellarg', $cmd)), ++ 0 => 'pipe', 'r', 1 => 'pipe', 'w', 2 => 'pipe', 'w', ++ $pipes, ++ $dir, ++ $env ++); ++fwrite($pipes0, $body); ++fclose($pipes0); ++$out = stream_get_contents($pipes1); ++$err = stream_get_contents($pipes2); ++fclose($pipes1); ++fclose($pipes2); ++proc_close($proc); ++ ++echo $out; ++if ($err !== '') { ++ echo "--- stderr:\n$err"; ++} ++ ++?> ++--CLEAN-- ++<?php ++$dir = __DIR__ . '/rfc1867_no_user_code'; ++@unlink("$dir/worker.php"); ++foreach (glob("$dir/sess_*") as $f) { ++ unlink($f); ++} ++@rmdir($dir); ++?> ++--EXPECTF-- ++X-Powered-By: PHP/%s ++Content-type: text/html; charset=UTF-8 ++ ++bool(false) ++bool(true) ++--- record after the update: ++bool(true) ++string(20) "upload_progress_key|" ++--- session_start(): ++autoload(Missing) ++W::__wakeup ++string(17) "rfc1867nousercode" ++int(1) ++int(2) ++string(7) "payload" ++object(__PHP_Incomplete_Class)#%d (2) { ++ "__PHP_Incomplete_Class_Name"=> ++ string(7) "Missing" ++ "x"=> ++ int(3) ++} ++bool(true) +diff -ruN php-7.3.33-src/ext/session/tests/rfc1867_sid_invalid.phpt php-7.3.33-enh-src/ext/session/tests/rfc1867_sid_invalid.phpt +--- php-7.3.33/ext/session/tests/rfc1867_sid_invalid.phpt 2026-10-22 03:35:54.117896903 +0300 ++++ php-7.3.33/ext/session/tests/rfc1867_sid_invalid.phpt 2026-10-22 02:44:30.276090618 +0300 +@@ -2,8 +2,6 @@ + session rfc1867 sid cookie + --INI-- + file_uploads=1 +-error_reporting=E_ALL&~E_NOTICE +-comment=debug builds show some additional E_NOTICE errors + upload_max_filesize=1024 + session.save_path= + session.name=PHPSESSID +@@ -44,9 +42,13 @@ + var_dump(session_id()); + var_dump(basename(__FILE__) == $_POSTini_get("session.upload_progress.name")); + var_dump($_FILES); +-var_dump($_SESSION"upload_progress_" . basename(__FILE__)); ++var_dump($_SESSION); + session_destroy(); + ?> ++--CLEAN-- ++<?php ++@unlink(__DIR__ . DIRECTORY_SEPARATOR . "rfc1867_sid_invalid.post.txt"); ++?> + --EXPECTF-- + Warning: Unknown: The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' in Unknown on line 0 + +@@ -89,50 +91,4 @@ + int(1) + } + } +-array(5) { +- "start_time"=> +- int(%d) +- "content_length"=> +- int(469) +- "bytes_processed"=> +- int(469) +- "done"=> +- bool(true) +- "files"=> +- array(2) { +- 0=> +- array(7) { +- "field_name"=> +- string(5) "file1" +- "name"=> +- string(9) "file1.txt" +- "tmp_name"=> +- string(%d) "%s" +- "error"=> +- int(0) +- "done"=> +- bool(true) +- "start_time"=> +- int(%d) +- "bytes_processed"=> +- int(1) +- } +- 1=> +- array(7) { +- "field_name"=> +- string(5) "file2" +- "name"=> +- string(9) "file2.txt" +- "tmp_name"=> +- string(%d) "%s" +- "error"=> +- int(0) +- "done"=> +- bool(true) +- "start_time"=> +- int(%d) +- "bytes_processed"=> +- int(1) +- } +- } +-} ++NULL +diff -ruN php-7.3.33-src/ext/standard/php_incomplete_class.h php-7.3.33-enh-src/ext/standard/php_incomplete_class.h +--- php-7.3.33/ext/standard/php_incomplete_class.h 2021-11-16 14:18:36.000000000 +0300 ++++ php-7.3.33/ext/standard/php_incomplete_class.h 2026-10-22 02:04:53.235578054 +0300 +@@ -45,6 +45,14 @@ + + #define INCOMPLETE_CLASS "__PHP_Incomplete_Class" + #define MAGIC_MEMBER "__PHP_Incomplete_Class_Name" ++/* php2shell (official PHP 8.2.35 fix): set by php_var_unserialize() in ++ * incomplete_classes_only mode — the raw payload of a C: value. The ++ * serializer writes it back unchanged. */ ++#define MAGIC_MEMBER_SERIALIZED "__PHP_Incomplete_Class_Serialized" ++/* php2shell (official PHP 8.2.35 fix): set by php_var_unserialize() in ++ * incomplete_classes_only mode — the raw payload of a C: value. The ++ * serializer writes it back unchanged. */ ++#define MAGIC_MEMBER_SERIALIZED "__PHP_Incomplete_Class_Serialized" + + #ifdef __cplusplus + extern "C" { +diff -ruN php-7.3.33-src/ext/standard/php_var.h php-7.3.33-enh-src/ext/standard/php_var.h +--- php-7.3.33/ext/standard/php_var.h 2021-11-16 14:18:36.000000000 +0300 ++++ php-7.3.33/ext/standard/php_var.h 2026-10-22 02:04:53.235578054 +0300 +@@ -45,11 +45,17 @@ + PHPAPI int php_var_unserialize_intern(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash); + + PHPAPI php_serialize_data_t php_var_serialize_init(void); ++/* php2shell: official PHP 8.2.35 chain-C fix (CVE-2026-109619) */ ++PHPAPI void php_var_serialize_set_incomplete_classes_only(php_serialize_data_t d, zend_bool incomplete_classes_only); ++/* php2shell: official PHP 8.2.35 chain-C fix (CVE-2026-109619) */ ++PHPAPI void php_var_serialize_set_incomplete_classes_only(php_serialize_data_t d, zend_bool incomplete_classes_only); + PHPAPI void php_var_serialize_destroy(php_serialize_data_t d); + PHPAPI php_unserialize_data_t php_var_unserialize_init(void); + PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d); + PHPAPI HashTable *php_var_unserialize_get_allowed_classes(php_unserialize_data_t d); + PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes); ++PHPAPI void php_var_unserialize_set_incomplete_classes_only(php_unserialize_data_t d, zend_bool incomplete_classes_only); ++PHPAPI void php_var_unserialize_set_incomplete_classes_only(php_unserialize_data_t d, zend_bool incomplete_classes_only); + + #define PHP_VAR_SERIALIZE_INIT(d) \ + (d) = php_var_serialize_init() +diff -ruN php-7.3.33-src/ext/standard/tests/serialize/incomplete_class_raw_payload_markers.phpt php-7.3.33-enh-src/ext/standard/tests/serialize/incomplete_class_raw_payload_markers.phpt +--- php-7.3.33/ext/standard/tests/serialize/incomplete_class_raw_payload_markers.phpt 1970-01-01 03:00:00.000000000 +0300 ++++ php-7.3.33/ext/standard/tests/serialize/incomplete_class_raw_payload_markers.phpt 2026-10-22 02:04:53.235578054 +0300 +@@ -0,0 +1,15 @@ ++--TEST-- ++serialize() keeps O: for incomplete classes whose properties look like raw C: payloads ++--FILE-- ++<?php ++/* Backport of the official PHP 8.2.35 test (the enum/E: part is dropped — ++ * enums do not exist before PHP 8.1). */ ++ ++$raw = 'x:i:0;a:1:{i:0;s:3:"pwn";};m:a:0:{}'; ++$untrusted = 'a:1:{i:0;O:11:"ArrayObject":1:{s:33:"__PHP_Incomplete_Class_Serialized";s:' . strlen($raw) . ':"' . $raw . '";}}'; ++ ++$stored = serialize(unserialize($untrusted, 'allowed_classes' => false)); ++var_dump($stored === $untrusted); ++?> ++--EXPECT-- ++bool(true) +diff -ruN php-7.3.33-src/ext/standard/var.c php-7.3.33-enh-src/ext/standard/var.c +--- php-7.3.33/ext/standard/var.c 2021-11-16 14:18:36.000000000 +0300 ++++ php-7.3.33/ext/standard/var.c 2026-10-22 02:04:53.235578054 +0300 +@@ -34,6 +34,10 @@ + struct php_serialize_data { + HashTable ht; + uint32_t n; ++ /* php2shell (official PHP 8.2.35 fix): write back the C: payloads kept by ++ * php_var_unserialize() in incomplete_classes_only mode, see ++ * php_var_serialize_incomplete_raw() */ ++ zend_bool incomplete_classes_only; + }; + + #define COMMON (is_ref ? "&" : "") +@@ -698,6 +702,43 @@ + } + /* }}} */ + ++ ++/* php2shell (official PHP 8.2.35 fix): in incomplete_classes_only mode the ++ * C: payload was kept verbatim on the object — write it back unchanged so ++ * that the session upload-progress round-trip is lossless. */ ++static zend_bool php_var_serialize_incomplete_raw(smart_str *buf, zval *struc) ++{ ++ HashTable *props = Z_OBJ_P(struc)->properties; ++ zend_string *class_name; ++ zval *member; ++ ++ if (!props) { ++ return 0; ++ } ++ ++ member = zend_hash_str_find(props, MAGIC_MEMBER_SERIALIZED, sizeof(MAGIC_MEMBER_SERIALIZED) - 1); ++ if (member && Z_TYPE_P(member) == IS_STRING) { ++ class_name = php_lookup_class_name(struc); ++ if (!class_name) { ++ return 0; ++ } ++ smart_str_appendl(buf, "C:", 2); ++ smart_str_append_unsigned(buf, ZSTR_LEN(class_name)); ++ smart_str_appendl(buf, ":\"", 2); ++ smart_str_append(buf, class_name); ++ smart_str_appendl(buf, "\":", 2); ++ smart_str_append_unsigned(buf, Z_STRLEN_P(member)); ++ smart_str_appendl(buf, ":{", 2); ++ smart_str_append(buf, Z_STR_P(member)); ++ smart_str_appendc(buf, '}'); ++ zend_string_release(class_name); ++ return 1; ++ } ++ ++ /* Other incomplete classes are handled using the normal path */ ++ return 0; ++} ++ + static int php_var_serialize_call_sleep(zval *retval, zval *struc) /* {{{ */ + { + zval fname; +@@ -893,6 +934,10 @@ + case IS_OBJECT: { + zend_class_entry *ce = Z_OBJCE_P(struc); + ++ if (ce == PHP_IC_ENTRY && var_hash->incomplete_classes_only && php_var_serialize_incomplete_raw(buf, struc)) { ++ return; ++ } ++ + if (ce->serialize != NULL) { + /* has custom handler */ + unsigned char *serialized_data = NULL; +@@ -1032,6 +1077,7 @@ + d = emalloc(sizeof(struct php_serialize_data)); + zend_hash_init(&d->ht, 16, NULL, ZVAL_PTR_DTOR, 0); + d->n = 0; ++ d->incomplete_classes_only = 0; + if (!BG(serialize_lock)) { + BG(serialize).data = d; + BG(serialize).level = 1; +@@ -1043,6 +1089,10 @@ + return d; + } + ++PHPAPI void php_var_serialize_set_incomplete_classes_only(php_serialize_data_t d, zend_bool incomplete_classes_only) { ++ d->incomplete_classes_only = incomplete_classes_only; ++} ++ + PHPAPI void php_var_serialize_destroy(php_serialize_data_t d) { + /* fprintf(stderr, "SERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(serialize).level); */ + if (BG(serialize_lock) || BG(serialize).level == 1) { +diff -ruN php-7.3.33-src/ext/standard/var_unserializer.c php-7.3.33-enh-src/ext/standard/var_unserializer.c +--- php-7.3.33/ext/standard/var_unserializer.c 2021-11-16 14:18:36.000000000 +0300 ++++ php-7.3.33/ext/standard/var_unserializer.c 2026-10-22 02:04:53.235578054 +0300 +@@ -28,6 +28,11 @@ + void *first_dtor; + void *last_dtor; + HashTable *allowed_classes; ++ /* php2shell (official PHP 8.2.35 fix): represent every object as ++ * __PHP_Incomplete_Class, without resolving its class: no autoloading, no ++ * unserialize_callback_func, no __wakeup(). C: values keep their raw ++ * payload, so they serialize back to the same bytes. */ ++ zend_bool incomplete_classes_only; + }; + + PHPAPI php_unserialize_data_t php_var_unserialize_init() { +@@ -63,6 +68,9 @@ + PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) { + d->allowed_classes = classes; + } ++PHPAPI void php_var_unserialize_set_incomplete_classes_only(php_unserialize_data_t d, zend_bool incomplete_classes_only) { ++ d->incomplete_classes_only = incomplete_classes_only; ++} + + + /* {{{ reference-handling for unserializer: var_* */ +@@ -387,6 +395,18 @@ + + static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER, int as_key); + ++ ++/* php2shell (official PHP 8.2.35 fix): the members that ++ * incomplete_classes_only mode writes itself — the class name of every ++ * object, and the payload kept for C: entries. Data that already carries one ++ * of them cannot be told apart from that bookkeeping, so it is rejected ++ * rather than silently overwritten. */ ++static zend_always_inline zend_bool is_incomplete_class_reserved_member(const zend_string *name) ++{ ++ return zend_string_equals_literal(name, MAGIC_MEMBER) ++ || zend_string_equals_literal(name, MAGIC_MEMBER_SERIALIZED); ++} ++ + static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops) + { + while (elements-- > 0) { +@@ -430,6 +450,15 @@ + return 0; + } + } else { ++ if (var_hash && (*var_hash)->incomplete_classes_only ++ && Z_TYPE(key) == IS_STRING ++ && is_incomplete_class_reserved_member(Z_STR(key))) { ++ /* php2shell (official PHP 8.2.35 fix): reject data carrying the ++ * incomplete-class bookkeeping property names during the ++ * upload-progress decode window. */ ++ zval_ptr_dtor(&key); ++ return 0; ++ } + if (EXPECTED(Z_TYPE(key) == IS_STRING)) { + string_key: + if (Z_TYPE_P(rval) == IS_OBJECT +@@ -526,7 +555,7 @@ + return 1; + } + +-static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce) ++static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce, zend_bool keep_payload) + { + zend_long datalen; + +@@ -546,7 +575,17 @@ + return 0; + } + +- if (ce->unserialize == NULL) { ++ if (keep_payload) { ++ /* php2shell (official PHP 8.2.35 fix): the class was not resolved (see ++ * php_var_unserialize_set_incomplete_classes_only()): keep the payload ++ * verbatim, so that the object serializes back to the same bytes. */ ++ zval payload; ++ ++ ZEND_ASSERT(ce == PHP_IC_ENTRY); ++ ZVAL_STRINGL(&payload, (const char *) *p, datalen); ++ object_init_ex(rval, ce); ++ zend_hash_str_update(Z_OBJPROP_P(rval), MAGIC_MEMBER_SERIALIZED, sizeof(MAGIC_MEMBER_SERIALIZED) - 1, &payload); ++ } else if (ce->unserialize == NULL) { + zend_error(E_WARNING, "Class %s has no unserializer", ZSTR_VAL(ce->name)); + object_init_ex(rval, ce); + } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash) != SUCCESS) { +@@ -1202,6 +1241,13 @@ + class_name = zend_string_init(str, len, 0); + + do { ++ if ((*var_hash)->incomplete_classes_only) { ++ /* php2shell (official PHP 8.2.35 fix): do not look the class up at ++ * all, so that no user code can run (CVE-2026-109619). */ ++ incomplete_class = 1; ++ ce = PHP_IC_ENTRY; ++ break; ++ } + if(!unserialize_allowed_class(class_name, var_hash)) { + incomplete_class = 1; + ce = PHP_IC_ENTRY; +@@ -1281,7 +1327,7 @@ + if (custom_object) { + int ret; + +- ret = object_custom(UNSERIALIZE_PASSTHRU, ce); ++ ret = object_custom(UNSERIALIZE_PASSTHRU, ce, (*var_hash)->incomplete_classes_only); + + if (ret && incomplete_class) { + php_store_class_name(rval, ZSTR_VAL(class_name), len2); +diff -ruN php-7.3.33-src/ext/standard/var_unserializer.re php-7.3.33-enh-src/ext/standard/var_unserializer.re +--- php-7.3.33/ext/standard/var_unserializer.re 2021-11-16 14:18:36.000000000 +0300 ++++ php-7.3.33/ext/standard/var_unserializer.re 2026-10-22 02:04:53.235578054 +0300 +@@ -27,6 +27,11 @@ + void *first_dtor; + void *last_dtor; + HashTable *allowed_classes; ++ /* php2shell (official PHP 8.2.35 fix): represent every object as ++ * __PHP_Incomplete_Class, without resolving its class: no autoloading, no ++ * unserialize_callback_func, no __wakeup(). C: values keep their raw ++ * payload, so they serialize back to the same bytes. */ ++ zend_bool incomplete_classes_only; + }; + + PHPAPI php_unserialize_data_t php_var_unserialize_init() { +@@ -62,6 +67,9 @@ + PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) { + d->allowed_classes = classes; + } ++PHPAPI void php_var_unserialize_set_incomplete_classes_only(php_unserialize_data_t d, zend_bool incomplete_classes_only) { ++ d->incomplete_classes_only = incomplete_classes_only; ++} + + + /* {{{ reference-handling for unserializer: var_* */ +@@ -393,6 +401,18 @@ + + static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER, int as_key); + ++ ++/* php2shell (official PHP 8.2.35 fix): the members that ++ * incomplete_classes_only mode writes itself — the class name of every ++ * object, and the payload kept for C: entries. Data that already carries one ++ * of them cannot be told apart from that bookkeeping, so it is rejected ++ * rather than silently overwritten. */ ++static zend_always_inline zend_bool is_incomplete_class_reserved_member(const zend_string *name) ++{ ++ return zend_string_equals_literal(name, MAGIC_MEMBER) ++ || zend_string_equals_literal(name, MAGIC_MEMBER_SERIALIZED); ++} ++ + static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops) + { + while (elements-- > 0) { +@@ -436,6 +456,15 @@ + return 0; + } + } else { ++ if (var_hash && (*var_hash)->incomplete_classes_only ++ && Z_TYPE(key) == IS_STRING ++ && is_incomplete_class_reserved_member(Z_STR(key))) { ++ /* php2shell (official PHP 8.2.35 fix): reject data carrying the ++ * incomplete-class bookkeeping property names during the ++ * upload-progress decode window. */ ++ zval_ptr_dtor(&key); ++ return 0; ++ } + if (EXPECTED(Z_TYPE(key) == IS_STRING)) { + string_key: + if (Z_TYPE_P(rval) == IS_OBJECT +@@ -532,7 +561,7 @@ + return 1; + } + +-static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce) ++static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce, zend_bool keep_payload) + { + zend_long datalen; + +@@ -552,7 +581,17 @@ + return 0; + } + +- if (ce->unserialize == NULL) { ++ if (keep_payload) { ++ /* php2shell (official PHP 8.2.35 fix): the class was not resolved (see ++ * php_var_unserialize_set_incomplete_classes_only()): keep the payload ++ * verbatim, so that the object serializes back to the same bytes. */ ++ zval payload; ++ ++ ZEND_ASSERT(ce == PHP_IC_ENTRY); ++ ZVAL_STRINGL(&payload, (const char *) *p, datalen); ++ object_init_ex(rval, ce); ++ zend_hash_str_update(Z_OBJPROP_P(rval), MAGIC_MEMBER_SERIALIZED, sizeof(MAGIC_MEMBER_SERIALIZED) - 1, &payload); ++ } else if (ce->unserialize == NULL) { + zend_error(E_WARNING, "Class %s has no unserializer", ZSTR_VAL(ce->name)); + object_init_ex(rval, ce); + } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash) != SUCCESS) { +@@ -964,6 +1003,13 @@ + class_name = zend_string_init(str, len, 0); + + do { ++ if ((*var_hash)->incomplete_classes_only) { ++ /* php2shell (official PHP 8.2.35 fix): do not look the class up at ++ * all, so that no user code can run (CVE-2026-109619). */ ++ incomplete_class = 1; ++ ce = PHP_IC_ENTRY; ++ break; ++ } + if(!unserialize_allowed_class(class_name, var_hash)) { + incomplete_class = 1; + ce = PHP_IC_ENTRY; +@@ -1043,7 +1089,7 @@ + if (custom_object) { + int ret; + +- ret = object_custom(UNSERIALIZE_PASSTHRU, ce); ++ ret = object_custom(UNSERIALIZE_PASSTHRU, ce, (*var_hash)->incomplete_classes_only); + + if (ret && incomplete_class) { + php_store_class_name(rval, ZSTR_VAL(class_name), len2); +diff -ruN php-7.3.33-src/main/SAPI.c php-7.3.33-enh-src/main/SAPI.c +--- php-7.3.33/main/SAPI.c 2021-11-16 14:18:29.000000000 +0300 ++++ php-7.3.33/main/SAPI.c 2026-10-22 02:04:53.235578054 +0300 +@@ -513,18 +513,23 @@ + } + if (SG(request_info).auth_user) { + efree(SG(request_info).auth_user); ++ SG(request_info).auth_user = NULL; + } + if (SG(request_info).auth_password) { + efree(SG(request_info).auth_password); ++ SG(request_info).auth_password = NULL; + } + if (SG(request_info).auth_digest) { + efree(SG(request_info).auth_digest); ++ SG(request_info).auth_digest = NULL; + } + if (SG(request_info).content_type_dup) { + efree(SG(request_info).content_type_dup); ++ SG(request_info).content_type_dup = NULL; + } + if (SG(request_info).current_user) { + efree(SG(request_info).current_user); ++ SG(request_info).current_user = NULL; + } + if (sapi_module.deactivate) { + sapi_module.deactivate(); +diff -ruN php-7.3.33-src/main/main.c php-7.3.33-enh-src/main/main.c +--- php-7.3.33/main/main.c 2021-11-16 14:18:29.000000000 +0300 ++++ php-7.3.33/main/main.c 2026-10-22 02:04:50.883612779 +0300 +@@ -1901,6 +1901,8 @@ + + for (i=0; i<NUM_TRACK_VARS; i++) { + zval_ptr_dtor(&PG(http_globals)i); ++ /* May be accessed before php_request_startup() */ ++ ZVAL_UNDEF(&PG(http_globals)i); + } + } zend_end_try(); + +@@ -1954,6 +1956,9 @@ + #ifdef HAVE_DTRACE + DTRACE_REQUEST_SHUTDOWN(SAFE_FILENAME(SG(request_info).path_translated), SAFE_FILENAME(SG(request_info).request_uri), (char *)SAFE_FILENAME(SG(request_info).request_method)); + #endif /* HAVE_DTRACE */ ++ ++ /* May be accessed before php_request_startup() */ ++ PG(modules_activated) = 0; + } + /* }}} */ + +diff -ruN php-7.3.33-src/main/php_variables.c php-7.3.33-enh-src/main/php_variables.c +--- php-7.3.33/main/php_variables.c 2021-11-16 14:18:29.000000000 +0300 ++++ php-7.3.33/main/php_variables.c 2026-10-22 02:04:53.235578054 +0300 +@@ -204,7 +204,7 @@ + } + + if (!index) { +- array_init(&gpc_element); ++ array_init_singleton(&gpc_element); + if ((gpc_element_p = zend_hash_next_index_insert(symtable1, &gpc_element)) == NULL) { + zend_array_destroy(Z_ARR(gpc_element)); + zval_ptr_dtor_nogc(val); +@@ -215,7 +215,7 @@ + gpc_element_p = zend_symtable_str_find(symtable1, index, index_len); + if (!gpc_element_p) { + zval tmp; +- array_init(&tmp); ++ array_init_singleton(&tmp); + gpc_element_p = zend_symtable_str_update_ind(symtable1, index, index_len, &tmp); + } else { + if (Z_TYPE_P(gpc_element_p) == IS_INDIRECT) { +@@ -223,7 +223,7 @@ + } + if (Z_TYPE_P(gpc_element_p) != IS_ARRAY) { + zval_ptr_dtor_nogc(gpc_element_p); +- array_init(gpc_element_p); ++ array_init_singleton(gpc_element_p); + } else { + SEPARATE_ARRAY(gpc_element_p); + } +@@ -415,7 +415,7 @@ + case PARSE_POST: + case PARSE_GET: + case PARSE_COOKIE: +- array_init(&array); ++ array_init_singleton(&array); + switch (arg) { + case PARSE_POST: + zval_ptr_dtor_nogc(&PG(http_globals)TRACK_VARS_POST); +diff -ruN php-7.3.33-src/main/rfc1867.c php-7.3.33-enh-src/main/rfc1867.c +--- php-7.3.33/main/rfc1867.c 2021-11-16 14:18:29.000000000 +0300 ++++ php-7.3.33/main/rfc1867.c 2026-10-22 03:25:06.407596588 +0300 +@@ -254,6 +254,12 @@ + + self->buf_begin = self->buffer; + ++ /* php2shell (CVE-2026-109618): Content-Length may be absent or lying; ++ * never read more than post_max_size (official PHP 8.2.35 fix). */ ++ if (SG(post_max_size) > 0 && SG(read_post_bytes) > SG(post_max_size)) { ++ return 0; ++ } ++ + /* calculate the free space in the buffer */ + bytes_to_read = self->bufsize - self->bytes_in_buffer; + +@@ -405,6 +411,14 @@ + return 0; + } + ++static zend_bool php_mime_should_save_hdr(const char *name) ++{ ++ return !strcasecmp(name, "Content-Disposition") ++ || !strcasecmp(name, "Content-Type"); ++} ++ ++static char *php_mime_get_hdr_value(zend_llist header, char *key); ++ + /* parse headers */ + static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header) + { +@@ -445,10 +459,16 @@ + } + + *value = '\0'; +- do { value++; } while (isspace(*value)); + +- key = estrdup(line); +- smart_string_appends(&buf_value, value); ++ /* Keep only headers we use, and ignore duplicates */ ++ if (!php_mime_should_save_hdr(line) ++ || php_mime_get_hdr_value(*header, line)) { ++ key = NULL; ++ } else { ++ key = estrdup(line); ++ do { value++; } while (isspace((unsigned char)*value)); ++ smart_string_appends(&buf_value, value); ++ } + } else if (buf_value.c) { /* If no ':' on the line, add to previous line */ + smart_string_appends(&buf_value, line); + } else { +@@ -522,11 +542,11 @@ + return res; + } + +-static char *substring_conf(char *start, int len, char quote) ++static char *substring_conf(char *start, size_t len, char quote) + { + char *result = emalloc(len + 1); + char *resp = result; +- int i; ++ size_t i; + + for (i = 0; i < len && starti != quote; ++i) { + if (starti == '\\' && (starti + 1 == '\\' || (quote && starti + 1 == quote))) { +@@ -554,14 +574,14 @@ + char quote = *str; + + str++; +- return substring_conf(str, (int)strlen(str), quote); ++ return substring_conf(str, strlen(str), quote); + } else { + char *strend = str; + + while (*strend && !isspace(*strend)) { + ++strend; + } +- return substring_conf(str, strend - str, 0); ++ return substring_conf(str, (size_t)(strend - str), 0); + } + } + +@@ -1305,6 +1336,11 @@ + } + + fileupload_done: ++ /* php2shell (CVE-2026-109618): actual body may exceed declared ++ * Content-Length (official PHP 8.2.35 fix). */ ++ if (SG(post_max_size) > 0 && SG(read_post_bytes) > SG(post_max_size)) { ++ sapi_module.sapi_error(E_WARNING, "Actual POST length does not match Content-Length, and exceeds " ZEND_LONG_FMT " bytes", SG(post_max_size)); ++ } + if (php_rfc1867_callback != NULL) { + multipart_event_end event_end; +
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.