Skip to content

Instantly share code, notes, and snippets.

@smx-smx
Created November 28, 2023 01:29
Show Gist options
  • Save smx-smx/608d2f339d1a822df4b1e125c3a33ca7 to your computer and use it in GitHub Desktop.
Save smx-smx/608d2f339d1a822df4b1e125c3a33ca7 to your computer and use it in GitHub Desktop.
PHP FFI Thread Safe patch
diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c
index 9be5ac3405..81ac091750 100644
--- a/ext/ffi/ffi.c
+++ b/ext/ffi/ffi.c
@@ -922,9 +922,11 @@ static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */
}
/* }}} */
-static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, void* data) /* {{{ */
-{
- zend_ffi_callback_data *callback_data = (zend_ffi_callback_data*)data;
+static void (*orig_interrupt_function)(zend_execute_data *execute_data);
+
+static void zend_ffi_interrupt_function(zend_execute_data *execute_data){
+
+ zend_ffi_callback_data *callback_data = FFI_G(callback_data).data;
zend_fcall_info fci;
zend_ffi_type *ret_type;
zval retval;
@@ -938,13 +940,14 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v
fci.param_count = callback_data->arg_count;
fci.named_params = NULL;
+
if (callback_data->type->func.args) {
int n = 0;
zend_ffi_type *arg_type;
ZEND_HASH_PACKED_FOREACH_PTR(callback_data->type->func.args, arg_type) {
arg_type = ZEND_FFI_TYPE(arg_type);
- zend_ffi_cdata_to_zval(NULL, args[n], arg_type, BP_VAR_R, &fci.params[n], (zend_ffi_flags)(arg_type->attr & ZEND_FFI_ATTR_CONST), 0, 0);
+ zend_ffi_cdata_to_zval(NULL, FFI_G(callback_data).args[n], arg_type, BP_VAR_R, &fci.params[n], (zend_ffi_flags)(arg_type->attr & ZEND_FFI_ATTR_CONST), 0, 0);
n++;
} ZEND_HASH_FOREACH_END();
}
@@ -969,10 +972,40 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v
ret_type = ZEND_FFI_TYPE(callback_data->type->func.ret_type);
if (ret_type->kind != ZEND_FFI_TYPE_VOID) {
- zend_ffi_zval_to_cdata(ret, ret_type, &retval);
+ zend_ffi_zval_to_cdata(FFI_G(callback_data).ret, ret_type, &retval);
}
zval_ptr_dtor(&retval);
+
+ if (orig_interrupt_function) {
+ orig_interrupt_function(execute_data);
+ }
+ zend_interrupt_function = orig_interrupt_function;
+
+ pthread_cond_signal(&FFI_G(vm_ack));
+ pthread_mutex_unlock(&FFI_G(vm_lock));
+}
+
+static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, void* data) /* {{{ */
+{
+ // get lock, first
+ pthread_mutex_lock(&FFI_G(vm_lock));
+ zend_ffi_call_data call_data = {
+ .cif = cif,
+ .ret = ret,
+ .args = args,
+ .data = (zend_ffi_call_data *)data
+ };
+ FFI_G(callback_data) = call_data;
+
+ /** post interrupt request */
+ orig_interrupt_function = zend_interrupt_function;
+ zend_interrupt_function = zend_ffi_interrupt_function;
+ zend_atomic_bool_store_ex(&EG(vm_interrupt), true);
+
+ pthread_mutex_lock(&FFI_G(vm_lock));
+ pthread_cond_wait(&FFI_G(vm_ack), &FFI_G(vm_lock));
+ pthread_mutex_unlock(&FFI_G(vm_lock));
}
/* }}} */
@@ -5447,6 +5480,9 @@ ZEND_MINIT_FUNCTION(ffi)
return zend_ffi_preload(FFI_G(preload));
}
+ pthread_mutex_init(&FFI_G(vm_lock), NULL);
+ pthread_cond_init(&FFI_G(vm_ack), NULL);
+
return SUCCESS;
}
/* }}} */
diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h
index 02a241c6bb..e8f7f39b67 100644
--- a/ext/ffi/php_ffi.h
+++ b/ext/ffi/php_ffi.h
@@ -17,6 +17,9 @@
#ifndef PHP_FFI_H
#define PHP_FFI_H
+#include <ffi.h>
+#include <pthread.h>
+
extern zend_module_entry ffi_module_entry;
#define phpext_ffi_ptr &ffi_module_entry
@@ -27,6 +30,15 @@ typedef enum _zend_ffi_api_restriction {
} zend_ffi_api_restriction;
typedef struct _zend_ffi_type zend_ffi_type;
+typedef struct _zend_ffi_callback_data zend_ffi_callback_data;
+
+
+typedef struct _zend_ffi_call_data {
+ ffi_cif* cif;
+ void* ret;
+ void** args;
+ zend_ffi_callback_data* data;
+} zend_ffi_call_data;
ZEND_BEGIN_MODULE_GLOBALS(ffi)
zend_ffi_api_restriction restriction;
@@ -35,6 +47,10 @@ ZEND_BEGIN_MODULE_GLOBALS(ffi)
/* predefined ffi_types */
HashTable types;
+ pthread_mutex_t vm_lock;
+ pthread_cond_t vm_ack;
+ zend_ffi_call_data callback_data;
+
/* preloading */
char *preload;
HashTable *scopes; /* list of preloaded scopes */
#include <pthread.h>
#include <stdint.h>
typedef int (*callback_t)(int);
typedef void *(*__start_routine)(void *);
void make_thread(callback_t cb, int arg){
pthread_t tid;
pthread_create(&tid, NULL, (__start_routine)cb, (void *)(uintptr_t)(arg));
pthread_detach(tid);
}
<?php
$ffi = FFI::cdef(
'void make_thread(int (*cb)(void *), int arg);',
'./libtest.so'
);
$cb_func = function($arg){
print("hi from thread\n");
return 0;
};
for($i=0; $i<2000; $i++){
$ffi->make_thread($cb_func, 0xBEEF);
}
print("-- survived\n");
sleep(100);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment