|
31 | 31 | #include "ext/standard/info.h" |
32 | 32 | #include "php_signal.h" |
33 | 33 | #include "php_ticks.h" |
| 34 | +#include "zend_exceptions.h" |
34 | 35 | #include "zend_fibers.h" |
35 | 36 |
|
36 | 37 | #if defined(HAVE_GETPRIORITY) || defined(HAVE_SETPRIORITY) || defined(HAVE_WAIT3) |
@@ -1318,6 +1319,9 @@ void pcntl_signal_dispatch(void) |
1318 | 1319 | { |
1319 | 1320 | zval params[2], *handle, retval; |
1320 | 1321 | struct php_pcntl_pending_signal *queue, *next; |
| 1322 | + zend_object *old_exception; |
| 1323 | + const zend_op *old_opline_before_exception = NULL; |
| 1324 | + const zend_op *old_opline = NULL; |
1321 | 1325 | sigset_t mask; |
1322 | 1326 | sigset_t old_mask; |
1323 | 1327 |
|
@@ -1345,6 +1349,20 @@ void pcntl_signal_dispatch(void) |
1345 | 1349 | PCNTL_G(head) = NULL; /* simple stores are atomic */ |
1346 | 1350 | PCNTL_G(tail) = NULL; |
1347 | 1351 |
|
| 1352 | + /* Dispatching can happen with an exception pending, e.g. from the interrupt check that runs |
| 1353 | + * right after an internal function threw. call_user_function() does nothing in that state, |
| 1354 | + * so set the exception aside while the handlers run. The frame is left as found: depending |
| 1355 | + * on the caller, the exception may not be registered on it yet, or may already be on its |
| 1356 | + * way to a catch block, and the caller takes it from there once we return. */ |
| 1357 | + old_exception = EG(exception); |
| 1358 | + if (old_exception) { |
| 1359 | + if (EG(current_execute_data)) { |
| 1360 | + old_opline = EG(current_execute_data)->opline; |
| 1361 | + } |
| 1362 | + old_opline_before_exception = EG(opline_before_exception); |
| 1363 | + EG(exception) = NULL; |
| 1364 | + } |
| 1365 | + |
1348 | 1366 | /* Allocate */ |
1349 | 1367 | while (queue) { |
1350 | 1368 | if ((handle = zend_hash_index_find(&PCNTL_G(php_signal_table), queue->signo)) != NULL) { |
@@ -1385,6 +1403,18 @@ void pcntl_signal_dispatch(void) |
1385 | 1403 | queue = next; |
1386 | 1404 | } |
1387 | 1405 |
|
| 1406 | + if (old_exception) { |
| 1407 | + if (EG(current_execute_data)) { |
| 1408 | + EG(current_execute_data)->opline = old_opline; |
| 1409 | + } |
| 1410 | + EG(opline_before_exception) = old_opline_before_exception; |
| 1411 | + if (EG(exception)) { |
| 1412 | + zend_exception_set_previous(EG(exception), old_exception); |
| 1413 | + } else { |
| 1414 | + EG(exception) = old_exception; |
| 1415 | + } |
| 1416 | + } |
| 1417 | + |
1388 | 1418 | PCNTL_G(pending_signals) = 0; |
1389 | 1419 |
|
1390 | 1420 | /* Re-enable queue */ |
|
0 commit comments