LibWebView: Tie helper processes to browser lifetime
Add an opt-in Core::Process spawn mode that asks supported kernels to terminate the child when its parent exits. On Linux, use a fork/exec path for this mode so the child can install PR_SET_PDEATHSIG before execing the helper process. The existing posix_spawn path remains in place for other process launches. Opt LibWebView helper processes into this mode so WebContent, WebWorker, and other browser children do not survive a crashed or exited UI process.
This commit is contained in:
parent
42f89bb679
commit
8e85f5a0c4
3 changed files with 109 additions and 4 deletions
|
|
@ -16,8 +16,10 @@
|
|||
#include <LibCore/Process.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <spawn.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(AK_OS_SERENITY)
|
||||
|
|
@ -31,6 +33,9 @@ extern "C" {
|
|||
# include <hurd.h>
|
||||
}
|
||||
#endif
|
||||
#if defined(AK_OS_LINUX)
|
||||
# include <sys/prctl.h>
|
||||
#endif
|
||||
#if defined(AK_OS_FREEBSD)
|
||||
# include <sys/user.h>
|
||||
#endif
|
||||
|
|
@ -82,8 +87,109 @@ Process Process::current()
|
|||
return p;
|
||||
}
|
||||
|
||||
#if defined(AK_OS_LINUX)
|
||||
static Optional<int> run_file_actions_in_child(Vector<ProcessSpawnOptions::FileActionType> const& file_actions)
|
||||
{
|
||||
for (auto const& file_action : file_actions) {
|
||||
auto error = file_action.visit(
|
||||
[&](FileAction::OpenFile const& action) {
|
||||
auto fd = open(
|
||||
action.path.characters(),
|
||||
File::open_mode_to_options(action.mode | Core::File::OpenMode::KeepOnExec),
|
||||
action.permissions);
|
||||
if (fd < 0)
|
||||
return Optional<int> { errno };
|
||||
|
||||
if (fd != action.fd) {
|
||||
if (dup2(fd, action.fd) < 0) {
|
||||
auto saved_errno = errno;
|
||||
close(fd);
|
||||
return Optional<int> { saved_errno };
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
return Optional<int> {};
|
||||
},
|
||||
[&](FileAction::CloseFile const& action) {
|
||||
close(action.fd);
|
||||
return Optional<int> {};
|
||||
},
|
||||
[&](FileAction::DupFd const& action) {
|
||||
if (dup2(action.write_fd, action.fd) < 0)
|
||||
return Optional<int> { errno };
|
||||
return Optional<int> {};
|
||||
});
|
||||
if (error.has_value())
|
||||
return error;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static ErrorOr<pid_t> fork_and_exec_with_parent_death_signal(ProcessSpawnOptions const& options, Span<char const*> arguments)
|
||||
{
|
||||
auto error_pipe = TRY(System::pipe2(O_CLOEXEC));
|
||||
|
||||
auto parent_pid = getpid();
|
||||
auto pid = fork();
|
||||
if (pid < 0) {
|
||||
auto saved_errno = errno;
|
||||
MUST(System::close(error_pipe[0]));
|
||||
MUST(System::close(error_pipe[1]));
|
||||
return Error::from_syscall("fork"sv, saved_errno);
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
close(error_pipe[0]);
|
||||
|
||||
auto report_errno_and_exit = [&](int error) {
|
||||
auto bytes_written = write(error_pipe[1], &error, sizeof(error));
|
||||
(void)bytes_written;
|
||||
_exit(127);
|
||||
};
|
||||
|
||||
if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0)
|
||||
report_errno_and_exit(errno);
|
||||
if (getppid() != parent_pid)
|
||||
_exit(127);
|
||||
|
||||
if (auto error = run_file_actions_in_child(options.file_actions); error.has_value())
|
||||
report_errno_and_exit(error.value());
|
||||
|
||||
if (options.search_for_executable_in_path)
|
||||
execvp(options.executable.characters(), const_cast<char* const*>(arguments.data()));
|
||||
else
|
||||
execve(options.executable.characters(), const_cast<char* const*>(arguments.data()), Environment::raw_environ());
|
||||
|
||||
report_errno_and_exit(errno);
|
||||
}
|
||||
|
||||
MUST(System::close(error_pipe[1]));
|
||||
|
||||
int child_errno = 0;
|
||||
auto bytes_read = TRY(System::read(error_pipe[0], { &child_errno, sizeof(child_errno) }));
|
||||
MUST(System::close(error_pipe[0]));
|
||||
|
||||
if (bytes_read > 0) {
|
||||
int status = 0;
|
||||
(void)waitpid(pid, &status, 0);
|
||||
return Error::from_errno(child_errno);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
#endif
|
||||
|
||||
ErrorOr<Process> Process::spawn(ProcessSpawnOptions const& options)
|
||||
{
|
||||
ArgvList argv_list(options.executable, options.arguments.size());
|
||||
for (auto const& argument : options.arguments)
|
||||
argv_list.append(argument.characters());
|
||||
|
||||
#if defined(AK_OS_LINUX)
|
||||
if (options.die_with_parent)
|
||||
return Process { TRY(fork_and_exec_with_parent_death_signal(options, argv_list.get())) };
|
||||
#endif
|
||||
|
||||
#define CHECK(invocation) \
|
||||
if (int returned_errno = (invocation)) \
|
||||
return Error::from_errno(returned_errno);
|
||||
|
|
@ -117,10 +223,6 @@ ErrorOr<Process> Process::spawn(ProcessSpawnOptions const& options)
|
|||
|
||||
#undef CHECK
|
||||
|
||||
ArgvList argv_list(options.executable, options.arguments.size());
|
||||
for (auto const& argument : options.arguments)
|
||||
argv_list.append(argument.characters());
|
||||
|
||||
pid_t pid;
|
||||
if (options.search_for_executable_in_path) {
|
||||
pid = TRY(System::posix_spawnp(options.executable.view(), &spawn_actions, nullptr, const_cast<char**>(argv_list.get().data()), Core::Environment::raw_environ()));
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ struct ProcessSpawnOptions {
|
|||
StringView name {};
|
||||
ByteString executable {};
|
||||
bool search_for_executable_in_path { false };
|
||||
// On supported platforms, ask the kernel to terminate this process when its parent dies.
|
||||
bool die_with_parent { false };
|
||||
Vector<ByteString> const& arguments {};
|
||||
|
||||
using FileActionType = Variant<FileAction::OpenFile, FileAction::CloseFile, FileAction::DupFd>;
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ ErrorOr<Process::ProcessAndIPCTransport> Process::spawn_and_connect_to_process(C
|
|||
Array<int, 2> stderr_pipe {};
|
||||
|
||||
Core::ProcessSpawnOptions spawn_options = options;
|
||||
spawn_options.die_with_parent = true;
|
||||
|
||||
if (capture_output) {
|
||||
stdout_pipe = TRY(Core::System::pipe2(O_CLOEXEC));
|
||||
|
|
|
|||
Loading…
Reference in a new issue