在PHP扩展中使用emalloc从线程时出现隔离错误

Segfault while using emalloc from thread in PHP extension

本文关键字:线程 错误 隔离 emalloc 扩展 PHP      更新时间:2023-10-16

我无法在线程内分配任何数量的内存,而不会在 https://github.com/php/php-src/blob/master/Zend/zend_alloc.c#L2409 触发段错误。

具有讽刺意味的是,仅当在线程安全版本的 PHP (ZTS( 上运行时,才会发生段错误。

在普通 NTS 版本上运行时,一切正常。

这里有一些可用于重现该问题的代码(我正在使用 php-cpp 来简化扩展的创建(。

void* test(void* wrapper){
emalloc(sizeof(Php::Value));
return NULL;
}
void VoIP::__construct()
{
pthread_t a;    
pthread_create(&a, NULL, test, this);
}
extern "C" {
PHPCPP_EXPORT void *get_module()
{
static Php::Extension extension("php-libtgvoip", "1.0");
Php::Class<VoIP> voip("VoIP");
voip.method<&VoIP::__construct>("__construct", Php::Public | Php::Final);
Php::Namespace danog("danog");
Php::Namespace MadelineProto("MadelineProto");
MadelineProto.add(std::move(voip));
danog.add(std::move(MadelineProto));
extension.add(std::move(danog));
return extension;
}
}

页眉:

#include <php.h>
#include <php_ini.h>
#include <ext/standard/info.h>
#include <phpcpp.h>
class VoIP : public Php::Base {
public:
void __construct();
}

\danog\MadelineProto\VoIP 类的实例化抛出一个段错误,由 test(( 中的 emalloc 引起:

Thread 2 "php" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffe91ff700 (LWP 4267)]
0x0000555555cfc7ed in _emalloc (size=32, __zend_filename=0x7fffed9c88a8 "main.cpp", __zend_lineno=30, __zend_orig_filename=0x0, __zend_orig_lineno=0)
at /root/php-src/Zend/zend_alloc.c:2409
2409            if (UNEXPECTED(AG(mm_heap)->use_custom_heap)) {
(gdb) backtrace
#0  0x0000555555cfc7ed in _emalloc (size=32, __zend_filename=0x7fffed9c88a8 "main.cpp", __zend_lineno=30, __zend_orig_filename=0x0, __zend_orig_lineno=0)
at /root/php-src/Zend/zend_alloc.c:2409
#1  0x00007fffed94e80e in test (wrapper=0x555556a73310) at main.cpp:30
#2  0x00007ffff572a494 in start_thread (arg=0x7fffe91ff700) at pthread_create.c:333
#3  0x00007ffff206eaff in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:97
(gdb)

完整的源代码可以找到@https://github.com/danog/php-libtgvoip

我的答案可能很明显,但不要使用 php 线程之外的emalloc和所有其他 php/zend 方法。无论ZTS是打开还是关闭,代码都会失败。看看应用程序崩溃的行:

if (UNEXPECTED(AG(mm_heap)->use_custom_heap)) 

AG 被声明为

#ifdef ZTS
static int alloc_globals_id;
#define AG(v) ZEND_TSRMG(alloc_globals_id, zend_alloc_globals *, v)
#else
#define AG(v) (alloc_globals.v)
static zend_alloc_globals alloc_globals;
#endif

当 ZTS 打开时,将出现直接段错误,因为调用线程未正确初始化为 php 线程以使用 TSRMG(线程安全资源管理器(,当 ZTS 关闭时,将出现修改全局变量的争用条件。