From 01ee1225dc9c6c8c319414d60761e9e4c66db739 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Sat, 7 May 2005 14:55:39 +0000 Subject: [PATCH] Fix #32974 --- NEWS | 1 + ext/pcntl/pcntl.c | 100 +++++++++++++++++++++++++-------------- ext/pcntl/php_pcntl.h | 9 +++- ext/pcntl/test-pcntl.php | 7 +-- 4 files changed, 76 insertions(+), 41 deletions(-) diff --git a/NEWS b/NEWS index 3071c02baf..dd8d6ab993 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ PHP 4 NEWS them sort based on the current locale. (Derick) - Changed sha1_file() and md5_file() functions to use streams instead of low level IO. (Uwe) +- Fixed bug #32974 (pcntl calls malloc() from a signal handler). (Wez) - Fixed bug #32936 (http redirects URLs are not checked for control chars). (Ilia) - Fixed bug #32813 (parse_url() does not handle scheme-only urls properly). (Ilia) - Fixed bug #32802 (General cookie overrides more specific cookie). (Ilia) diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 959383c2fa..ce0e083dc6 100755 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -139,17 +139,13 @@ void php_register_signal_constants(INIT_FUNC_ARGS) static void php_pcntl_init_globals(zend_pcntl_globals *pcntl_globals) { - /* Just in case ... */ - memset(&pcntl_globals->php_signal_queue,0,sizeof(pcntl_globals->php_signal_queue)); - - zend_llist_init(&pcntl_globals->php_signal_queue, sizeof (long), NULL, 1); - pcntl_globals->signal_queue_ready = 0; - pcntl_globals->processing_signal_queue = 0; + memset(pcntl_globals, 0, sizeof(*pcntl_globals)); } PHP_RINIT_FUNCTION(pcntl) { - zend_hash_init(&PCNTL_G(php_signal_table), 16, NULL, ZVAL_PTR_DTOR, 1); + zend_hash_init(&PCNTL_G(php_signal_table), 16, NULL, ZVAL_PTR_DTOR, 0); + PCNTL_G(head) = PCNTL_G(tail) = PCNTL_G(spares) = NULL; return SUCCESS; } @@ -164,13 +160,26 @@ PHP_MINIT_FUNCTION(pcntl) PHP_MSHUTDOWN_FUNCTION(pcntl) { - zend_llist_destroy(&PCNTL_G(php_signal_queue)); return SUCCESS; } PHP_RSHUTDOWN_FUNCTION(pcntl) { + struct php_pcntl_pending_signal *sig; + + /* FIXME: if a signal is delivered after this point, things will go pear shaped; + * need to remove signal handlers */ zend_hash_destroy(&PCNTL_G(php_signal_table)); + while (PCNTL_G(head)) { + sig = PCNTL_G(head); + PCNTL_G(head) = sig->next; + efree(sig); + } + while (PCNTL_G(spares)) { + sig = PCNTL_G(spares); + PCNTL_G(spares) = sig->next; + efree(sig); + } return SUCCESS; } @@ -467,6 +476,19 @@ PHP_FUNCTION(pcntl_signal) return; } + if (!PCNTL_G(spares)) { + /* since calling malloc() from within a signal handler is not portable, + * pre-allocate a few records for recording signals */ + int i; + for (i = 0; i < 32; i++) { + struct php_pcntl_pending_signal *psig; + + psig = emalloc(sizeof(*psig)); + psig->next = PCNTL_G(spares); + PCNTL_G(spares) = psig; + } + } + /* Special long value case for SIG_DFL and SIG_IGN */ if (Z_TYPE_P(handle)==IS_LONG) { if (Z_LVAL_P(handle)!= (long) SIG_DFL && Z_LVAL_P(handle) != (long) SIG_IGN) { @@ -501,57 +523,63 @@ PHP_FUNCTION(pcntl_signal) /* Our custom signal handler that calls the appropriate php_function */ static void pcntl_signal_handler(int signo) { - long signal_num = signo; + struct php_pcntl_pending_signal *psig; TSRMLS_FETCH(); - IF_DEBUG(DEBUG_OUT("Caught signo %d\n", signo)); - if (! PCNTL_G(processing_signal_queue)) { - zend_llist_add_element(&PCNTL_G(php_signal_queue), &signal_num); - PCNTL_G(signal_queue_ready) = 1; - IF_DEBUG(DEBUG_OUT("Added queue entry\n")); + psig = PCNTL_G(spares); + if (!psig) { + /* oops, too many signals for us to track, so we'll forget about this one */ + return; + } + PCNTL_G(spares) = psig->next; + + psig->signo = signo; + psig->next = NULL; + + /* the head check is important, as the tick handler cannot atomically clear both + * the head and tail */ + if (PCNTL_G(head) && PCNTL_G(tail)) { + PCNTL_G(tail)->next = psig; + } else { + PCNTL_G(head) = psig; } - return; + PCNTL_G(tail) = psig; } void pcntl_tick_handler() { - zend_llist_element *element; zval *param, **handle, *retval; + struct php_pcntl_pending_signal *queue, *next; TSRMLS_FETCH(); /* Bail if the queue is empty or if we are already playing the queue*/ - if (! PCNTL_G(signal_queue_ready) || PCNTL_G(processing_signal_queue)) + if (! PCNTL_G(head) || PCNTL_G(processing_signal_queue)) return; - /* Mark our queue empty */ - PCNTL_G(signal_queue_ready) = 0; - - /* If for some reason our signal queue is empty then return */ - if (zend_llist_count(&PCNTL_G(php_signal_queue)) <= 0) { - return; - } - /* Prevent reentrant handler calls */ PCNTL_G(processing_signal_queue) = 1; + queue = PCNTL_G(head); + PCNTL_G(head) = NULL; /* simple stores are atomic */ + /* Allocate */ MAKE_STD_ZVAL(param); MAKE_STD_ZVAL(retval); - /* Traverse through our signal queue and call the appropriate php functions */ - for (element = (&PCNTL_G(php_signal_queue))->head; element; element = element->next) { - long *signal_num = (long *)&element->data; - if (zend_hash_index_find(&PCNTL_G(php_signal_table), *signal_num, (void **) &handle)==FAILURE) { - continue; + while (queue) { + if (zend_hash_index_find(&PCNTL_G(php_signal_table), queue->signo, (void **) &handle)==SUCCESS) { + ZVAL_LONG(param, queue->signo); + + /* Call php signal handler - Note that we do not report errors, and we ignore the return value */ + /* FIXME: this is probably broken when multiple signals are handled in this while loop (retval) */ + call_user_function(EG(function_table), NULL, *handle, retval, 1, ¶m TSRMLS_CC); } - ZVAL_LONG(param, *signal_num); - - /* Call php signal handler - Note that we do not report errors, and we ignore the eturn value */ - call_user_function(EG(function_table), NULL, *handle, retval, 1, ¶m TSRMLS_CC); + next = queue->next; + queue->next = PCNTL_G(spares); + PCNTL_G(spares) = queue; + queue = next; } - /* Clear */ - zend_llist_clean(&PCNTL_G(php_signal_queue)); /* Re-enable queue */ PCNTL_G(processing_signal_queue) = 0; diff --git a/ext/pcntl/php_pcntl.h b/ext/pcntl/php_pcntl.h index 941bebb855..e3597e15ec 100644 --- a/ext/pcntl/php_pcntl.h +++ b/ext/pcntl/php_pcntl.h @@ -51,12 +51,17 @@ PHP_FUNCTION(pcntl_wstopsig); PHP_FUNCTION(pcntl_signal); PHP_FUNCTION(pcntl_exec); +struct php_pcntl_pending_signal { + struct php_pcntl_pending_signal *next; + long signo; +}; + ZEND_BEGIN_MODULE_GLOBALS(pcntl) HashTable php_signal_table; - zend_llist php_signal_queue; - int signal_queue_ready; int processing_signal_queue; + struct php_pcntl_pending_signal *head, *tail, *spares; ZEND_END_MODULE_GLOBALS(pcntl) + #ifdef ZTS #define PCNTL_G(v) TSRMG(pcntl_globals_id, zend_pcntl_globals *, v) #else diff --git a/ext/pcntl/test-pcntl.php b/ext/pcntl/test-pcntl.php index 6b3ee5649e..01d44fcad4 100755 --- a/ext/pcntl/test-pcntl.php +++ b/ext/pcntl/test-pcntl.php @@ -4,11 +4,11 @@ declare(ticks=1); function alarm_handle($signal){ - if ($signal==SIGALRM) print "Caught SIGALRM!!!\n"; + if ($signal==SIGALRM) print "Child: Caught SIGALRM!!!\n"; } function usr1_handle($signal){ - if ($signal==SIGUSR1) print "Caught SIGUSR1!!!\n"; + if ($signal==SIGUSR1) print "Child: Caught SIGUSR1!!!\n"; } print "This test will demonstrate a fork followed by ipc via signals.\n"; @@ -23,6 +23,7 @@ if ($pid==0) { sleep(100); print "Child: Resetting Alarm handler to Ignore....\n"; pcntl_signal(SIGALRM, SIG_IGN); + print "Child: sleeping for 10 seconds....\n"; sleep(10); print "Done\n"; } else { @@ -33,7 +34,7 @@ if ($pid==0) { sleep(1); print "Parent: Senging SIGUSR1 to Child\n"; posix_kill($pid,SIGUSR1); - sleep(1); + sleep(2); print "Parent: Sending SIGALRM to Child\n"; pcntl_waitpid($pid, &$status, $options); } -- 2.40.0