[flang] use setsid to assign the child to prevent zombie as it will be clean up by init process (#77944)

When using `setsid()` in a child process created by `fork()`, a new
session is created, and the child becomes a session leader. If the
parent process terminates before the child, the child becomes an orphan
and is adopted by the `init` process. The `init` process will eventually
clean up the child process once it exits.

However, killing the parent does not automatically kill the child; the
child will continue running until it exits.
Proper cleanup involves waiting for the child process to exit using
`wait()` or `waitpid()` in the parent process to avoid zombie processes,
but this approach is not valid for `EXECUTE_COMMAND_LINE` with async
mode.
Fix: https://github.com/llvm/llvm-project/issues/77803
This commit is contained in:
Yi Wu 2024-01-19 14:18:57 +00:00 committed by GitHub
parent eaa8def929
commit 5a7f9a5a9c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 31 additions and 2 deletions

View File

@ -181,8 +181,6 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
}
FreeMemory(wcmd);
#else
// terminated children do not become zombies
signal(SIGCHLD, SIG_IGN);
pid_t pid{fork()};
if (pid < 0) {
if (!cmdstat) {
@ -192,6 +190,19 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
CheckAndCopyCharsToDescriptor(cmdmsg, "Fork failed");
}
} else if (pid == 0) {
// Create a new session, let init process take care of zombie child
if (setsid() == -1) {
if (!cmdstat) {
terminator.Crash("setsid() failed with errno: %d, asynchronous "
"process initiation failed.",
errno);
} else {
StoreIntToDescriptor(cmdstat, ASYNC_NO_SUPPORT_ERR, terminator);
CheckAndCopyCharsToDescriptor(cmdmsg,
"setsid() failed, asynchronous process initiation failed.");
}
exit(EXIT_FAILURE);
}
int status{std::system(newCmd)};
TerminationCheck(status, cmdstat, cmdmsg, terminator);
exit(status);

View File

@ -404,6 +404,24 @@ TEST_F(ZeroArguments, ECLInvalidCommandParentNotTerminatedAsync) {
CheckDescriptorEqStr(cmdMsg.get(), "No change");
}
TEST_F(ZeroArguments, ECLInvalidCommandAsyncDontAffectSync) {
OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
*command.get(), false, nullptr, nullptr, nullptr));
EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
*command.get(), true, nullptr, nullptr, nullptr));
}
TEST_F(ZeroArguments, ECLInvalidCommandAsyncDontAffectAsync) {
OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
*command.get(), false, nullptr, nullptr, nullptr));
EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
*command.get(), false, nullptr, nullptr, nullptr));
}
static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
class OneArgument : public CommandFixture {
protected: