Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ jobs:
env:
phpts: ts
debug: true
- name: Install system dependencies
run: sudo apt-get update && sudo apt-get install --reinstall -y libbrotli-dev
Comment thread
henderkes marked this conversation as resolved.
Outdated
- name: Install e-dant/watcher
uses: ./.github/actions/watcher
- name: Reinstall libbrotli-dev
Expand Down
25 changes: 24 additions & 1 deletion frankenphp.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,12 @@ __thread HashTable *sandboxed_env = NULL;

#ifndef PHP_WIN32
static bool is_forked_child = false;
static void frankenphp_fork_child(void) { is_forked_child = true; }
static void frankenphp_fork_child(void) {
is_forked_child = true;
#ifdef __linux__
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's an equivalent to this in macOS or BSD?

realistically speaking, it's hard to imagine this ever kicks in if a php object destructor waits on the child pid, but in case the php runtime throws an unrecoverable error in a destructor, it might exit the thread without calling the destructor responsible for signalling and cleaning up the child

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that this syscall can race. Here is a workaround: https://stackoverflow.com/a/59216119

On FreeBSD, you can do this:

#include <sys/procctl.h>
#include <signal.h>

int sig = SIGKILL;
// P_PID means we are targeting a process ID. 
// 0 means "the current calling process".
procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &sig);

On macOS, it's harder; you have to run kqueue and way more code.

Comment thread
henderkes marked this conversation as resolved.
Outdated
#endif
}
#endif

/* Best-effort force-kill for stuck PHP threads.
Expand Down Expand Up @@ -916,6 +921,12 @@ static int frankenphp_startup(sapi_module_struct *sapi_module) {
static int frankenphp_deactivate(void) { return SUCCESS; }

static size_t frankenphp_ub_write(const char *str, size_t str_length) {
#ifndef PHP_WIN32
if (UNEXPECTED(is_forked_child)) {
return 0;
}
#endif

struct go_ub_write_return result =
go_ub_write(thread_index, (char *)str, str_length);

Expand All @@ -927,6 +938,12 @@ static size_t frankenphp_ub_write(const char *str, size_t str_length) {
}

static int frankenphp_send_headers(sapi_headers_struct *sapi_headers) {
#ifndef PHP_WIN32
if (UNEXPECTED(is_forked_child)) {
return SAPI_HEADER_SEND_FAILED;
}
#endif

if (SG(request_info).no_headers == 1) {
return SAPI_HEADER_SENT_SUCCESSFULLY;
}
Expand All @@ -952,6 +969,12 @@ static int frankenphp_send_headers(sapi_headers_struct *sapi_headers) {
}

static void frankenphp_sapi_flush(void *server_context) {
#ifndef PHP_WIN32
if (UNEXPECTED(is_forked_child)) {
return;
}
#endif

sapi_send_headers();
if (go_sapi_flush(thread_index)) {
php_handle_aborted_connection();
Expand Down
Loading