Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

segmentation fault when sending SIGINT to PHP embedded in another program #9649

Closed
dunglas opened this issue Sep 30, 2022 · 4 comments · Fixed by #9766
Closed

segmentation fault when sending SIGINT to PHP embedded in another program #9649

dunglas opened this issue Sep 30, 2022 · 4 comments · Fixed by #9766

Comments

@dunglas
Copy link
Contributor

dunglas commented Sep 30, 2022

Description

I'm embedding PHP in a Go program. When this program receives SIGINT, PHP crashes with the following stack trace (extracted using GDB):

Thread 1 "internal" received signal SIGSEGV, Segmentation fault.
0x00000000007b907c in zend_signal_handler_defer (signo=2, siginfo=0xffffeb3e5600, context=0xffffeb3e5680) at /go/php-src/Zend/zend_signal.c:98
98              if (EXPECTED(SIGG(active))) {

This may be related to #8029, #8789, #9337.

If anyone has an idea of what's happening, I can give them access to a private repository with a simple way to reproduce the crash. I reproduced the issue on Debian and macOS.

Go documentation about signal handling and C code: https://pkg.go.dev/os/signal#hdr-Go_programs_that_use_cgo_or_SWIG

PHP Version

PHP 8.2-dev

Operating System

Debian, macOS

@arnaud-lb
Copy link
Member

There is a good chance that the signal handler is being invoked on a thread that didn't execute PHP. In this case, per-thread resources have not been initialized on the thread, so SIGG(active) dereferences an invalid pointer.

I can reproduce the issue with this: https://gist.github.com/arnaud-lb/d0f2282e66def91a6ee34e1537b3ac5f

I'm assuming that there is no way to install hooks in Go's thread creations.

One way to prevent this crash would be to mask all signals in all threads except the ones that are ready to handle signals.

An other way would be to disable ZEND_SIGNAL entirely (--disable-zend-signals). Its purpose is to prevent corruption of shared state (such as the opcache) in forking SAPIs, but I believe that it's unnecessary when running a single process. In a multi-process setup, a process could be terminated while in the middle of updating the shared state. This would leave other processes with a corrupted state. In a single-process setup, this doesn't matter.

@dunglas
Copy link
Contributor Author

dunglas commented Oct 3, 2022

Thank you very much for your help @arnaud-lb.

I confirm that disabling ZEND_SIGNAL fixes the issue both on Linux and Mac.

I also tried to mask all signals: https://gist.github.com/dunglas/278225a71ba3fc11a27ca8c1705fa0c8

This fixes the problem in your reproducer, unfortunately, it doesn't play very well with the Go embedding use case because it's not desirable (and probably not possible at all) to change the mask for threads started by Go itself.

Couldn't we detect if it's a thread executing PHP before calling SIGG(active)?

@arnaud-lb
Copy link
Member

We could detect it at least to avoid the crash. I'm not sure we could do anything else than returning from the signal handler, though.

This fixes the problem in your reproducer, unfortunately, it doesn't play very well with the Go embedding use case because it's not desirable (and probably not possible at all) to change the mask for threads started by Go itself.

If it was possible to set the mask before Go starts any thread (or before Go itself starts), it would be inherited by all threads. It would only be required to mask the signals listed here:

static int zend_sigs[] = { TIMEOUT_SIG, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2 };

@rcmcdonald91
Copy link

Looks like this might be the stack overflow I was seeing in #8029

#0  zend_signal_handler (signo=2, siginfo=0x8204babb0, context=0x8204ba840)
    at Zend/zend_signal.c:176
176             int errno_save = errno;
(gdb) bt
#0  zend_signal_handler (signo=2, siginfo=0x8204babb0, context=0x8204ba840)
    at Zend/zend_signal.c:176
#1  0x00000008304a1744 in zend_signal_handler_defer (signo=2,
    siginfo=0x8204babb0, context=0x8204ba840) at Zend/zend_signal.c:143
#2  0x00000008304a18bb in zend_signal_handler (signo=2, siginfo=0x8204babb0,
    context=0x8204ba840) at Zend/zend_signal.c:216
#3  0x00000008304a1744 in zend_signal_handler_defer (signo=2,
    siginfo=0x8204babb0, context=0x8204ba840) at Zend/zend_signal.c:143
#4  0x00000008304a18bb in zend_signal_handler (signo=2, siginfo=0x8204babb0,
    context=0x8204ba840) at Zend/zend_signal.c:216
#5  0x00000008304a1744 in zend_signal_handler_defer (signo=2,
    siginfo=0x8204babb0, context=0x8204ba840) at Zend/zend_signal.c:143
#6  0x00000008304a18bb in zend_signal_handler (signo=2, siginfo=0x8204babb0,
    context=0x8204ba840) at Zend/zend_signal.c:216
#7  0x00000008304a1744 in zend_signal_handler_defer (signo=2,
    siginfo=0x8204babb0, context=0x8204ba840) at Zend/zend_signal.c:143
#8  0x00000008304a18bb in zend_signal_handler (signo=2, siginfo=0x8204babb0,
    context=0x8204ba840) at Zend/zend_signal.c:216
#9  0x00000008304a1744 in zend_signal_handler_defer (signo=2,
    siginfo=0x8204babb0, context=0x8204ba840) at Zend/zend_signal.c:143
#10 0x00000008304a18bb in zend_signal_handler (signo=2, siginfo=0x8204babb0,
    context=0x8204ba840) at Zend/zend_signal.c:216

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants