From 0fc8993e11c42f4986d7329011cb6685ecb88cd7 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 24 Jun 2026 14:40:12 +0300 Subject: [PATCH 1/2] Test stackoverflow fix --- src/coreclr/vm/interpexec.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 714f41bfa7a742..a1d727232177c7 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -3360,8 +3360,11 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr if (!pChildFrame) { pChildFrame = (InterpMethodContextFrame*)alloca(sizeof(InterpMethodContextFrame)); - pChildFrame->pNext = NULL; - pFrame->pNext = pChildFrame; + // We make sure that a new frame can't be seen with invalid ip/next when a stack + // overflow is triggered at a location outside of our control. + VolatileStoreWithoutBarrier(&pChildFrame->ip, (const int32_t*)nullptr); + VolatileStoreWithoutBarrier(&pChildFrame->pNext, (InterpMethodContextFrame*)nullptr); + VolatileStoreWithoutBarrier(&pFrame->pNext, pChildFrame); } pChildFrame->ReInit(pFrame, targetIp, returnValueAddress, LOCAL_VAR_ADDR(callArgsOffset, int8_t)); pFrame = pChildFrame; @@ -3463,8 +3466,11 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr if (!pChildFrame) { pChildFrame = (InterpMethodContextFrame*)alloca(sizeof(InterpMethodContextFrame)); - pChildFrame->pNext = NULL; - pFrame->pNext = pChildFrame; + // We make sure that a new frame can't be seen with invalid ip/next when a stack + // overflow is triggered at a location outside of our control. + VolatileStoreWithoutBarrier(&pChildFrame->ip, (const int32_t*)nullptr); + VolatileStoreWithoutBarrier(&pChildFrame->pNext, (InterpMethodContextFrame*)nullptr); + VolatileStoreWithoutBarrier(&pFrame->pNext, pChildFrame); } pChildFrame->ReInit(pFrame, targetIp, returnValueAddress, callArgsAddress); pFrame = pChildFrame; @@ -4296,8 +4302,11 @@ do \ if (!pChildFrame) { pChildFrame = (InterpMethodContextFrame*)alloca(sizeof(InterpMethodContextFrame)); - pChildFrame->pNext = NULL; - pFrame->pNext = pChildFrame; + // We make sure that a new frame can't be seen with invalid ip/next when a stack + // overflow is triggered at a location outside of our control. + VolatileStoreWithoutBarrier(&pChildFrame->ip, (const int32_t*)nullptr); + VolatileStoreWithoutBarrier(&pChildFrame->pNext, (InterpMethodContextFrame*)nullptr); + VolatileStoreWithoutBarrier(&pFrame->pNext, pChildFrame); } // Set the frame to the same values as the caller frame. pChildFrame->ReInit(pFrame, pFrame->startIp, pFrame->pRetVal, pFrame->pStack); From ec79203cf41f1f49f73959cf593d83d9de3cd1fa Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Thu, 25 Jun 2026 09:08:19 +0300 Subject: [PATCH 2/2] Use std::atomic_signal_fence instead --- src/coreclr/inc/volatile.h | 14 ++++++++++++++ src/coreclr/vm/interpexec.cpp | 21 ++++++++++++--------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/coreclr/inc/volatile.h b/src/coreclr/inc/volatile.h index 1c97750496d74d..5c322e9125a9f6 100644 --- a/src/coreclr/inc/volatile.h +++ b/src/coreclr/inc/volatile.h @@ -59,6 +59,8 @@ #include "staticcontract.h" +#include + // // This code is extremely compiler- and CPU-specific, and will need to be altered to // support new compilers and/or CPUs. Here we enforce that we can only compile using @@ -325,6 +327,18 @@ void VolatileLoadBarrier() #endif } +// +// Compiler reordering barrier. Prevents the compiler from moving memory accesses across this +// point. It emits no instructions and provides no CPU or cross-thread ordering; use it only to +// order accesses with respect to asynchronous interruption on the SAME thread, such as a signal +// or hardware-exception handler. +// +inline +void CompilerBarrier() +{ + std::atomic_signal_fence(std::memory_order_acq_rel); +} + // // Volatile implements accesses with our volatile semantics over a variable of type T. // Wherever you would have used a "volatile Foo" or, equivalently, "Foo volatile", use Volatile diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index a1d727232177c7..4107328b658064 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -3362,9 +3362,10 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr pChildFrame = (InterpMethodContextFrame*)alloca(sizeof(InterpMethodContextFrame)); // We make sure that a new frame can't be seen with invalid ip/next when a stack // overflow is triggered at a location outside of our control. - VolatileStoreWithoutBarrier(&pChildFrame->ip, (const int32_t*)nullptr); - VolatileStoreWithoutBarrier(&pChildFrame->pNext, (InterpMethodContextFrame*)nullptr); - VolatileStoreWithoutBarrier(&pFrame->pNext, pChildFrame); + pChildFrame->ip = NULL; + pChildFrame->pNext = NULL; + CompilerBarrier(); + pFrame->pNext = pChildFrame; } pChildFrame->ReInit(pFrame, targetIp, returnValueAddress, LOCAL_VAR_ADDR(callArgsOffset, int8_t)); pFrame = pChildFrame; @@ -3468,9 +3469,10 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr pChildFrame = (InterpMethodContextFrame*)alloca(sizeof(InterpMethodContextFrame)); // We make sure that a new frame can't be seen with invalid ip/next when a stack // overflow is triggered at a location outside of our control. - VolatileStoreWithoutBarrier(&pChildFrame->ip, (const int32_t*)nullptr); - VolatileStoreWithoutBarrier(&pChildFrame->pNext, (InterpMethodContextFrame*)nullptr); - VolatileStoreWithoutBarrier(&pFrame->pNext, pChildFrame); + pChildFrame->ip = NULL; + pChildFrame->pNext = NULL; + CompilerBarrier(); + pFrame->pNext = pChildFrame; } pChildFrame->ReInit(pFrame, targetIp, returnValueAddress, callArgsAddress); pFrame = pChildFrame; @@ -4304,9 +4306,10 @@ do \ pChildFrame = (InterpMethodContextFrame*)alloca(sizeof(InterpMethodContextFrame)); // We make sure that a new frame can't be seen with invalid ip/next when a stack // overflow is triggered at a location outside of our control. - VolatileStoreWithoutBarrier(&pChildFrame->ip, (const int32_t*)nullptr); - VolatileStoreWithoutBarrier(&pChildFrame->pNext, (InterpMethodContextFrame*)nullptr); - VolatileStoreWithoutBarrier(&pFrame->pNext, pChildFrame); + pChildFrame->ip = NULL; + pChildFrame->pNext = NULL; + CompilerBarrier(); + pFrame->pNext = pChildFrame; } // Set the frame to the same values as the caller frame. pChildFrame->ReInit(pFrame, pFrame->startIp, pFrame->pRetVal, pFrame->pStack);