Skip to content
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
5 changes: 5 additions & 0 deletions src/coreclr/src/pal/src/exception/machexception.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,11 @@ HijackFaultingThread(
ts64.__rflags &= ~EFL_TF;
}

if (fIsStackOverflow)
{
exceptionRecord.ExceptionCode = EXCEPTION_STACK_OVERFLOW;
}

exceptionRecord.ExceptionFlags = EXCEPTION_IS_SIGNAL;
exceptionRecord.ExceptionRecord = NULL;
exceptionRecord.ExceptionAddress = (void *)ts64.__rip;
Expand Down
18 changes: 0 additions & 18 deletions src/coreclr/src/pal/src/exception/seh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,24 +264,6 @@ SEHProcessException(PAL_SEHException* exception)
// or in a jitter helper or it is a debugger breakpoint)
if (g_safeExceptionCheckFunction(contextRecord, exceptionRecord))
{
if (exceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
// Check if the failed access has hit a stack guard page. In such case, it
// was a stack probe that detected that there is not enough stack left.
void* stackLimit = CPalThread::GetStackLimit();
void* stackOverflowBottom = (void*)((size_t)stackLimit - GetVirtualPageSize());
// On some versions of glibc / platforms the stackLimit is an address of the guard page, on some
// it is right above the guard page.
// So consider SIGSEGV in one page above and below stack limit to be stack overflow.
void* stackOverflowTop = (void*)((size_t)stackLimit + GetVirtualPageSize());
void* violationAddr = (void*)exceptionRecord->ExceptionInformation[1];

if ((violationAddr >= stackOverflowBottom) && (violationAddr < stackOverflowTop))
{
exceptionRecord->ExceptionCode = EXCEPTION_STACK_OVERFLOW;
}
}

EnsureExceptionRecordsOnHeap(exception);
if (g_hardwareExceptionHandler(exception))
{
Expand Down
16 changes: 14 additions & 2 deletions src/coreclr/src/pal/src/exception/signal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ int g_common_signal_handler_context_locvar_offset = 0;

// TOP of special stack for handling stack overflow
volatile void* g_stackOverflowHandlerStack = NULL;

// Flag that is or-ed with SIGSEGV to indicate that the SIGSEGV was a stack overflow
const int StackOverflowFlag = 0x40000000;

#endif // !HAVE_MACH_EXCEPTIONS

/* public function definitions ************************************************/
Expand Down Expand Up @@ -529,7 +533,7 @@ static void sigsegv_handler(int code, siginfo_t *siginfo, void *context)
}
}

if (SwitchStackAndExecuteHandler(code, siginfo, context, (size_t)handlerStackTop))
if (SwitchStackAndExecuteHandler(code | StackOverflowFlag, siginfo, context, (size_t)handlerStackTop))
{
PROCAbort();
}
Expand Down Expand Up @@ -841,7 +845,15 @@ static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext
ucontext = (native_context_t *)sigcontext;
g_common_signal_handler_context_locvar_offset = (int)((char*)&signalContextRecord - (char*)__builtin_frame_address(0));

exceptionRecord.ExceptionCode = CONTEXTGetExceptionCodeForSignal(siginfo, ucontext);
if (code == (SIGSEGV | StackOverflowFlag))
{
exceptionRecord.ExceptionCode = EXCEPTION_STACK_OVERFLOW;
code &= ~StackOverflowFlag;
}
else
{
exceptionRecord.ExceptionCode = CONTEXTGetExceptionCodeForSignal(siginfo, ucontext);
}
exceptionRecord.ExceptionFlags = EXCEPTION_IS_SIGNAL;
exceptionRecord.ExceptionRecord = NULL;
exceptionRecord.ExceptionAddress = GetNativeContextPC(ucontext);
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/src/vm/exceptionhandling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5157,6 +5157,7 @@ BOOL IsSafeToHandleHardwareException(PCONTEXT contextRecord, PEXCEPTION_RECORD e
return g_fEEStarted && (
exceptionRecord->ExceptionCode == STATUS_BREAKPOINT ||
exceptionRecord->ExceptionCode == STATUS_SINGLE_STEP ||
exceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW ||
(IsSafeToCallExecutionManager() && ExecutionManager::IsManagedCode(controlPc)) ||
IsIPinVirtualStub(controlPc) || // access violation comes from DispatchStub of Interface call
IsIPInMarkedJitHelper(controlPc));
Expand Down Expand Up @@ -5192,6 +5193,7 @@ BOOL HandleHardwareException(PAL_SEHException* ex)
if (ex->GetExceptionRecord()->ExceptionCode == EXCEPTION_STACK_OVERFLOW)
{
GetThread()->SetExecutingOnAltStack();
Thread::VirtualUnwindToFirstManagedCallFrame(ex->GetContextRecord());
EEPolicy::HandleFatalStackOverflow(&ex->ExceptionPointers, FALSE);
UNREACHABLE();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
Copy link
Member

Choose a reason for hiding this comment

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

Nit: License headers

Copy link
Member Author

Choose a reason for hiding this comment

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

Oops, thank you for noticing that, fixed.

using System.Threading;

namespace TestStackOverflow
{
struct LargeStruct256
{
Guid g0;
Guid g1;
Guid g2;
Guid g3;
Guid g4;
Guid g5;
Guid g6;
Guid g7;
Guid g8;
Guid g9;
Guid ga;
Guid gb;
Guid gc;
Guid gd;
Guid ge;
Guid gf;
}

struct LargeStruct4096
{
LargeStruct256 s0;
LargeStruct256 s1;
LargeStruct256 s2;
LargeStruct256 s3;
LargeStruct256 s4;
LargeStruct256 s5;
LargeStruct256 s6;
LargeStruct256 s7;
LargeStruct256 s8;
LargeStruct256 s9;
LargeStruct256 sa;
LargeStruct256 sb;
LargeStruct256 sc;
LargeStruct256 sd;
LargeStruct256 se;
LargeStruct256 sf;
}

struct LargeStruct65536
{
LargeStruct4096 s0;
LargeStruct4096 s1;
LargeStruct4096 s2;
LargeStruct4096 s3;
LargeStruct4096 s4;
LargeStruct4096 s5;
LargeStruct4096 s6;
LargeStruct4096 s7;
LargeStruct4096 s8;
LargeStruct4096 s9;
LargeStruct4096 sa;
LargeStruct4096 sb;
LargeStruct4096 sc;
LargeStruct4096 sd;
LargeStruct4096 se;
LargeStruct4096 sf;
}
class Program
{
static void InfiniteRecursionA()
{
InfiniteRecursionB();
}

static void InfiniteRecursionB()
{
InfiniteRecursionC();
}
static void InfiniteRecursionC()
{
InfiniteRecursionA();
}

static void InfiniteRecursionA2()
{
LargeStruct65536 s;
InfiniteRecursionB2();
}

static void InfiniteRecursionB2()
{
LargeStruct65536 s;
InfiniteRecursionC2();
}

static void InfiniteRecursionC2()
{
LargeStruct65536 s;
InfiniteRecursionA2();
}

static void MainThreadTest(bool smallframe)
{
if (smallframe)
{
InfiniteRecursionA();
}
else
{
InfiniteRecursionA2();
}
}

static void SecondaryThreadsTest(bool smallframe)
{
Thread[] threads = new Thread[32];
for (int i = 0; i < threads.Length; i++)
{
threads[i] = new Thread(() => {
if (smallframe)
{
InfiniteRecursionA();
}
else
{
InfiniteRecursionA2();
}
});
threads[i].Start();
}

for (int i = 0; i < threads.Length; i++)
{
threads[i].Join();
}
}

static void Main(string[] args)
{
bool smallframe = (args[0] == "smallframe");
if (args[1] == "secondary")
{
SecondaryThreadsTest(smallframe);
}
else if (args[1] == "main")
{
MainThreadTest(smallframe);
}
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<Optimize>false</Optimize>
<CLRTestKind>BuildOnly</CLRTestKind>
<CLRTestPriority>0</CLRTestPriority>
</PropertyGroup>
<ItemGroup>
<Compile Include="stackoverflow.cs" />
</ItemGroup>
</Project>

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.
// See the LICENSE file in the project root for more information.
using System;

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

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

private unsafe void Execute(string arg1 = "")
{
long* bar = stackalloc long [1000];
ctr++;
if (ctr % 50 == 0)
Console.WriteLine("Call number {0} to the Execute method", ctr);

if (ctr <= MAX_RECURSIVE_CALLS)
Execute(string.Format("{0}", (IntPtr)bar));

ctr--;
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<Optimize>false</Optimize>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CLRTestKind>BuildOnly</CLRTestKind>
<CLRTestPriority>0</CLRTestPriority>
</PropertyGroup>
<ItemGroup>
<Compile Include="stackoverflow3.cs" />
</ItemGroup>
</Project>

Loading