From 290c9aef5628a31bdfd01b42a3a6625c1b1bc500 Mon Sep 17 00:00:00 2001
From: Dmitry Stogov <dmitry@php.net>
Date: Wed, 13 Aug 2025 13:46:14 +0300
Subject: [PATCH 1/5] Eliminate useless spill stores (#19467)

---
 ext/opcache/jit/zend_jit_ir.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c
index b1ebecc84a70..3ed9a5114756 100644
--- a/ext/opcache/jit/zend_jit_ir.c
+++ b/ext/opcache/jit/zend_jit_ir.c
@@ -4177,6 +4177,11 @@ static int zend_jit_spill_store_inv(zend_jit_ctx *jit, zend_jit_addr src, zend_j
 	ZEND_ASSERT(Z_MODE(src) == IS_REG);
 	ZEND_ASSERT(Z_MODE(dst) == IS_MEM_ZVAL);
 
+	if (Z_LOAD(src) || Z_STORE(src)) {
+		/* it's not necessary to store register if it was previously loaded or already stored */
+		return 1;
+	}
+
 	if ((info & MAY_BE_ANY) == MAY_BE_LONG) {
 		jit_set_Z_LVAL(jit, dst, zend_jit_use_reg(jit, src));
 		if (Z_REG(dst) != ZREG_FP || !JIT_G(current_frame)) {

From bf64dfcd99f2f14753d8301dd3d4a85cdd53afc7 Mon Sep 17 00:00:00 2001
From: Alexandre Daubois <2144837+alexandre-daubois@users.noreply.github.com>
Date: Wed, 13 Aug 2025 13:59:01 +0200
Subject: [PATCH 2/5] Add an enum for HASH_KEY_IS_* constants (GH-19376)

---
 UPGRADING.INTERNALS          |  1 +
 Zend/zend_hash.c             |  4 ++--
 Zend/zend_hash.h             | 16 +++++++++-------
 Zend/zend_weakrefs.c         |  2 +-
 ext/com_dotnet/com_variant.c |  2 +-
 ext/com_dotnet/com_wrapper.c |  2 +-
 6 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS
index e8a5a4ca7831..b7396941e502 100644
--- a/UPGRADING.INTERNALS
+++ b/UPGRADING.INTERNALS
@@ -79,6 +79,7 @@ PHP 8.5 INTERNALS UPGRADE NOTES
     delayed. Before, errors would be recorded but not delayed.
   . zend_mm_refresh_key_child() must be called on any zend_mm_heap inherited
     from the parent process after a fork().
+  . HASH_KEY_IS_* constants have been moved in the zend_hash_key_type enum.
 
 - standard
   . ext/standard/php_smart_string.h and ext/standard/php_smart_string_public.h
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c
index 8d3f4d1b6881..f48c298f167e 100644
--- a/Zend/zend_hash.c
+++ b/Zend/zend_hash.c
@@ -2842,7 +2842,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_hash_move_backwards_ex(const HashTable *
 
 
 /* This function should be made binary safe  */
-ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_ex(const HashTable *ht, zend_string **str_index, zend_ulong *num_index, const HashPosition *pos)
+ZEND_API zend_hash_key_type ZEND_FASTCALL zend_hash_get_current_key_ex(const HashTable *ht, zend_string **str_index, zend_ulong *num_index, const HashPosition *pos)
 {
 	uint32_t idx;
 	Bucket *p;
@@ -2889,7 +2889,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_get_current_key_zval_ex(const HashTable *h
 	}
 }
 
-ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_type_ex(const HashTable *ht, const HashPosition *pos)
+ZEND_API zend_hash_key_type ZEND_FASTCALL zend_hash_get_current_key_type_ex(const HashTable *ht, const HashPosition *pos)
 {
 	uint32_t idx;
 	Bucket *p;
diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h
index b2aaecce0d27..71206e61550b 100644
--- a/Zend/zend_hash.h
+++ b/Zend/zend_hash.h
@@ -26,9 +26,11 @@
 #include "zend_string.h"
 #include "zend_sort.h"
 
-#define HASH_KEY_IS_STRING 1
-#define HASH_KEY_IS_LONG 2
-#define HASH_KEY_NON_EXISTENT 3
+typedef enum {
+	HASH_KEY_IS_STRING = 1,
+	HASH_KEY_IS_LONG,
+	HASH_KEY_NON_EXISTENT
+} zend_hash_key_type;
 
 #define HASH_UPDATE 			(1<<0) /* Create new entry, or update the existing one. */
 #define HASH_ADD				(1<<1) /* Create new entry, or fail if it exists. */
@@ -251,9 +253,9 @@ ZEND_API HashPosition ZEND_FASTCALL zend_hash_get_current_pos(const HashTable *h
 
 ZEND_API zend_result   ZEND_FASTCALL zend_hash_move_forward_ex(const HashTable *ht, HashPosition *pos);
 ZEND_API zend_result   ZEND_FASTCALL zend_hash_move_backwards_ex(const HashTable *ht, HashPosition *pos);
-ZEND_API int   ZEND_FASTCALL zend_hash_get_current_key_ex(const HashTable *ht, zend_string **str_index, zend_ulong *num_index, const HashPosition *pos);
+ZEND_API zend_hash_key_type  ZEND_FASTCALL zend_hash_get_current_key_ex(const HashTable *ht, zend_string **str_index, zend_ulong *num_index, const HashPosition *pos);
 ZEND_API void  ZEND_FASTCALL zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, const HashPosition *pos);
-ZEND_API int   ZEND_FASTCALL zend_hash_get_current_key_type_ex(const HashTable *ht, const HashPosition *pos);
+ZEND_API zend_hash_key_type ZEND_FASTCALL zend_hash_get_current_key_type_ex(const HashTable *ht, const HashPosition *pos);
 ZEND_API zval* ZEND_FASTCALL zend_hash_get_current_data_ex(const HashTable *ht, const HashPosition *pos);
 ZEND_API void  ZEND_FASTCALL zend_hash_internal_pointer_reset_ex(const HashTable *ht, HashPosition *pos);
 ZEND_API void  ZEND_FASTCALL zend_hash_internal_pointer_end_ex(const HashTable *ht, HashPosition *pos);
@@ -270,13 +272,13 @@ static zend_always_inline zend_result zend_hash_move_forward(HashTable *ht) {
 static zend_always_inline zend_result zend_hash_move_backwards(HashTable *ht) {
 	return zend_hash_move_backwards_ex(ht, &ht->nInternalPointer);
 }
-static zend_always_inline int zend_hash_get_current_key(const HashTable *ht, zend_string **str_index, zend_ulong *num_index) {
+static zend_always_inline zend_hash_key_type zend_hash_get_current_key(const HashTable *ht, zend_string **str_index, zend_ulong *num_index) {
 	return zend_hash_get_current_key_ex(ht, str_index, num_index, &ht->nInternalPointer);
 }
 static zend_always_inline void zend_hash_get_current_key_zval(const HashTable *ht, zval *key) {
 	zend_hash_get_current_key_zval_ex(ht, key, &ht->nInternalPointer);
 }
-static zend_always_inline int zend_hash_get_current_key_type(const HashTable *ht) {
+static zend_always_inline zend_hash_key_type zend_hash_get_current_key_type(const HashTable *ht) {
 	return zend_hash_get_current_key_type_ex(ht, &ht->nInternalPointer);
 }
 static zend_always_inline zval* zend_hash_get_current_data(const HashTable *ht) {
diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c
index cf363cd12595..fb564a99e252 100644
--- a/Zend/zend_weakrefs.c
+++ b/Zend/zend_weakrefs.c
@@ -648,7 +648,7 @@ static void zend_weakmap_iterator_get_current_key(zend_object_iterator *obj_iter
 
 	zend_string *string_key;
 	zend_ulong num_key;
-	int key_type = zend_hash_get_current_key_ex(&wm->ht, &string_key, &num_key, pos);
+	zend_hash_key_type key_type = zend_hash_get_current_key_ex(&wm->ht, &string_key, &num_key, pos);
 	if (key_type == HASH_KEY_NON_EXISTENT) {
 		ZVAL_NULL(key);
 		return;
diff --git a/ext/com_dotnet/com_variant.c b/ext/com_dotnet/com_variant.c
index 23cbd078fb47..7e63d66b3d3a 100644
--- a/ext/com_dotnet/com_variant.c
+++ b/ext/com_dotnet/com_variant.c
@@ -32,7 +32,7 @@ static void safe_array_from_zval(VARIANT *v, zval *z, int codepage)
 	SAFEARRAY *sa = NULL;
 	SAFEARRAYBOUND bound;
 	HashPosition pos;
-	int keytype;
+	zend_hash_key_type keytype;
 	zend_string *strindex;
 	zend_ulong intindex = 0;
 	VARIANT *va;
diff --git a/ext/com_dotnet/com_wrapper.c b/ext/com_dotnet/com_wrapper.c
index 6e885fa802e9..3d05e17affae 100644
--- a/ext/com_dotnet/com_wrapper.c
+++ b/ext/com_dotnet/com_wrapper.c
@@ -423,7 +423,7 @@ static void generate_dispids(php_dispatchex *disp)
 	HashPosition pos;
 	zend_string *name = NULL;
 	zval *tmp, tmp2;
-	int keytype;
+	zend_hash_key_type keytype;
 	zend_long pid;
 
 	if (disp->dispid_to_name == NULL) {

From c8774f9e617173fd1486282c7ef4e99d8290b48f Mon Sep 17 00:00:00 2001
From: Alexandre Daubois <alex.daubois@gmail.com>
Date: Thu, 7 Aug 2025 09:41:50 +0200
Subject: [PATCH 3/5] Fix GH-16649: Avoid UAF when using array_splice

Closes GH-19399
---
 NEWS                                          |  3 ++
 ext/standard/array.c                          | 12 +++++++
 .../array_splice_normal_destructor.phpt       | 20 +++++++++++
 .../array_splice_uaf_add_elements.phpt        | 22 +++++++++++++
 .../array_splice_uaf_array_deallocated.phpt   | 22 +++++++++++++
 ...array_splice_uaf_complex_modification.phpt | 25 ++++++++++++++
 ...array_splice_uaf_multiple_destructors.phpt | 33 +++++++++++++++++++
 .../array_splice_uaf_original_case.phpt       | 29 ++++++++++++++++
 .../array_splice_uaf_packed_to_hash.phpt      | 23 +++++++++++++
 .../array_splice_with_replacement.phpt        | 23 +++++++++++++
 10 files changed, 212 insertions(+)
 create mode 100644 ext/standard/tests/array/gh16649/array_splice_normal_destructor.phpt
 create mode 100644 ext/standard/tests/array/gh16649/array_splice_uaf_add_elements.phpt
 create mode 100644 ext/standard/tests/array/gh16649/array_splice_uaf_array_deallocated.phpt
 create mode 100644 ext/standard/tests/array/gh16649/array_splice_uaf_complex_modification.phpt
 create mode 100644 ext/standard/tests/array/gh16649/array_splice_uaf_multiple_destructors.phpt
 create mode 100644 ext/standard/tests/array/gh16649/array_splice_uaf_original_case.phpt
 create mode 100644 ext/standard/tests/array/gh16649/array_splice_uaf_packed_to_hash.phpt
 create mode 100644 ext/standard/tests/array/gh16649/array_splice_with_replacement.phpt

diff --git a/NEWS b/NEWS
index de12a9a775e4..998ef70e1d4e 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,9 @@ PHP                                                                        NEWS
   . Fixed bug GH-19245 (Success error message on TLS stream accept failure).
     (Jakub Zelenka)
 
+- Standard:
+  . Fixed bug GH-16649 (UAF during array_splice). (alexandre-daubois)
+
 28 Aug 2025, PHP 8.3.25
 
 - Core:
diff --git a/ext/standard/array.c b/ext/standard/array.c
index a2a0459ec3b2..f0a81b5ba895 100644
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@ -3214,6 +3214,9 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H
 	zval		*entry;				/* Hash entry */
 	uint32_t    iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
 
+	GC_ADDREF(in_hash);
+	HT_ALLOW_COW_VIOLATION(in_hash); /* Will be reset when setting the flags for in_hash */
+
 	/* Get number of entries in the input hash */
 	num_in = zend_hash_num_elements(in_hash);
 
@@ -3372,6 +3375,15 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H
 	HT_SET_ITERATORS_COUNT(&out_hash, HT_ITERATORS_COUNT(in_hash));
 	HT_SET_ITERATORS_COUNT(in_hash, 0);
 	in_hash->pDestructor = NULL;
+
+	if (UNEXPECTED(GC_DELREF(in_hash) == 0)) {
+		/* Array was completely deallocated during the operation */
+		zend_array_destroy(in_hash);
+		zend_hash_destroy(&out_hash);
+		zend_throw_error(NULL, "Array was modified during array_splice operation");
+		return;
+	}
+
 	zend_hash_destroy(in_hash);
 
 	HT_FLAGS(in_hash)          = HT_FLAGS(&out_hash);
diff --git a/ext/standard/tests/array/gh16649/array_splice_normal_destructor.phpt b/ext/standard/tests/array/gh16649/array_splice_normal_destructor.phpt
new file mode 100644
index 000000000000..59c0b316f54d
--- /dev/null
+++ b/ext/standard/tests/array/gh16649/array_splice_normal_destructor.phpt
@@ -0,0 +1,20 @@
+--TEST--
+GH-16649: array_splice with normal destructor should work fine
+--FILE--
+<?php
+class C {
+    function __destruct() {
+        echo "Destructor called\n";
+    }
+}
+
+$arr = ["1", new C, "2"];
+array_splice($arr, 1, 2);
+var_dump($arr);
+?>
+--EXPECT--
+Destructor called
+array(1) {
+  [0]=>
+  string(1) "1"
+}
diff --git a/ext/standard/tests/array/gh16649/array_splice_uaf_add_elements.phpt b/ext/standard/tests/array/gh16649/array_splice_uaf_add_elements.phpt
new file mode 100644
index 000000000000..f5e538122f41
--- /dev/null
+++ b/ext/standard/tests/array/gh16649/array_splice_uaf_add_elements.phpt
@@ -0,0 +1,22 @@
+--TEST--
+GH-16649: array_splice UAF when destructor adds elements to array
+--FILE--
+<?php
+class C {
+    function __destruct() {
+        global $arr;
+        $arr[] = 0;
+    }
+}
+
+$arr = ["1", new C, "2"];
+
+try {
+    array_splice($arr, 1, 2);
+    echo "ERROR: Should have thrown exception\n";
+} catch (Error $e) {
+    echo "Exception caught: " . $e->getMessage() . "\n";
+}
+?>
+--EXPECT--
+Exception caught: Array was modified during array_splice operation
diff --git a/ext/standard/tests/array/gh16649/array_splice_uaf_array_deallocated.phpt b/ext/standard/tests/array/gh16649/array_splice_uaf_array_deallocated.phpt
new file mode 100644
index 000000000000..1874ddad8934
--- /dev/null
+++ b/ext/standard/tests/array/gh16649/array_splice_uaf_array_deallocated.phpt
@@ -0,0 +1,22 @@
+--TEST--
+GH-16649: array_splice UAF when array is released entirely
+--FILE--
+<?php
+class C {
+    function __destruct() {
+        global $arr;
+        $arr = null;
+    }
+}
+
+$arr = ["1", new C, "2"];
+
+try {
+    array_splice($arr, 1, 2);
+    echo "ERROR: Should have thrown exception\n";
+} catch (Error $e) {
+    echo "Exception caught: " . $e->getMessage() . "\n";
+}
+?>
+--EXPECT--
+Exception caught: Array was modified during array_splice operation
diff --git a/ext/standard/tests/array/gh16649/array_splice_uaf_complex_modification.phpt b/ext/standard/tests/array/gh16649/array_splice_uaf_complex_modification.phpt
new file mode 100644
index 000000000000..8ad4133300ec
--- /dev/null
+++ b/ext/standard/tests/array/gh16649/array_splice_uaf_complex_modification.phpt
@@ -0,0 +1,25 @@
+--TEST--
+GH-16649: array_splice UAF with complex array modification
+--FILE--
+<?php
+class ComplexModifier {
+    function __destruct() {
+        global $arr;
+        // complex modification that causes cow
+        unset($arr[0]);
+        $arr["new_key"] = "new_value";
+        $arr[100] = "another_value";
+    }
+}
+
+$arr = ["first", new ComplexModifier, "last"];
+
+try {
+    array_splice($arr, 1, 1, ["replacement"]);
+    echo "ERROR: Should have thrown exception\n";
+} catch (Error $e) {
+    echo "Exception caught: " . $e->getMessage() . "\n";
+}
+?>
+--EXPECT--
+Exception caught: Array was modified during array_splice operation
diff --git a/ext/standard/tests/array/gh16649/array_splice_uaf_multiple_destructors.phpt b/ext/standard/tests/array/gh16649/array_splice_uaf_multiple_destructors.phpt
new file mode 100644
index 000000000000..9e2c6cf8fc71
--- /dev/null
+++ b/ext/standard/tests/array/gh16649/array_splice_uaf_multiple_destructors.phpt
@@ -0,0 +1,33 @@
+--TEST--
+GH-16649: array_splice UAF with multiple destructors
+--FILE--
+<?php
+class MultiDestructor {
+    public $id;
+
+    function __construct($id) {
+        $this->id = $id;
+    }
+
+    function __destruct() {
+        global $arr;
+        echo "Destructor {$this->id} called\n";
+        if ($this->id == 2) {
+            $arr = null;
+        }
+    }
+}
+
+$arr = ["start", new MultiDestructor(1), new MultiDestructor(2), "end"];
+
+try {
+    array_splice($arr, 1, 2);
+    echo "ERROR: Should have thrown exception\n";
+} catch (Error $e) {
+    echo "Exception caught: " . $e->getMessage() . "\n";
+}
+?>
+--EXPECT--
+Destructor 1 called
+Destructor 2 called
+Exception caught: Array was modified during array_splice operation
diff --git a/ext/standard/tests/array/gh16649/array_splice_uaf_original_case.phpt b/ext/standard/tests/array/gh16649/array_splice_uaf_original_case.phpt
new file mode 100644
index 000000000000..4a82d5893157
--- /dev/null
+++ b/ext/standard/tests/array/gh16649/array_splice_uaf_original_case.phpt
@@ -0,0 +1,29 @@
+--TEST--
+GH-16649: array_splice UAF with destructor modifying array (original case)
+--FILE--
+<?php
+function resize_arr() {
+    global $arr;
+    for ($i = 0; $i < 10; $i++) {
+        $arr[$i] = $i;
+    }
+}
+
+class C {
+    function __destruct() {
+        resize_arr();
+        return "3";
+    }
+}
+
+$arr = ["a" => "1", "3" => new C, "2" => "2"];
+
+try {
+    array_splice($arr, 1, 2);
+    echo "ERROR: Should have thrown exception\n";
+} catch (Error $e) {
+    echo "Exception caught: " . $e->getMessage() . "\n";
+}
+?>
+--EXPECT--
+Exception caught: Array was modified during array_splice operation
diff --git a/ext/standard/tests/array/gh16649/array_splice_uaf_packed_to_hash.phpt b/ext/standard/tests/array/gh16649/array_splice_uaf_packed_to_hash.phpt
new file mode 100644
index 000000000000..bd8f511b6253
--- /dev/null
+++ b/ext/standard/tests/array/gh16649/array_splice_uaf_packed_to_hash.phpt
@@ -0,0 +1,23 @@
+--TEST--
+GH-16649: array_splice UAF when array is converted from packed to hash
+--FILE--
+<?php
+class C {
+    function __destruct() {
+        global $arr;
+        // array is converted from packed to hash
+        $arr["str"] = 0;
+    }
+}
+
+$arr = ["1", new C, "2"];
+
+try {
+    array_splice($arr, 1, 2);
+    echo "ERROR: Should have thrown exception\n";
+} catch (Error $e) {
+    echo "Exception caught: " . $e->getMessage() . "\n";
+}
+?>
+--EXPECT--
+Exception caught: Array was modified during array_splice operation
diff --git a/ext/standard/tests/array/gh16649/array_splice_with_replacement.phpt b/ext/standard/tests/array/gh16649/array_splice_with_replacement.phpt
new file mode 100644
index 000000000000..8754a913a24e
--- /dev/null
+++ b/ext/standard/tests/array/gh16649/array_splice_with_replacement.phpt
@@ -0,0 +1,23 @@
+--TEST--
+GH-16649: array_splice with replacement array when destructor modifies array
+--FILE--
+<?php
+class C {
+    function __destruct() {
+        global $arr;
+        $arr["modified"] = "by_destructor";
+    }
+}
+
+$arr = ["a", new C, "b"];
+$replacement = ["replacement1", "replacement2"];
+
+try {
+    array_splice($arr, 1, 1, $replacement);
+    echo "ERROR: Should have thrown exception\n";
+} catch (Error $e) {
+    echo "Exception caught: " . $e->getMessage() . "\n";
+}
+?>
+--EXPECT--
+Exception caught: Array was modified during array_splice operation

From cbb9ee8f5b1976c19fd0070f242cd8c798083bd5 Mon Sep 17 00:00:00 2001
From: Dmitry Stogov <dmitry@php.net>
Date: Wed, 13 Aug 2025 15:58:04 +0300
Subject: [PATCH 4/5] Added test for PR #19458

Thanks to @DanielEScherzer
---
 ext/opcache/tests/jit/assign_obj_006.phpt | 37 +++++++++++++++++++++++
 1 file changed, 37 insertions(+)
 create mode 100644 ext/opcache/tests/jit/assign_obj_006.phpt

diff --git a/ext/opcache/tests/jit/assign_obj_006.phpt b/ext/opcache/tests/jit/assign_obj_006.phpt
new file mode 100644
index 000000000000..a48299f04015
--- /dev/null
+++ b/ext/opcache/tests/jit/assign_obj_006.phpt
@@ -0,0 +1,37 @@
+--TEST--
+JIT ASSIGN_OBJ: violation of dominance
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit_buffer_size=1M
+--FILE--
+<?php
+class CacheItem {
+    protected mixed $value = null;
+}
+
+function test($value) {
+    $test = Closure::bind(
+        static function ($value) {
+            $item = new CacheItem();
+            if (\is_array($value) && \array_key_exists('value', $value)) {
+                $item->value = $value['value'];
+            }
+            return $item;
+        },
+        null,
+        CacheItem::class);
+    return $test($value);
+}
+
+$values = [['value'=>'str'], ['value'=>'str'], ['value'=>42]];
+$n = count($values);
+
+for ($i = 0; $i < $n; $i++) {
+   test($values[$i]);
+}
+?>
+OK
+--EXPECT--
+OK

From 2650248a927224aa5b76130d406a691ad774fd38 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= <tim@bastelstu.be>
Date: Wed, 13 Aug 2025 19:06:09 +0200
Subject: [PATCH 5/5] Update GitHub Action workflows to `actions/checkout@v5`

Keep this up to date in all non-security-only branches, because the node.js
runtime for older versions might get deprecated in the future and fixing this
for all branches at once is easier.

see 45e60e585eed63353301f797c75da4e553ce21ff
---
 .github/workflows/nightly.yml | 38 +++++++++++++++++------------------
 .github/workflows/push.yml    |  8 ++++----
 .github/workflows/root.yml    |  2 +-
 3 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index ffa43fe744a2..62ea79650212 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -54,7 +54,7 @@ jobs:
     runs-on: [self-hosted, gentoo, ppc64]
     steps:
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           ref: ${{ inputs.branch }}
       - name: System info
@@ -95,7 +95,7 @@ jobs:
       image: 'alpine:3.20.1'
     steps:
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           ref: ${{ inputs.branch }}
       - name: apk
@@ -207,7 +207,7 @@ jobs:
     runs-on: ubuntu-${{ matrix.asan && inputs.asan_ubuntu_version || inputs.ubuntu_version }}
     steps:
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           ref: ${{ inputs.branch }}
       - name: Create MSSQL container
@@ -310,7 +310,7 @@ jobs:
           FIREBIRD_PASSWORD: test
     steps:
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           ref: ${{ inputs.branch }}
       - name: apt
@@ -381,7 +381,7 @@ jobs:
     runs-on: macos-${{ matrix.os }}
     steps:
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           ref: ${{ inputs.branch }}
       - name: brew
@@ -459,7 +459,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           ref: ${{ inputs.branch }}
       - name: Create MSSQL container
@@ -513,7 +513,7 @@ jobs:
       USE_TRACKED_ALLOC: 1
     steps:
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           ref: ${{ inputs.branch }}
       - name: apt
@@ -717,7 +717,7 @@ jobs:
     runs-on: ubuntu-${{ inputs.ubuntu_version }}
     steps:
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           ref: ${{ inputs.branch }}
       - name: Create MSSQL container
@@ -785,7 +785,7 @@ jobs:
     runs-on: ubuntu-${{ inputs.ubuntu_version }}
     steps:
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           ref: ${{ inputs.branch }}
       - name: apt
@@ -877,7 +877,7 @@ jobs:
     runs-on: ubuntu-${{ inputs.ubuntu_version }}
     steps:
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           ref: ${{ inputs.branch }}
       - name: apt
@@ -927,38 +927,38 @@ jobs:
       CXX: ccache g++
     steps:
       - name: git checkout PHP
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           path: php
           ref: ${{ inputs.branch }}
       - name: git checkout apcu
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           repository: krakjoe/apcu
           path: apcu
       - name: git checkout imagick
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           repository: Imagick/imagick
           path: imagick
       - name: git checkout memcached
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           repository: php-memcached-dev/php-memcached
           path: memcached
       - name: git checkout redis
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           repository: phpredis/phpredis
           path: redis
       - name: git checkout xdebug
         if: false
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           repository: xdebug/xdebug
           path: xdebug
       - name: git checkout yaml
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           repository: php/pecl-file_formats-yaml
           path: yaml
@@ -1068,7 +1068,7 @@ jobs:
       - name: git config
         run: git config --global core.autocrlf false && git config --global core.eol lf
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           ref: ${{ inputs.branch }}
       - name: Setup
@@ -1089,7 +1089,7 @@ jobs:
     timeout-minutes: 50
     steps:
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           ref: ${{ inputs.branch }}
       - name: FreeBSD
diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml
index 29fc841dd4e7..028255330d1f 100644
--- a/.github/workflows/push.yml
+++ b/.github/workflows/push.yml
@@ -71,7 +71,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
       - name: Create MSSQL container
         uses: ./.github/actions/setup-mssql
       - name: Create Oracle container
@@ -116,7 +116,7 @@ jobs:
     runs-on: macos-14
     steps:
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
       - name: brew
         uses: ./.github/actions/brew
       - name: ccache
@@ -163,7 +163,7 @@ jobs:
       - name: git config
         run: git config --global core.autocrlf false && git config --global core.eol lf
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
       - name: Setup
         uses: ./.github/actions/setup-windows
       - name: Build
@@ -177,6 +177,6 @@ jobs:
     timeout-minutes: 50
     steps:
       - name: git checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
       - name: FreeBSD
         uses: ./.github/actions/freebsd
diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml
index 96943a8cfb2a..16418b1aa1d3 100644
--- a/.github/workflows/root.yml
+++ b/.github/workflows/root.yml
@@ -13,7 +13,7 @@ jobs:
     outputs:
       branches: ${{ steps.set-matrix.outputs.branches }}
     steps:
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@v5
         with:
           # Set fetch-depth to 0 to clone the full repository
           # including all branches. This is required to find
