Skip to content

Commit 2bc6025

Browse files
authored
Prevent fiber switching in tick function and signal handlers (#9028)
1 parent 928624e commit 2bc6025

File tree

6 files changed

+127
-0
lines changed

6 files changed

+127
-0
lines changed

Zend/tests/fibers/signal-async.phpt

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
Prevent switching fibers when async signals are enabled
3+
--EXTENSIONS--
4+
pcntl
5+
posix
6+
--FILE--
7+
<?php
8+
9+
pcntl_async_signals(true);
10+
11+
pcntl_signal(SIGUSR1, function (): void {
12+
if (Fiber::getCurrent() !== null) {
13+
Fiber::suspend();
14+
}
15+
});
16+
17+
$fiber = new Fiber(function (): void {
18+
echo "Fiber start\n";
19+
posix_kill(posix_getpid(), SIGUSR1);
20+
time_nanosleep(1);
21+
echo "Fiber end\n";
22+
});
23+
24+
$fiber->start();
25+
26+
?>
27+
--EXPECTF--
28+
Fiber start
29+
30+
Fatal error: Uncaught FiberError: Cannot switch fibers in current execution context in %ssignal-async.php:%d
31+
Stack trace:
32+
#0 %ssignal-async.php(%d): Fiber::suspend()
33+
#1 %ssignal-async.php(%d): {closure}(%d, Array)
34+
#2 [internal function]: {closure}()
35+
#3 %ssignal-async.php(%d): Fiber->start()
36+
#4 {main}
37+
thrown in %ssignal-async.php on line %d
+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
Prevent switching fibers when dispatching pending signals
3+
--EXTENSIONS--
4+
pcntl
5+
posix
6+
--FILE--
7+
<?php
8+
9+
pcntl_signal(SIGUSR1, function (): void {
10+
if (Fiber::getCurrent() !== null) {
11+
Fiber::suspend();
12+
}
13+
});
14+
15+
$fiber = new Fiber(function (): void {
16+
echo "Fiber start\n";
17+
18+
posix_kill(posix_getpid(), SIGUSR1);
19+
20+
try {
21+
pcntl_signal_dispatch();
22+
} catch (FiberError $e) {
23+
Fiber::suspend($e);
24+
}
25+
26+
echo "Fiber end\n";
27+
});
28+
29+
$e = $fiber->start();
30+
31+
echo $e, "\n";
32+
33+
$fiber->resume();
34+
35+
?>
36+
--EXPECTF--
37+
Fiber start
38+
FiberError: Cannot switch fibers in current execution context in %ssignal-dispatch.php:%d
39+
Stack trace:
40+
#0 %ssignal-dispatch.php(%d): Fiber::suspend()
41+
#1 [internal function]: {closure}(%d, Array)
42+
#2 %ssignal-dispatch.php(%d): pcntl_signal_dispatch()
43+
#3 [internal function]: {closure}()
44+
#4 %ssignal-dispatch.php(%d): Fiber->start()
45+
#5 {main}
46+
Fiber end

Zend/tests/fibers/ticks.phpt

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
Prevent switching fibers in tick function
3+
--FILE--
4+
<?php
5+
6+
declare(ticks=1);
7+
8+
register_tick_function(function (): void {
9+
if (Fiber::getCurrent() !== null) {
10+
Fiber::suspend();
11+
}
12+
});
13+
14+
$fiber = new Fiber(function (): void {
15+
echo "1\n";
16+
echo "2\n";
17+
echo "3\n";
18+
});
19+
20+
$fiber->start();
21+
22+
?>
23+
--EXPECTF--
24+
1
25+
26+
Fatal error: Uncaught FiberError: Cannot switch fibers in current execution context in %sticks.php:%d
27+
Stack trace:
28+
#0 %sticks.php(%d): Fiber::suspend()
29+
#1 %sticks.php(%d): {closure}()
30+
#2 [internal function]: {closure}()
31+
#3 %sticks.php(%d): Fiber->start()
32+
#4 {main}
33+
thrown in %sticks.php on line %d

Zend/zend_vm_def.h

+2
Original file line numberDiff line numberDiff line change
@@ -7752,7 +7752,9 @@ ZEND_VM_HANDLER(105, ZEND_TICKS, ANY, ANY, NUM)
77527752
EG(ticks_count) = 0;
77537753
if (zend_ticks_function) {
77547754
SAVE_OPLINE();
7755+
zend_fiber_switch_block();
77557756
zend_ticks_function(opline->extended_value);
7757+
zend_fiber_switch_unblock();
77567758
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
77577759
}
77587760
}

Zend/zend_vm_execute.h

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/pcntl/pcntl.c

+7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "pcntl_arginfo.h"
3535
#include "php_signal.h"
3636
#include "php_ticks.h"
37+
#include "zend_fibers.h"
3738

3839
#if defined(HAVE_GETPRIORITY) || defined(HAVE_SETPRIORITY) || defined(HAVE_WAIT3)
3940
#include <sys/wait.h>
@@ -1410,6 +1411,9 @@ void pcntl_signal_dispatch()
14101411
return;
14111412
}
14121413

1414+
/* Prevent switching fibers when handling signals */
1415+
zend_fiber_switch_block();
1416+
14131417
/* Prevent reentrant handler calls */
14141418
PCNTL_G(processing_signal_queue) = 1;
14151419

@@ -1450,6 +1454,9 @@ void pcntl_signal_dispatch()
14501454
/* Re-enable queue */
14511455
PCNTL_G(processing_signal_queue) = 0;
14521456

1457+
/* Re-enable fiber switching */
1458+
zend_fiber_switch_unblock();
1459+
14531460
/* return signal mask to previous state */
14541461
sigprocmask(SIG_SETMASK, &old_mask, NULL);
14551462
}

0 commit comments

Comments
 (0)