diff --git a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp index 37648c15db835d..a0952e926fb570 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp @@ -1055,6 +1055,26 @@ uint16_t PalCaptureStackBackTrace(uint32_t arg1, uint32_t arg2, void* arg3, uint #ifdef FEATURE_HIJACK static struct sigaction g_previousActivationHandler; +static bool IsSaSigInfo(struct sigaction* action) +{ + return (action->sa_flags & SA_SIGINFO) != 0; +} + +static bool IsSigDfl(struct sigaction* action) +{ + // macOS can return sigaction with SIG_DFL and SA_SIGINFO. + // SA_SIGINFO means we should use sa_sigaction, but here we want to check sa_handler. + // So we ignore SA_SIGINFO when sa_sigaction and sa_handler are at the same address. + return (&action->sa_handler == (void*)&action->sa_sigaction || !IsSaSigInfo(action)) && + action->sa_handler == SIG_DFL; +} + +static bool IsSigIgn(struct sigaction* action) +{ + return (&action->sa_handler == (void*)&action->sa_sigaction || !IsSaSigInfo(action)) && + action->sa_handler == SIG_IGN; +} + static void ActivationHandler(int code, siginfo_t* siginfo, void* context) { Thread* pThread = ThreadStore::GetCurrentThreadIfAvailableAsyncSafe(); @@ -1079,15 +1099,14 @@ static void ActivationHandler(int code, siginfo_t* siginfo, void* context) } // Call the original handler when it is not ignored or default (terminate). - if (g_previousActivationHandler.sa_flags & SA_SIGINFO) - { - _ASSERTE(g_previousActivationHandler.sa_sigaction != NULL); - g_previousActivationHandler.sa_sigaction(code, siginfo, context); - } - else + if (!IsSigDfl(&g_previousActivationHandler) && !IsSigIgn(&g_previousActivationHandler)) { - if (g_previousActivationHandler.sa_handler != SIG_IGN && - g_previousActivationHandler.sa_handler != SIG_DFL) + if (IsSaSigInfo(&g_previousActivationHandler)) + { + _ASSERTE(g_previousActivationHandler.sa_sigaction != NULL); + g_previousActivationHandler.sa_sigaction(code, siginfo, context); + } + else { _ASSERTE(g_previousActivationHandler.sa_handler != NULL); g_previousActivationHandler.sa_handler(code); diff --git a/src/coreclr/pal/src/exception/signal.cpp b/src/coreclr/pal/src/exception/signal.cpp index 74fa88db04e409..ed2b72d69c6ce1 100644 --- a/src/coreclr/pal/src/exception/signal.cpp +++ b/src/coreclr/pal/src/exception/signal.cpp @@ -975,15 +975,14 @@ static void inject_activation_handler(int code, siginfo_t *siginfo, void *contex else { // Call the original handler when it is not ignored or default (terminate). - if (g_previous_activation.sa_flags & SA_SIGINFO) + if (!IsSigDfl(&g_previous_activation) && !IsSigIgn(&g_previous_activation)) { - _ASSERTE(g_previous_activation.sa_sigaction != NULL); - g_previous_activation.sa_sigaction(code, siginfo, context); - } - else - { - if (g_previous_activation.sa_handler != SIG_IGN && - g_previous_activation.sa_handler != SIG_DFL) + if (IsSaSigInfo(&g_previous_activation)) + { + _ASSERTE(g_previous_activation.sa_sigaction != NULL); + g_previous_activation.sa_sigaction(code, siginfo, context); + } + else { _ASSERTE(g_previous_activation.sa_handler != NULL); g_previous_activation.sa_handler(code); diff --git a/src/tests/Regressions/coreclr/GitHub_132581/CMakeLists.txt b/src/tests/Regressions/coreclr/GitHub_132581/CMakeLists.txt new file mode 100644 index 00000000000000..af07c1df472bc0 --- /dev/null +++ b/src/tests/Regressions/coreclr/GitHub_132581/CMakeLists.txt @@ -0,0 +1,8 @@ +# The test is valid only on macOS. +if (CLR_CMAKE_TARGET_OSX) + include_directories(${INC_PLATFORM_DIR}) + + add_library(nativetest132581 ${TEST_LIB_TYPE} nativetest132581.cpp) + + install(TARGETS nativetest132581 DESTINATION bin) +endif() diff --git a/src/tests/Regressions/coreclr/GitHub_132581/nativetest132581.cpp b/src/tests/Regressions/coreclr/GitHub_132581/nativetest132581.cpp new file mode 100644 index 00000000000000..5c92a316385af1 --- /dev/null +++ b/src/tests/Regressions/coreclr/GitHub_132581/nativetest132581.cpp @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include + +#include +#include +#include +#include +#include +#include + +static void SignalHandler(int, siginfo_t*, void*) +{ +} + +extern "C" DLL_EXPORT int InstallSignalHandlerAndExec(const char* executable, const char* managedAssembly) +{ + struct sigaction action; + memset(&action, 0, sizeof(action)); + action.sa_sigaction = SignalHandler; + action.sa_flags = SA_SIGINFO | SA_RESTART; + sigemptyset(&action.sa_mask); + + if (sigaction(SIGUSR1, &action, nullptr) != 0) + { + return errno; + } + + if (setenv("DOTNET_TEST_132581_CHILD", "1", 1) != 0) + { + return errno; + } + + char* arguments[4]; + int index = 0; + arguments[index++] = const_cast(executable); + if (managedAssembly[0] != '\0') + { + arguments[index++] = const_cast(managedAssembly); + } + arguments[index++] = const_cast("--child"); + arguments[index] = nullptr; + + execv(executable, arguments); + return errno; +} + +extern "C" DLL_EXPORT int SendSignalFromChildProcess() +{ + pid_t child = fork(); + if (child == -1) + { + return errno; + } + + if (child == 0) + { + int result = kill(getppid(), SIGUSR1); + _exit(result == 0 ? 0 : 1); + } + + int status; + pid_t result; + do + { + result = waitpid(child, &status, 0); + } + while ((result == -1) && (errno == EINTR)); + + return (result == child) && WIFEXITED(status) && (WEXITSTATUS(status) == 0) ? 0 : -1; +} diff --git a/src/tests/Regressions/coreclr/GitHub_132581/test132581.cs b/src/tests/Regressions/coreclr/GitHub_132581/test132581.cs new file mode 100644 index 00000000000000..2a4c8c91e9cb5f --- /dev/null +++ b/src/tests/Regressions/coreclr/GitHub_132581/test132581.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using Xunit; + +public class Program +{ + private const string ChildEnvironmentVariable = "DOTNET_TEST_132581_CHILD"; + + [DllImport("nativetest132581")] + private static extern int InstallSignalHandlerAndExec(string executable, string managedAssembly); + + [DllImport("nativetest132581")] + private static extern int SendSignalFromChildProcess(); + + [Fact] + public static void TestEntryPoint() + { + if (Environment.GetEnvironmentVariable(ChildEnvironmentVariable) is null) + { + string executable = Environment.ProcessPath ?? throw new InvalidOperationException("The process path is unavailable."); + string command = Environment.GetCommandLineArgs()[0]; + string managedAssembly = string.Equals(executable, command, StringComparison.Ordinal) ? string.Empty : command; + + int error = InstallSignalHandlerAndExec(executable, managedAssembly); + throw new InvalidOperationException($"execv failed with error {error}."); + } + + Assert.Equal(0, SendSignalFromChildProcess()); + } +} diff --git a/src/tests/Regressions/coreclr/GitHub_132581/test132581.csproj b/src/tests/Regressions/coreclr/GitHub_132581/test132581.csproj new file mode 100644 index 00000000000000..6d55af1a67b67e --- /dev/null +++ b/src/tests/Regressions/coreclr/GitHub_132581/test132581.csproj @@ -0,0 +1,17 @@ + + + + true + true + 1 + + + + + + + + + + +