Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 27 additions & 8 deletions src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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);
Expand Down
15 changes: 7 additions & 8 deletions src/coreclr/pal/src/exception/signal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions src/tests/Regressions/coreclr/GitHub_132581/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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()
72 changes: 72 additions & 0 deletions src/tests/Regressions/coreclr/GitHub_132581/nativetest132581.cpp
Original file line number Diff line number Diff line change
@@ -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 <platformdefines.h>

#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

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<char*>(executable);
if (managedAssembly[0] != '\0')
{
arguments[index++] = const_cast<char*>(managedAssembly);
}
arguments[index++] = const_cast<char*>("--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;
}
33 changes: 33 additions & 0 deletions src/tests/Regressions/coreclr/GitHub_132581/test132581.cs
Original file line number Diff line number Diff line change
@@ -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());
}
}
17 changes: 17 additions & 0 deletions src/tests/Regressions/coreclr/GitHub_132581/test132581.csproj

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I find the regression tests by issue number a lot harder to understand if I need to go back to them. Thoughts on naming by the scenario - something like ActivationSignalChaining class and Exec_InheritedFlags method?

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Needed for CLRTestTargetUnsupported and CMakeProjectReference -->

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
<!-- Needed for CLRTestTargetUnsupported and CMakeProjectReference -->
<!-- Installs a custom signal handler. -->

This test actually has a reason outside of infra limitations for why it is marked RPI. Let's put that here so later tooling doesn't try to force this in-proc with other tests.

<RequiresProcessIsolation>true</RequiresProcessIsolation>
<CLRTestTargetUnsupported Condition="'$(TargetsOSX)' != 'true'">true</CLRTestTargetUnsupported>
<CLRTestPriority>1</CLRTestPriority>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since this is pri 1, do we want to kick of an explicit outerloop run (or whatever will run this in coreclr and native AOT)?

</PropertyGroup>
<ItemGroup>
<Compile Include="test132581.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(TestLibraryProjectPath)" />
</ItemGroup>
<ItemGroup>
<CMakeProjectReference Include="CMakeLists.txt" />
</ItemGroup>
</Project>
Loading