2626#include <stdint.h>
2727#include <stdio.h>
2828#include <stdlib.h>
29- #ifndef ZEND_WIN32
29+ #ifndef PHP_WIN32
3030#include <unistd.h>
3131#endif
32- #ifdef __linux__
32+ #if defined( __linux__ )
3333#include <sys/prctl.h>
34- #elif defined(__FreeBSD__ ) || defined( __OpenBSD__ )
34+ #elif defined(__FreeBSD__ )
3535#include <pthread_np.h>
36+ #include <sys/procctl.h>
37+ #elif defined(__OpenBSD__ )
38+ #include <pthread_np.h>
39+ #endif
40+ #if defined(__APPLE__ ) || defined(__OpenBSD__ ) || defined(__NetBSD__ ) || \
41+ defined(__DragonFly__ )
42+ #define FRANKENPHP_KQUEUE_PARENT_DEATH 1
43+ #include <sys/event.h>
44+ #include <sys/types.h>
3645#endif
3746
3847#include "_cgo_export.h"
@@ -110,10 +119,63 @@ static inline uintptr_t frankenphp_thread_index(void) {
110119
111120#ifndef PHP_WIN32
112121static bool is_forked_child = false;
113- static void frankenphp_fork_child (void ) { is_forked_child = true; }
122+ static pid_t fork_parent_pid = 0 ;
123+
124+ static void frankenphp_fork_prepare (void ) { fork_parent_pid = getpid (); }
125+
126+ #if defined(FRANKENPHP_KQUEUE_PARENT_DEATH )
127+ /* Watcher thread for platforms without a kernel parent-death signal.
128+ * Blocks in kevent() until the parent exits, then force-kills this child. */
129+ static void * frankenphp_parent_death_watcher (void * arg ) {
130+ pid_t ppid = (pid_t )(intptr_t )arg ;
131+ int kq = kqueue ();
132+ if (kq < 0 ) {
133+ _exit (1 );
134+ }
135+ struct kevent kev ;
136+ EV_SET (& kev , ppid , EVFILT_PROC , EV_ADD | EV_ONESHOT , NOTE_EXIT , 0 , NULL );
137+ if (kevent (kq , & kev , 1 , NULL , 0 , NULL ) < 0 ) {
138+ _exit (1 );
139+ }
140+ struct kevent event ;
141+ while (kevent (kq , NULL , 0 , & event , 1 , NULL ) < 0 && errno == EINTR );
142+ _exit (1 );
143+ }
144+ #endif
145+
146+ static void frankenphp_fork_child (void ) {
147+ is_forked_child = true;
148+ #if defined(__linux__ )
149+ // if the parent process dies between fork() and this prctl()
150+ if (prctl (PR_SET_PDEATHSIG , SIGKILL , 0 , 0 , 0 ) != 0 ||
151+ getppid () != fork_parent_pid ) {
152+ _exit (1 );
153+ }
154+ #elif defined(__FreeBSD__ )
155+ /* FreeBSD analogue of PR_SET_PDEATHSIG - except with
156+ * parent process death, rather than parent thread death. */
157+ int sig = SIGKILL ;
158+ if (procctl (P_PID , 0 , PROC_PDEATHSIG_CTL , & sig ) != 0 ||
159+ getppid () != fork_parent_pid ) {
160+ _exit (1 );
161+ }
162+ #elif defined(FRANKENPHP_KQUEUE_PARENT_DEATH )
163+ /* No kernel parent-death signal available; spawn a watcher that
164+ * blocks on EVFILT_PROC/NOTE_EXIT. */
165+ if (getppid () != fork_parent_pid ) {
166+ _exit (1 );
167+ }
168+ pthread_t watcher ;
169+ if (pthread_create (& watcher , NULL , frankenphp_parent_death_watcher ,
170+ (void * )(intptr_t )fork_parent_pid ) != 0 ) {
171+ _exit (1 );
172+ }
173+ pthread_detach (watcher );
174+ #endif
175+ }
114176
115177static void frankenphp_register_atfork (void ) {
116- pthread_atfork (NULL , NULL , frankenphp_fork_child );
178+ pthread_atfork (frankenphp_fork_prepare , NULL , frankenphp_fork_child );
117179}
118180#endif
119181
@@ -939,6 +1001,12 @@ static int frankenphp_startup(sapi_module_struct *sapi_module) {
9391001static int frankenphp_deactivate (void ) { return SUCCESS ; }
9401002
9411003static size_t frankenphp_ub_write (const char * str , size_t str_length ) {
1004+ #ifndef PHP_WIN32
1005+ if (UNEXPECTED (is_forked_child )) {
1006+ return 0 ;
1007+ }
1008+ #endif
1009+
9421010 struct go_ub_write_return result =
9431011 go_ub_write (frankenphp_thread_index (), (char * )str , str_length );
9441012
@@ -950,6 +1018,12 @@ static size_t frankenphp_ub_write(const char *str, size_t str_length) {
9501018}
9511019
9521020static int frankenphp_send_headers (sapi_headers_struct * sapi_headers ) {
1021+ #ifndef PHP_WIN32
1022+ if (UNEXPECTED (is_forked_child )) {
1023+ return SAPI_HEADER_SEND_FAILED ;
1024+ }
1025+ #endif
1026+
9531027 if (SG (request_info ).no_headers == 1 ) {
9541028 return SAPI_HEADER_SENT_SUCCESSFULLY ;
9551029 }
@@ -972,6 +1046,12 @@ static int frankenphp_send_headers(sapi_headers_struct *sapi_headers) {
9721046}
9731047
9741048static void frankenphp_sapi_flush (void * server_context ) {
1049+ #ifndef PHP_WIN32
1050+ if (UNEXPECTED (is_forked_child )) {
1051+ return ;
1052+ }
1053+ #endif
1054+
9751055 sapi_send_headers ();
9761056 if (go_sapi_flush (frankenphp_thread_index ())) {
9771057 php_handle_aborted_connection ();
0 commit comments