Commit 296fad10fb4 for php.net

commit 296fad10fb4e1a572ec05269bc798f68cddc72fa
Author: David Carlier <devnexen@gmail.com>
Date:   Thu Feb 19 20:07:44 2026 +0000

    ext/pcntl: fix pcntl_signal_dispatch() stale tail pointer and exception handling.

    close GH-21259

diff --git a/NEWS b/NEWS
index 6a1388a961c..28187bd8590 100644
--- a/NEWS
+++ b/NEWS
@@ -50,6 +50,8 @@ PHP                                                                        NEWS
     on NetBSD/Solaris platforms. (David Carlier)
   . Fixed pcntl_signal() signal table registering the callback first
     OS-wise before the internal list. (David Carlier)
+  . Fixed pcntl_signal_dispatch() stale pointer and exception
+    handling. (David Carlier)

 - PDO_PGSQL:
   . Fixed bug GH-21055 (connection attribute status typo for GSS negotiation).
diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c
index de8d7dfecfd..082bdc4ba90 100644
--- a/ext/pcntl/pcntl.c
+++ b/ext/pcntl/pcntl.c
@@ -1343,6 +1343,7 @@ void pcntl_signal_dispatch(void)

 	queue = PCNTL_G(head);
 	PCNTL_G(head) = NULL; /* simple stores are atomic */
+	PCNTL_G(tail) = NULL;

 	/* Allocate */
 	while (queue) {
@@ -1364,6 +1365,9 @@ void pcntl_signal_dispatch(void)
 #ifdef HAVE_STRUCT_SIGINFO_T
 				zval_ptr_dtor(&params[1]);
 #endif
+				if (EG(exception)) {
+					break;
+				}
 			}
 		}

@@ -1373,6 +1377,14 @@ void pcntl_signal_dispatch(void)
 		queue = next;
 	}

+	/* drain the remaining in case of exception thrown */
+	while (queue) {
+		next = queue->next;
+		queue->next = PCNTL_G(spares);
+		PCNTL_G(spares) = queue;
+		queue = next;
+	}
+
 	PCNTL_G(pending_signals) = 0;

 	/* Re-enable queue */
diff --git a/ext/pcntl/tests/pcntl_signal_dispatch_exception.phpt b/ext/pcntl/tests/pcntl_signal_dispatch_exception.phpt
new file mode 100644
index 00000000000..06c4f827c6e
--- /dev/null
+++ b/ext/pcntl/tests/pcntl_signal_dispatch_exception.phpt
@@ -0,0 +1,34 @@
+--TEST--
+pcntl_signal_dispatch() stops dispatching after handler throws exception
+--EXTENSIONS--
+pcntl
+posix
+--FILE--
+<?php
+
+$called = [];
+
+pcntl_signal(SIGUSR1, function ($signo) use (&$called) {
+    $called[] = 'SIGUSR1';
+    throw new \Exception('Exception in signal handler');
+});
+
+pcntl_signal(SIGUSR2, function ($signo) use (&$called) {
+    $called[] = 'SIGUSR2';
+});
+
+posix_kill(posix_getpid(), SIGUSR1);
+posix_kill(posix_getpid(), SIGUSR2);
+
+try {
+    pcntl_signal_dispatch();
+} catch (\Exception $e) {
+    echo $e->getMessage() . "\n";
+}
+
+echo "Handlers called: " . implode(', ', $called) . "\n";
+
+?>
+--EXPECT--
+Exception in signal handler
+Handlers called: SIGUSR1