Skip to content

Do not let PHP-FPM children miss SIGTERM, SIGQUIT #4836

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

Closed
wants to merge 5 commits into from

Conversation

plmnikulin
Copy link
Contributor

Follow up of #4471.

Postpone signal delivery while spawning children.
Prevent the following case:

  • Reload (reexec) is in progress.
  • New master is forking to start enough children for pools
    where pm is not on-demand.
  • Another SIGUSR2 is received by the master process.
  • Master process switches to reloading state.
  • Some child has not set its own signal handlers.
  • SIGQUIT and SIGTERM sent by master process are caught
    by signal handler set by master process and so they are ignored.
  • A child is running, it has no reason to finish

Before pull request #4465 this scenario could cause deadlock,
however with 0ed6c37 reload finishes after SIGKILL.

Use sigprocmask() around fork() to avoid race of delivery signal to children
and setting of own signal handlers.

Fixes bug #76601

Postpone signal delivery while spawning children.
Prevent the following case:

- Reload (reexec) is in progress.
- New master is forking to start enough children for pools
  where `pm` is not `on-demand`.
- Another `SIGUSR2` is received by the master process.
- Master process switches to reloading state.
- Some child has not set its own signal handlers.
- `SIGQUIT` and `SIGTERM` sent by master process are caught
  by signal handler set by master process and so they are ignored.
- A child is running, it has no reason to finish

Before pull request php#4465 this scenario could cause deadlock,
however with 0ed6c37 reload finishes after `SIGKILL`.

Use sigprocmask() around fork() to avoid race of delivery signal to children
and setting of own signal handlers.

Fixes bug #76601
@plmnikulin plmnikulin changed the base branch from master to PHP-7.4 October 21, 2019 08:14
@plmnikulin
Copy link
Contributor Author

I am unsure that it is possible to create a test with high enough probability of failure without these changes since #4465 is merged. Maybe fpm log could be checked for SIGKILL messages, maybe process_control_timeout could be set to a value that causes timeout. Anyway it will be a slow test, likely it will last even longer than sapi/fpm/tests/bug74083-concurrent-reload.phpt since it is related to a subtle race.

@plmnikulin
Copy link
Contributor Author

I have added a test that relies on relation of process_control_timeout and timeout in log helper.

@@ -276,6 +282,12 @@ int fpm_signals_init_mask(int *signum_array, size_t size) /* {{{ */
return -1;
}
}
memcpy(&child_block_sigset, &block_sigset, sizeof(block_sigset));
Copy link
Member

Choose a reason for hiding this comment

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

I think this is technically undefined (or at least not recommended for portability) as sigset_t should be used only with specified operations. I would prefer to set it in the loop above instead - using the same operations as for block_sigset.

@@ -276,6 +282,12 @@ int fpm_signals_init_mask(int *signum_array, size_t size) /* {{{ */
return -1;
}
}
memcpy(&child_block_sigset, &block_sigset, sizeof(block_sigset));
if (0 > sigaddset(&child_block_sigset, SIGTERM) ||
Copy link
Member

Choose a reason for hiding this comment

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

What's the actually the reason specify one part (block_sigset) from parameter provided in fpm_main.c and another part (child_block_sigset extra signals) specified directly in this function? I think we should either have everything in the parameters (meaning extra child signals to block array paramter) or just have everything defined in fpm_signals which might make more sense.

@@ -167,7 +168,8 @@ static void sig_handler(int signo) /* {{{ */

if (fpm_globals.parent_pid != getpid()) {
/* prevent a signal race condition when child process
have not set up it's own signal handler yet */
do not set up it's own sigprocmask for some reason,
Copy link
Member

Choose a reason for hiding this comment

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

few typos (grammar mistakes) in here. Maybe something like:

prevent a signal race condition if a child process does not set 
its own sigprocmask up which leads to #76601.

Avoid list of signals to block in `fpm_main.c`. It had some sense before
explicit signals for children control were added to
`fpm_signals_init_mask()`.
There is no guarantee that it could not cause undefined behavior.
@bukka
Copy link
Member

bukka commented Nov 17, 2019

Merged as e37bd5d . Thanks!

@bukka bukka closed this Nov 17, 2019
@plmnikulin plmnikulin deleted the issue-76601 branch February 18, 2020 04:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants