extra pthread handling#2390
Conversation
unlikely to be needed, but better safe than sorry
| static void frankenphp_fork_child(void) { | ||
| is_forked_child = true; | ||
| #ifdef __linux__ | ||
| prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
cdb8a37 to
1f2963e
Compare
There was a problem hiding this comment.
Pull request overview
This PR hardens FrankenPHP’s behavior around fork() in non-Windows builds to avoid unsafe interactions between forked PHP children and the Go-backed SAPI I/O layer, and tweaks the Linux CI workflow to ensure libbrotli-dev is present/reinstalled.
Changes:
- Mark forked children via
pthread_atfork()and (on Linux) attempt to setPR_SET_PDEATHSIGto kill the child if its parent dies. - Prevent forked children from calling Go-backed SAPI output/header/flush hooks (
ub_write,send_headers,sapi_flush). - Add an early
apt-get update+libbrotli-devreinstall step in the Linux test workflow.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| frankenphp.c | Adds Linux PDEATHSIG in the atfork child handler and disables Go-backed SAPI I/O paths in forked children. |
| .github/workflows/tests.yaml | Adds an extra dependencies install step (apt update + reinstall libbrotli-dev) ahead of building watcher. |
Comments suppressed due to low confidence (1)
.github/workflows/tests.yaml:66
- This adds an apt-get update + libbrotli-dev reinstall step, but the job already has a later 'Reinstall libbrotli-dev' workaround step. Consider merging these (e.g., do the update once and keep a single reinstall at the point it’s needed) to avoid redundant package operations and extra CI time.
- name: Install system dependencies
run: sudo apt-get update && sudo apt-get install --reinstall -y libbrotli-dev
- name: Install e-dant/watcher
uses: ./.github/actions/watcher
- name: Reinstall libbrotli-dev
# TODO: remove this workaround when fixed upstream
run: sudo apt-get install --reinstall -y libbrotli-dev
- name: Set CGO flags
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| static void frankenphp_fork_child(void) { | ||
| is_forked_child = true; | ||
| #ifdef __linux__ | ||
| prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); |
| static void frankenphp_fork_child(void) { | ||
| is_forked_child = true; | ||
| #ifdef __linux__ | ||
| prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); |
There was a problem hiding this comment.
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.
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr> Signed-off-by: Marc <m@pyc.ac>
|
I'll fix this up later - the BSD path is not equivalent to the linux one though. We optimally want the parent thread dying to trigger the signal, not the parent process. In reality they'd be the same if a user doesn't do silly things in php, though. |
unlikely to be needed, but better safe than sorry