Bug #71596 [Com]: Segmentation fault on ZTS with date function (setlocale)
| From: | maroszek at gmx dot net | Date: | Tue, 16 Feb 2016 10:15:12 +0000 |
| Subject: | Bug #71596 [Com]: Segmentation fault on ZTS with date function (setlocale) | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-199255@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=71596&edit=1
ID: 71596
Comment by: maroszek at gmx dot net
Reported by: maroszek at gmx dot net
Summary: Segmentation fault on ZTS with date function
(setlocale)
Status: Feedback
Type: Bug
Package: Date/time related
Operating System: Windows
PHP Version: 7.0.3
Block user comment: N
Private report: N
New Comment:
I have another upcoming ZTS bugreport :) and thanks for your time and effort!
std::thread seems to properly use beginthread. Nevertheless i updated the example just to be sure.
And i linked to threadlocale.obj to ensure that locales for each thread are enabled. I also compiled
the PHP7 example with Visual Studio 2015 Update 1. The error remains :) Can you have a look if you
can reproduce it aswell?
Updated Example:
unsigned __stdcall phpThread(void* params)
{
ts_resource(0);
while (true) {
zend_file_handle file_handle;
file_handle.type = ZEND_HANDLE_FILENAME;
file_handle.filename = "date.php";
file_handle.handle.fp = NULL;
file_handle.opened_path = NULL;
file_handle.free_filename = 0;
if (php_request_startup(TSRMLS_C) == FAILURE) {
//std::cout << "ERR" << std::endl;
php_request_shutdown(NULL);
continue;
}
if (php_execute_script(&file_handle TSRMLS_CC) == FAILURE)
break;
//std::cout << "RUN" << std::endl;
php_request_shutdown(NULL);
}
std::cout << "DIED" << std::endl;
return 0;
}
int main(int argc, const char * argv[]) {
tsrm_startup(128, 1, 0, NULL);
sapi_startup(&php_embed_module);
php_embed_module.ini_entries = "date.timezone=Europe/Berlin\n\0";
if (php_embed_module.startup(&php_embed_module) == FAILURE) {
throw std::runtime_error("Could not start PHP!");
}
for (int i = 0; i < 10; i++) {
_beginthreadex(NULL, 0, phpThread, NULL, 0, NULL);
//std::thread([]{phpThread(NULL);}).detach();
}
while (true);
return 0;
}
Previous Comments:
------------------------------------------------------------------------
[2016-02-16 08:20:07] ab@php.net
It's great you constantly aim improving the situation with TS builds :)
It looks correct to call configtreadlocale only once in the main thread. That's also what is
being done in the snippet from the doc page. However, from the doc page:
[QUOTE]
These functions affect the behavior of setlocale, _tsetlocale, _wsetlocale, _beginthread, and
_beginthreadex. If another method is used to create threads, the locale settings have no effect on
those threads.
[/QUOTE]
I'm currently unaware whether std::thread() is using _beginthread(), maybe you could trace it?
But if it doesn't, setlocale() will be clearly not thread safe so would require locking.
FYI we use vc11 with PHP5 currently, and vc14 with 7.0. vc12 could possibly have build issues with
PHP < 7 and in any case there are no dep libs builds for it.
Cheers.
------------------------------------------------------------------------
[2016-02-15 10:27:02] maroszek at gmx dot net
When adding this code before ts_resource(0); the crash remains but looks a little bit different.
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
MSDN: https://msdn.microsoft.com/de-de/library/26c0tb7x.aspx
We a calling this function in SAPI.c in sapi_startup. AFAIK this function is only called for the
"main" thread.
If i read the MSDN right, we need to call this function in all worker threads aswell. Neither one of
the SAPIs does this!?
------------------------------------------------------------------------
[2016-02-15 09:39:55] maroszek at gmx dot net
Examples: https://www.dropbox.com/s/c5cs2rz1zcssk3r/CrashTest.zip
StackTrace: https://www.dropbox.com/s/saff1d5z5z2vpon/PHPStackTraceSetLocale.png?dl=0
------------------------------------------------------------------------
[2016-02-15 09:38:26] maroszek at gmx dot net
Description:
------------
Description:
------------
PHP 7 is current stable (Also reproducable with 5.6.18)
PHP 7 is used as Embed (Also reproducable with 5.6.18)
Visual Studio 2013
I build a simple example to demonstrate the threading problem.
It crashes nearly instatly. Running only one thread works fine.
Expected result:
----------------
No crash. Endless loop doing the work.
Actual result:
--------------
Crash. I attached a backtrace which shows the concurrent access to setlocale which causes the crash
Test script:
---------------
#include <stdio.h>
#include <iostream>
#include <thread>
#include <main/php.h>
#include <main/SAPI.h>
#include <main/php_main.h>
#include <main/php_variables.h>
#include <main/php_ini.h>
#include <zend_ini.h>
#ifdef ZTS
ZEND_TSRMLS_CACHE_EXTERN();
#endif
zend_module_entry ips_module_entry = {
STANDARD_MODULE_HEADER,
"XYZ",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NO_VERSION_YET,
STANDARD_MODULE_PROPERTIES
};
static size_t php_embed_read_post(char *str, size_t str_length)
{
return 0;
}
static char* php_embed_read_cookies()
{
return NULL;
}
static size_t php_embed_ub_write(const char *str, size_t str_length)
{
std::cout << str;
return str_length;
}
static void php_embed_flush(void *server_context)
{
//
}
static void php_embed_send_header(sapi_header_struct *sapi_header, void *server_context)
{
//
}
static void php_embed_log_message(char *message)
{
fprintf(stderr, "%s\n", message);
}
static void php_embed_register_variables(zval *track_vars_array)
{
//
}
static int php_embed_startup(sapi_module_struct *sapi_module)
{
if (php_module_startup(sapi_module, NULL, 0) == FAILURE) {
return FAILURE;
}
return SUCCESS;
}
sapi_module_struct php_embed_module = {
"XYZ", /* name */
"PHP for XYZ", /* pretty name */
php_embed_startup, /* startup */
php_module_shutdown_wrapper, /* shutdown */
NULL, /* activate */
NULL, /* deactivate */
php_embed_ub_write, /* unbuffered write */
php_embed_flush, /* flush */
NULL, /* get uid */
NULL, /* getenv */
php_error, /* error handler */
NULL, /* header handler */
NULL, /* send headers handler */
php_embed_send_header, /* send header handler */
php_embed_read_post, /* read POST data */
php_embed_read_cookies, /* read Cookies */
php_embed_register_variables, /* register server variables */
php_embed_log_message, /* Log message */
NULL, /* Get request time */
NULL, /* Child terminate */
STANDARD_SAPI_MODULE_PROPERTIES
};
int main(int argc, const char * argv[]) {
tsrm_startup(128, 1, 0, NULL);
sapi_startup(&php_embed_module);
php_embed_module.ini_entries = "date.timezone=Europe/Berlin\n\0";
if (php_embed_module.startup(&php_embed_module) == FAILURE) {
throw std::runtime_error("Could not start PHP!");
}
for (int i = 0; i < 10; i++) {
std::thread([&argv]{
ts_resource(0);
while (true){
zend_file_handle file_handle;
file_handle.type = ZEND_HANDLE_FILENAME;
file_handle.filename = "date.php";
file_handle.handle.fp = NULL;
file_handle.opened_path = NULL;
file_handle.free_filename = 0;
if (php_request_startup() == FAILURE) {
//std::cout << "ERR" << std::endl;
php_request_shutdown(NULL);
continue;
}
if (php_execute_script(&file_handle) == FAILURE)
break;
//std::cout << "RUN" << std::endl;
php_request_shutdown(NULL);
}
std::cout << "DIED" << std::endl;
}).detach();
}
while (true);
return 0;
}
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=71596&edit=1