Skip to content
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

Revert "Report StackOverflowException on NativeAOT on Linux" #95415

Merged
merged 1 commit into from
Nov 29, 2023
Merged
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
1 change: 0 additions & 1 deletion src/coreclr/nativeaot/Runtime/inc/CommonTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#ifndef __COMMON_TYPES_H__
#define __COMMON_TYPES_H__

#include <assert.h>
#include <cstddef>
#include <cstdint>
#include <stdlib.h>
Expand Down
104 changes: 4 additions & 100 deletions src/coreclr/nativeaot/Runtime/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#include "CommonTypes.h"
#include "CommonMacros.h"
#include "daccess.h"
#include "CommonMacros.inl"
#include "PalRedhawkCommon.h"
#include "PalRedhawk.h"
#include "rhassert.h"
Expand All @@ -28,11 +27,6 @@
#include "RhConfig.h"
#include "RhVolatile.h"

#if defined(TARGET_UNIX) && !HAVE_MACH_EXCEPTIONS && !defined(HOST_TVOS)
#include <signal.h>
#include <sys/mman.h>
#endif // defined(TARGET_UNIX) && !HAVE_MACH_EXCEPTIONS && !defined(HOST_TVOS)

#ifndef DACCESS_COMPILE

EXTERN_C NATIVEAOT_API void* REDHAWK_CALLCONV RhpHandleAlloc(void* pObject, int type);
Expand Down Expand Up @@ -287,9 +281,6 @@ void Thread::Construct()
if (StressLog::StressLogOn(~0u, 0))
m_pThreadStressLog = StressLog::CreateThreadStressLog(this);
#endif // STRESS_LOG
#if defined(TARGET_UNIX) && !HAVE_MACH_EXCEPTIONS && !defined(HOST_TVOS)
EnsureSignalAlternateStack();
#endif // defined(TARGET_UNIX) && !HAVE_MACH_EXCEPTIONS && !defined(HOST_TVOS)

// Everything else should be initialized to 0 via the static initialization of tls_CurrentThread.

Expand All @@ -306,89 +297,6 @@ void Thread::Construct()
ASSERT(m_interruptedContext == NULL);
}

#if defined(TARGET_UNIX) && !HAVE_MACH_EXCEPTIONS && !defined(HOST_TVOS)
void Thread::FreeSignalAlternateStack()
{
void *altstack = m_alternateStack;
m_alternateStack = nullptr;

if (altstack != nullptr)
{
stack_t ss, oss;
// The man page for sigaltstack says that when the ss.ss_flags is set to SS_DISABLE,
// all other ss fields are ignored. However, MUSL implementation checks that the
// ss_size is >= MINSIGSTKSZ even in this case.
ss.ss_size = MINSIGSTKSZ;
ss.ss_flags = SS_DISABLE;
ss.ss_sp = NULL;
int st = sigaltstack(&ss, &oss);
if ((st == 0) && (oss.ss_flags != SS_DISABLE))
{
// Make sure this altstack is this PAL's before freeing.
if (oss.ss_sp == altstack)
{
int st = munmap(oss.ss_sp, oss.ss_size);
_ASSERTE(st == 0);
}
}
}
}

bool Thread::EnsureSignalAlternateStack()
{
int st = 0;

stack_t oss;

// Query the current alternate signal stack
st = sigaltstack(NULL, &oss);
if ((st == 0) && (oss.ss_flags == SS_DISABLE))
{
// There is no alternate stack for SIGSEGV handling installed yet so allocate one

// We include the size of the SignalHandlerWorkerReturnPoint in the alternate stack size since the
// context contained in it is large and the SIGSTKSZ was not sufficient on ARM64 during testing.
int altStackSize = SIGSTKSZ;
#ifdef HAS_ADDRESS_SANITIZER
// Asan also uses alternate stack so we increase its size on the SIGSTKSZ * 4 that enough for asan
// (see kAltStackSize in compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc)
altStackSize += SIGSTKSZ * 4;
#endif
altStackSize = ALIGN_UP(altStackSize, PalOsPageSize());
int flags = MAP_ANONYMOUS | MAP_PRIVATE;
#ifdef MAP_STACK
flags |= MAP_STACK;
#endif
void* altStack = mmap(NULL, altStackSize, PROT_READ | PROT_WRITE, flags, -1, 0);
if (altStack != MAP_FAILED)
{
// create a guard page for the alternate stack
st = mprotect(altStack, PalOsPageSize(), PROT_NONE);
if (st == 0)
{
stack_t ss;
ss.ss_sp = (char*)altStack;
ss.ss_size = altStackSize;
ss.ss_flags = 0;
st = sigaltstack(&ss, NULL);
}

if (st == 0)
{
m_alternateStack = altStack;
}
else
{
int st2 = munmap(altStack, altStackSize);
_ASSERTE(st2 == 0);
}
}
}

return (st == 0);
}
#endif // defined(TARGET_UNIX) && !HAVE_MACH_EXCEPTIONS && !defined(HOST_TVOS)

bool Thread::IsInitialized()
{
return (m_ThreadStateFlags != TSF_Unknown);
Expand Down Expand Up @@ -453,10 +361,6 @@ void Thread::Destroy()
}
#endif //FEATURE_SUSPEND_REDIRECTION

#if defined(TARGET_UNIX) && !HAVE_MACH_EXCEPTIONS && !defined(HOST_TVOS)
FreeSignalAlternateStack();
#endif // defined(TARGET_UNIX) && !HAVE_MACH_EXCEPTIONS && !defined(HOST_TVOS)

ASSERT(m_pGCFrameRegistrations == NULL);
}

Expand Down Expand Up @@ -962,19 +866,19 @@ void Thread::Unhijack()
}

// This unhijack routine is called to undo a hijack, that is potentially on a different thread.
//
//
// Although there are many code sequences (here and in asm) to
// perform an unhijack operation, they will never execute concurrently:
//
//
// - A thread may unhijack itself at any time so long as it does that from unmanaged code while in coop mode.
// This ensures that coop thread can access its stack synchronously.
// Unhijacking from unmanaged code ensures that another thread will not attempt to hijack it,
// since we only hijack threads that are executing managed code.
//
//
// - A GC thread may access a thread asynchronously, including unhijacking it.
// Asynchronously accessed thread must be in preemptive mode and should not
// access the managed portion of its stack.
//
//
// - A thread that owns the suspension can access another thread as long as the other thread is
// in preemptive mode or suspended in managed code.
// Either way the other thread cannot be accessing its hijack.
Expand Down
12 changes: 2 additions & 10 deletions src/coreclr/nativeaot/Runtime/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ struct ThreadBuffer
HANDLE m_hPalThread; // WARNING: this may legitimately be INVALID_HANDLE_VALUE
void ** m_ppvHijackedReturnAddressLocation;
void * m_pvHijackedReturnAddress;
uintptr_t m_uHijackedReturnValueFlags;
uintptr_t m_uHijackedReturnValueFlags;
PTR_ExInfo m_pExInfoStackHead;
Object* m_threadAbortException; // ThreadAbortException instance -set only during thread abort
Object* m_pThreadLocalStatics;
Expand All @@ -112,9 +112,6 @@ struct ThreadBuffer
#ifdef FEATURE_GC_STRESS
uint32_t m_uRand; // current per-thread random number
#endif // FEATURE_GC_STRESS
#if defined(TARGET_UNIX) && !HAVE_MACH_EXCEPTIONS && !defined(HOST_TVOS)
void * m_alternateStack; // ptr to alternate signal stack
#endif // defined(TARGET_UNIX) && !HAVE_MACH_EXCEPTIONS && !defined(HOST_TVOS)
};

struct ReversePInvokeFrame
Expand Down Expand Up @@ -155,7 +152,7 @@ class Thread : private ThreadBuffer
// For suspension APCs it is mostly harmless, but wasteful and in extreme
// cases may force the target thread into stack oveflow.
// We use this flag to avoid sending another APC when one is still going through.
//
//
// On Unix this is an optimization to not queue up more signals when one is
// still being processed.
};
Expand Down Expand Up @@ -315,11 +312,6 @@ class Thread : private ThreadBuffer

bool IsActivationPending();
void SetActivationPending(bool isPending);

#if defined(TARGET_UNIX) && !HAVE_MACH_EXCEPTIONS && !defined(HOST_TVOS)
bool EnsureSignalAlternateStack();
void FreeSignalAlternateStack();
#endif // defined(TARGET_UNIX) && !HAVE_MACH_EXCEPTIONS && !defined(HOST_TVOS)
};

#ifndef __GCENV_BASE_INCLUDED__
Expand Down
43 changes: 14 additions & 29 deletions src/coreclr/nativeaot/Runtime/unix/HardwareExceptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@
#include "HardwareExceptions.h"
#include "UnixSignals.h"
#include "PalCreateDump.h"
#include "thread.h"
#include "threadstore.h"
#include <sys/mman.h>

#if defined(HOST_APPLE)
#include <mach/mach.h>
Expand Down Expand Up @@ -548,17 +545,6 @@ bool HardwareExceptionHandler(int code, siginfo_t *siginfo, void *context, void*
// Handler for the SIGSEGV signal
void SIGSEGVHandler(int code, siginfo_t *siginfo, void *context)
{
// First check if we have a stack overflow
size_t sp = ((UNIX_CONTEXT *)context)->GetSp();
size_t failureAddress = (size_t)siginfo->si_addr;

// If the failure address is at most one page above or below the stack pointer,
// we have a stack overflow.
if ((failureAddress - (sp - PalOsPageSize())) < (size_t)PalOsPageSize() * 2)
{
PalPrintFatalError("\nProcess is terminating due to StackOverflowException.\n");
RhFailFast();
}
bool isHandled = HardwareExceptionHandler(code, siginfo, context, siginfo->si_addr);
if (isHandled)
{
Expand Down Expand Up @@ -603,8 +589,7 @@ void SIGFPEHandler(int code, siginfo_t *siginfo, void *context)
// Initialize hardware exception handling
bool InitializeHardwareExceptionHandling()
{
// Run SIGSEGV handler on separate stack so we can handle stack overflow. Otherwise, the current (invalid) stack is used and another segfault is raised.
if (!AddSignalHandler(SIGSEGV, SIGSEGVHandler, &g_previousSIGSEGV, SA_ONSTACK))
if (!AddSignalHandler(SIGSEGV, SIGSEGVHandler, &g_previousSIGSEGV))
{
return false;
}
Expand All @@ -616,23 +601,23 @@ bool InitializeHardwareExceptionHandling()

#if defined(HOST_APPLE)
#ifndef HOST_TVOS // task_set_exception_ports is not supported on tvOS
// LLDB installs task-wide Mach exception handlers. XNU dispatches Mach
// exceptions first to any registered "activation" handler and then to
// any registered task handler before dispatching the exception to a
// host-wide Mach exception handler that does translation to POSIX
// signals. This makes it impossible to use LLDB with implicit null
// LLDB installs task-wide Mach exception handlers. XNU dispatches Mach
// exceptions first to any registered "activation" handler and then to
// any registered task handler before dispatching the exception to a
// host-wide Mach exception handler that does translation to POSIX
// signals. This makes it impossible to use LLDB with implicit null
// checks in NativeAOT; continuing execution after LLDB traps an
// EXC_BAD_ACCESS will result in LLDB's EXC_BAD_ACCESS handler being
// invoked again. This also interferes with the translation of SIGFPEs
// to .NET-level ArithmeticExceptions. Work around this here by
// installing a no-op task-wide Mach exception handler for
// EXC_BAD_ACCESS and EXC_ARITHMETIC.
kern_return_t kr = task_set_exception_ports(
mach_task_self(),
EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC, /* SIGSEGV, SIGFPE */
MACH_PORT_NULL,
EXCEPTION_STATE_IDENTITY,
MACHINE_THREAD_STATE);
// installing a no-op task-wide Mach exception handler for
// EXC_BAD_ACCESS and EXC_ARITHMETIC.
kern_return_t kr = task_set_exception_ports(
mach_task_self(),
EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC, /* SIGSEGV, SIGFPE */
MACH_PORT_NULL,
EXCEPTION_STATE_IDENTITY,
MACHINE_THREAD_STATE);
ASSERT(kr == KERN_SUCCESS);
#endif
#endif
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/nativeaot/Runtime/unix/UnixSignals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
#include "UnixSignals.h"

// Add handler for hardware exception signal
bool AddSignalHandler(int signal, SignalHandler handler, struct sigaction* previousAction, int additionalFlags)
bool AddSignalHandler(int signal, SignalHandler handler, struct sigaction* previousAction)
{
struct sigaction newAction;

newAction.sa_flags = SA_RESTART | additionalFlags;
newAction.sa_flags = SA_RESTART;
newAction.sa_handler = NULL;
newAction.sa_sigaction = handler;
newAction.sa_flags |= SA_SIGINFO;
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/nativeaot/Runtime/unix/UnixSignals.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

typedef void (*SignalHandler)(int code, siginfo_t* siginfo, void* context);

bool AddSignalHandler(int signal, SignalHandler handler, struct sigaction* previousAction, int additionalFlags = 0);
bool AddSignalHandler(int signal, SignalHandler handler, struct sigaction* previousAction);
void RestoreSignalHandler(int signal_id, struct sigaction* previousAction);

#endif // __UNIX_SIGNALS_H__
2 changes: 1 addition & 1 deletion src/tests/Common/CoreCLRTestLibrary/Utilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public static bool IsWindowsIoTCore

public static bool IsMonoRuntime => Type.GetType("Mono.RuntimeStructs") != null;
public static bool IsNotMonoRuntime => !IsMonoRuntime;
public static bool IsNativeAot => IsSingleFile && IsNotMonoRuntime && !IsReflectionEmitSupported;
public static bool IsNativeAot => IsNotMonoRuntime && !IsReflectionEmitSupported;

public static bool HasAssemblyFiles => !string.IsNullOrEmpty(typeof(Utilities).Assembly.Location);
public static bool IsSingleFile => !HasAssemblyFiles;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ struct LargeStruct65536
LargeStruct4096 se;
LargeStruct4096 sf;
}

internal class StackOverflow
class Program
{
[MethodImpl(MethodImplOptions.NoInlining)]
static void InfiniteRecursionA()
Expand Down Expand Up @@ -135,7 +134,7 @@ static void SecondaryThreadsTest(bool smallframe)
}
}

internal static void Run(string[] args)
static void Main(string[] args)
{
bool smallframe = (args[0] == "smallframe");
if (args[1] == "secondary")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Uses explicit Main for testing various runtime crashing scenarios. -->
<RequiresProcessIsolation>true</RequiresProcessIsolation>
<ReferenceXUnitWrapperGenerator>false</ReferenceXUnitWrapperGenerator>
<CLRTestKind>BuildOnly</CLRTestKind>

<Optimize>false</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="stackoverflow.cs" />
</ItemGroup>
</Project>

Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;

namespace TestStackOverflow
namespace TestStackOverflow3
{
internal class StackOverflow3
class Program
{
private const int MAX_RECURSIVE_CALLS = 1000000;
static int ctr = 0;

public static void Run()
public static void Main()
{
StackOverflow3 ex = new StackOverflow3();
Program ex = new Program();
ex.Execute();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Uses explicit Main for testing various runtime crashing scenarios. -->
<RequiresProcessIsolation>true</RequiresProcessIsolation>
<ReferenceXUnitWrapperGenerator>false</ReferenceXUnitWrapperGenerator>

<Optimize>false</Optimize>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CLRTestKind>BuildOnly</CLRTestKind>
</PropertyGroup>
<ItemGroup>
<Compile Include="stackoverflow3.cs" />
</ItemGroup>
</Project>

Loading
Loading