https://www.mediawiki.org/wiki/Special:Code/MediaWiki/103433
Revision: 103433 Author: tstarling Date: 2011-11-17 01:27:02 +0000 (Thu, 17 Nov 2011) Log Message: ----------- * Added a workaround for https://bugs.php.net/bug.php?id=31749 . Do a flush and abort after timeout instead of returning control to Apache, and set a timer which will protect against deadlocks within wmerrors itself. * Added user function wmerrors_malloc_test() which is a tight infinite loop of malloc() intended to allow easy testing of libc deadlocks. Modified Paths: -------------- trunk/php/wmerrors/README trunk/php/wmerrors/debian/changelog trunk/php/wmerrors/php_wmerrors.h trunk/php/wmerrors/wmerrors.c Modified: trunk/php/wmerrors/README =================================================================== --- trunk/php/wmerrors/README 2011-11-17 01:25:22 UTC (rev 103432) +++ trunk/php/wmerrors/README 2011-11-17 01:27:02 UTC (rev 103433) @@ -62,3 +62,10 @@ If this is true, a concise backtrace, listing base filenames (not including the path) and line numbers only, will be included in the error message which is passed through to PHP, for output into error_log. + +wmerrors.timeout + + A timeout for the fatal error handler and part of the subsequent PHP + request shutdown. PHP is prone to deadlocks during fatal error handling + (bug 31749), this feature is intended to mitigate their impact. If the + timeout expires, a message is written to stderr and abort() is called. Modified: trunk/php/wmerrors/debian/changelog =================================================================== --- trunk/php/wmerrors/debian/changelog 2011-11-17 01:25:22 UTC (rev 103432) +++ trunk/php/wmerrors/debian/changelog 2011-11-17 01:27:02 UTC (rev 103433) @@ -1,3 +1,11 @@ +php5-wmerrors (1.1.3-1) oneiric; urgency=low + + * Added a workaround for https://bugs.php.net/bug.php?id=31749 . Do a flush + and abort after timeout instead of returning control to Apache, and set a + timer which will protect against deadlocks within wmerrors itself. + + -- Tim Starling <[email protected]> Thu, 17 Nov 2011 12:11:43 +1100 + php5-wmerrors (1.1.2-2) lucid; urgency=low * Recompiled against new version of php5 Modified: trunk/php/wmerrors/php_wmerrors.h =================================================================== --- trunk/php/wmerrors/php_wmerrors.h 2011-11-17 01:25:22 UTC (rev 103432) +++ trunk/php/wmerrors/php_wmerrors.h 2011-11-17 01:27:02 UTC (rev 103433) @@ -17,6 +17,12 @@ #include "ext/standard/php_smart_str_public.h" +#if _POSIX_C_SOURCE >= 200112 && !defined(ZTS) +#define WMERRORS_USE_TIMER +#include <signal.h> +#include <time.h> +#endif + PHP_MINIT_FUNCTION(wmerrors); PHP_MSHUTDOWN_FUNCTION(wmerrors); PHP_RINIT_FUNCTION(wmerrors); @@ -31,6 +37,14 @@ int log_backtrace; int ignore_logging_errors; int backtrace_in_php_error_message; + long timeout; + int alarm_set; + void (*old_on_timeout)(int seconds TSRMLS_DC); +#ifdef WMERRORS_USE_TIMER + struct sigaction old_rt_action; + timer_t timer; +#endif + ZEND_END_MODULE_GLOBALS(wmerrors) Modified: trunk/php/wmerrors/wmerrors.c =================================================================== --- trunk/php/wmerrors/wmerrors.c 2011-11-17 01:25:22 UTC (rev 103432) +++ trunk/php/wmerrors/wmerrors.c 2011-11-17 01:27:02 UTC (rev 103433) @@ -3,6 +3,8 @@ #include "config.h" #endif +#include <stdlib.h> + #include "php.h" #include "php_ini.h" #include "php_wmerrors.h" @@ -12,15 +14,25 @@ #include "ext/standard/php_smart_str.h" /* for smart_str */ #include "Zend/zend_builtin_functions.h" /* for zend_fetch_debug_backtrace */ +static int wmerrors_post_deactivate(); static void wmerrors_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); static void wmerrors_show_message(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args TSRMLS_DC); static void wmerrors_get_concise_backtrace(smart_str *s TSRMLS_DC); static void wmerrors_write_full_backtrace(php_stream *logfile_stream); static void wmerrors_write_request_info(php_stream *logfile_stream TSRMLS_DC); +static void wmerrors_alarm_handler(int signo); +static void wmerrors_install_alarm(TSRMLS_D); +static void wmerrors_remove_alarm(TSRMLS_D); ZEND_DECLARE_MODULE_GLOBALS(wmerrors) +PHP_FUNCTION(wmerrors_malloc_test); + +ZEND_BEGIN_ARG_INFO(wmerrors_malloc_test_arginfo, 0) +ZEND_END_ARG_INFO() + zend_function_entry wmerrors_functions[] = { + PHP_FE(wmerrors_malloc_test, wmerrors_malloc_test_arginfo) {NULL, NULL, NULL} }; @@ -39,7 +51,9 @@ #if ZEND_MODULE_API_NO >= 20010901 "1.1.2", #endif - STANDARD_MODULE_PROPERTIES + NO_MODULE_GLOBALS, + wmerrors_post_deactivate, + STANDARD_MODULE_PROPERTIES_EX }; @@ -54,6 +68,7 @@ STD_PHP_INI_BOOLEAN("wmerrors.log_backtrace", "0", PHP_INI_ALL, OnUpdateBool, log_backtrace, zend_wmerrors_globals, wmerrors_globals) STD_PHP_INI_BOOLEAN("wmerrors.ignore_logging_errors", "0", PHP_INI_ALL, OnUpdateBool, ignore_logging_errors, zend_wmerrors_globals, wmerrors_globals) STD_PHP_INI_BOOLEAN("wmerrors.backtrace_in_php_error_message", "0", PHP_INI_ALL, OnUpdateBool, backtrace_in_php_error_message, zend_wmerrors_globals, wmerrors_globals) + STD_PHP_INI_ENTRY("wmerrors.timeout", "10", PHP_INI_ALL, OnUpdateLong, timeout, zend_wmerrors_globals, wmerrors_globals) PHP_INI_END() void (*old_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); @@ -87,6 +102,7 @@ PHP_RINIT_FUNCTION(wmerrors) { WMERRORS_G(recursion_guard) = 0; + WMERRORS_G(alarm_set) = 0; return SUCCESS; } @@ -97,6 +113,13 @@ return SUCCESS; } +int wmerrors_post_deactivate() +{ + TSRMLS_FETCH(); + wmerrors_remove_alarm(TSRMLS_C); + return SUCCESS; +} + PHP_MINFO_FUNCTION(wmerrors) { php_info_print_table_start(); @@ -143,6 +166,10 @@ /* No more OOM errors for now thanks */ zend_set_memory_limit((size_t)-1); + if (WMERRORS_G(timeout)) { + wmerrors_install_alarm(TSRMLS_C); + } + /* Do not show the html error to console */ if ( WMERRORS_G(enabled) && strncmp(sapi_module.name, "cli", 3) ) { /* Show the message */ @@ -155,18 +182,27 @@ } /* Put a concise backtrace in the normal output */ - if (WMERRORS_G(backtrace_in_php_error_message)) + if (WMERRORS_G(backtrace_in_php_error_message)) { wmerrors_get_concise_backtrace(&new_filename TSRMLS_CC); + } smart_str_appendl(&new_filename, error_filename, strlen(error_filename)); smart_str_0(&new_filename); WMERRORS_G(recursion_guard) = 0; zend_set_memory_limit(PG(memory_limit)); + if (PG(connection_status) & PHP_CONNECTION_TIMEOUT) { + /* abort instead of deadlocking */ + const char abort_message[] = "wmerrors: doing precautionary abort() after request timeout\n"; + write(STDERR_FILENO, abort_message, sizeof(abort_message) - 1); + abort(); + } + /* Pass through */ old_error_cb(type, new_filename.c, error_lineno, format, args); - /* Note: old_error_cb() may not return, in which case there will be no explicit free of new_filename */ + /* Note: old_error_cb() may not return, in which case there will be no + * explicit free of new_filename */ smart_str_free(&old_error_cb); } @@ -480,7 +516,13 @@ /* Write the message out */ if (expanded.c) { php_write(expanded.c, expanded.len TSRMLS_CC); + + if (PG(connection_status) & PHP_CONNECTION_TIMEOUT) { + /* Probably PHP will crash soon, better flush the output first */ + sapi_flush(TSRMLS_C); + } } + /* Clean up */ smart_str_free(&expanded); @@ -521,4 +563,58 @@ } } +/** + * Occasionally, PHP will time out during a malloc() call, generating SIGALRM. + * A mutex will be held, and then the process will deadlock forever on the + * next malloc() call. This timeout handler is designed to handle this + * situation safely. + * + * We could try to avoid using malloc(), but then the process would deadlock + * when it returns control to Apache. + */ +static void wmerrors_alarm_handler(int signo) { + const char message[] = "wmerrors: timed out during fatal error handler, aborting\n"; + write(STDERR_FILENO, message, sizeof(message) - 1); + abort(); +} +static void wmerrors_install_alarm(TSRMLS_D) { +#ifdef WMERRORS_USE_TIMER + struct sigaction sa; + struct sigevent evp; + struct itimerspec its; + + memset(&sa, sizeof(sa), 0); + sa.sa_handler = wmerrors_alarm_handler; + sigaction(SIGRTMIN+3, &sa, &WMERRORS_G(old_rt_action)); + + evp.sigev_notify = SIGEV_SIGNAL; + evp.sigev_signo = SIGRTMIN+3; + if (timer_create(CLOCK_REALTIME, &evp, &WMERRORS_G(timer)) == -1) { + perror("timer_create"); + abort(); + } + + its.it_value.tv_sec = WMERRORS_G(timeout); + its.it_value.tv_nsec = 0; + its.it_interval.tv_sec = its.it_interval.tv_nsec = 0; + timer_settime(WMERRORS_G(timer), 0, &its, NULL); + WMERRORS_G(alarm_set) = 1; +#endif +} + +static void wmerrors_remove_alarm(TSRMLS_D) { +#ifdef WMERRORS_USE_TIMER + if (WMERRORS_G(alarm_set)) { + timer_delete(WMERRORS_G(timer)); + sigaction(SIGRTMIN+3, &WMERRORS_G(old_rt_action), NULL); + WMERRORS_G(alarm_set) = 0; + } +#endif +} + +PHP_FUNCTION(wmerrors_malloc_test) { + for (;;) { + free(malloc(100)); + } +} _______________________________________________ MediaWiki-CVS mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs
