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

[x86/Linux] Catch exceptions thrown from unsafe code #7244

Closed
parjong opened this issue Jan 18, 2017 · 7 comments · Fixed by dotnet/coreclr#9235
Closed

[x86/Linux] Catch exceptions thrown from unsafe code #7244

parjong opened this issue Jan 18, 2017 · 7 comments · Fixed by dotnet/coreclr#9235
Assignees
Labels
arch-x86 os-linux Linux OS (any supported distro)
Milestone

Comments

@parjong
Copy link
Contributor

parjong commented Jan 18, 2017

CoreCLR with recent PRs (#8889, dotnet/coreclr#8911, dotnet/coreclr#8913, dotnet/coreclr#8914, dotnet/coreclr#8916, dotnet/coreclr#8964) begins to support exception handling for x86/Linux. (It can run very simple example presented in #7216).

Unfortunately, CLR cannot catch an exception thrown from unsafe code. For example, let us consider the following code:

using System;
using System.Runtime.CompilerServices;

public class BringUpTest
{
    [MethodImplAttribute(MethodImplOptions.NoInlining)]
    public static unsafe void Localloc(byte n)
    {
        Console.WriteLine("Enter Localloc({0})", n);
        byte* a = stackalloc byte[n];

        Console.WriteLine("Here - 1");
        *a = 0;
        Console.WriteLine("Here - 2");

        byte i;
        for (i=1; i < n; ++i) { a[i] = i; }

        Console.WriteLine("Here - 2");

        for (i=1; i < n; ++i) { Console.WriteLine(a[i]); }

        Console.WriteLine("Leave Localloc({0})", n);
    }

    public static void Main()
    {
        try
        {
            Console.WriteLine("Before");
            Localloc(0);
            Console.WriteLine("After");
        }
        catch (Exception)
        {
            Console.WriteLine("Caught (Enter)");
            Console.WriteLine("Caught (Leave)");
        }

        Console.WriteLine("Done");
    }
}

We except to see the following output for the above code:

Before
Enter Localloc(0)
Here - 1
Caught (Enter)
Caught (Leave)
Done

Currently, however, we get the following output:

Before
Enter Localloc(0)
Here - 1
Segmentation fault (core dumped)
@parjong parjong changed the title [x86/Linux] Catch Exception thrown from unsafe code [x86/Linux] Catch exceptions thrown from unsafe code Jan 18, 2017
@parjong
Copy link
Contributor Author

parjong commented Jan 18, 2017

\CC @seanshpark

@janvorli
Copy link
Member

@parjong I think it would be the case for all hardware exceptions. You can try to make a test with null reference or division by zero.
Hardware exceptions are handled in a special way and I think that code path doesn't work yet.
For example:

using System;

namespace Test
{
    public class Program
    {
        public static void Main(string[] args)
        {
            string test = null;
            Console.WriteLine("NullReferenceException incoming?");

            try
            {
                Console.WriteLine("Hello {0}", test.Length);
            }
            catch (Exception ex)
            {
                Console.WriteLine("test.Length threw: {0}", ex);
            }
        }
    }
}

@parjong
Copy link
Contributor Author

parjong commented Jan 18, 2017

@janvorli There is another issue related with try region (which is almost same as #5676), but that issue seems te be irrelevant.

This issue comes from the folloing code in ExceptionTracker::ProcessOSExceptionNotification:

        if (fIsFrameLess)
        {
            pGSCookie = (GSCookie*)cfThisFrame.GetCodeManager()->GetGSCookieAddr(cfThisFrame.pRD,
                                                                                          &cfThisFrame.codeInfo,
                                                                                          &cfThisFrame.codeManState);
            if (pGSCookie)
            {
                // The following function call sets the GS cookie pointers and checks the cookie.
                cfThisFrame.SetCurGSCookie(pGSCookie);
            }

            status = HandleFunclets(&fProcessThisFrame, fIsFirstPass,
                cfThisFrame.GetFunction(), cfThisFrame.IsFunclet(), sf);
        }

For the above example, the return value of EECodeManager::GetGSCookieAddr comes from the following code:

    if  (info->ebpFrame)
    {
        return PVOID(SIZE_T((DWORD(*pContext->pEbp) - info->gsCookieOffset)));
    }

The current implementation does not initialize pContext->pEbp at this point, and thus it returns some strange value, and then segmentation fault occurs in SetCurGSCookie.

@parjong
Copy link
Contributor Author

parjong commented Jan 18, 2017

@janvorli Here is the result when running the code that you mentioned (it is a bit different from what I expected):

NullReferenceException incoming?

Assert failure(PID 44 [0x0000002c], Thread: 44 [0x002c]): (dac_cast<TADDR>(m_pMethTab) & MARKED_BIT) == 0
    File: /home/parjong/external/dotnet/coreclr/src/vm/object.h Line: 247
    Image: /opt/overlay/corerun

Aborted (core dumped)

@janvorli
Copy link
Member

@parjong as I've asked in your other issue, could you please dump the stack trace at the point where you git the sigsegv in EECodeManager::GetGSCookieAddr? There are two possible callers of this method and possibly multiple codepaths that can get you there.

@parjong
Copy link
Contributor Author

parjong commented Jan 18, 2017

@janvorli Sorry for late response. Here is the stack dump at segfault:

Program received signal SIGSEGV, Segmentation fault.
0xf6ded716 in CrawlFrame::CheckGSCookies (this=0xffffae20) at /home/parjong/projects/dotnet/coreclr/src/vm/stackwalk.cpp:397
397         if (*pFirstGSCookie != GetProcessGSCookie())
(gdb) bt
#0  0xf6ded716 in CrawlFrame::CheckGSCookies (this=0xffffae20) at /home/parjong/projects/dotnet/coreclr/src/vm/stackwalk.cpp:397
dotnet/coreclr#1  0xf6ded7dd in CrawlFrame::SetCurGSCookie (this=0xffffae20, pGSCookie=0xffffc9) at /home/parjong/projects/dotnet/coreclr/src/vm/stackwalk.cpp:418
dotnet/coreclr#2  0xf71117b6 in ExceptionTracker::ProcessOSExceptionNotification (this=0x8084870, pExceptionRecord=0x80e2f94, pContextRecord=0x80e2cc8, pDispatcherContext=0xffffb4a8, dwExceptionFlags=0, sf=..., pThread=
    0x8086938, STState=ExceptionTracker::STS_NewException) at /home/parjong/projects/dotnet/coreclr/src/vm/exceptionhandling.cpp:1823
dotnet/coreclr#3  0xf710ef39 in ProcessCLRException (pExceptionRecord=0x80e2f94, MemoryStackFp=4294952468, pContextRecord=0x80e2cc8, pDispatcherContext=0xffffb4a8)
    at /home/parjong/projects/dotnet/coreclr/src/vm/exceptionhandling.cpp:1031
dotnet/coreclr#4  0xf7119ec4 in UnwindManagedExceptionPass1 (ex=..., frameContext=0xffffb858) at /home/parjong/projects/dotnet/coreclr/src/vm/exceptionhandling.cpp:4525
dotnet/coreclr#5  0xf711a9d7 in DispatchManagedException (ex=..., isHardwareException=true) at /home/parjong/projects/dotnet/coreclr/src/vm/exceptionhandling.cpp:4647
dotnet/coreclr#6  0xf710af3a in HandleHardwareException (ex=0xffffbff0) at /home/parjong/projects/dotnet/coreclr/src/vm/exceptionhandling.cpp:5103
dotnet/coreclr#7  0xf77f6560 in SEHProcessException (exception=0xffffbff0) at /home/parjong/projects/dotnet/coreclr/src/pal/src/exception/seh.cpp:282
dotnet/coreclr#8  0xf77f930c in common_signal_handler (code=11, siginfo=0xffffc14c, sigcontext=0xffffc1cc, numParams=2) at /home/parjong/projects/dotnet/coreclr/src/pal/src/exception/signal.cpp:644
dotnet/coreclr#9  0xf77f7fd7 in sigsegv_handler (code=11, siginfo=0xffffc14c, context=0xffffc1cc) at /home/parjong/projects/dotnet/coreclr/src/pal/src/exception/signal.cpp:295
dotnet/coreclr#10 0xf1d0e313 in sigsegv_handler (code=11, siginfo=0xffffc14c, context=0xffffc1cc) at /home/parjong/projects/dotnet/coreclr/src/pal/src/exception/signal.cpp:303
dotnet/coreclr#11 <signal handler called>
dotnet/coreclr#12 0xf265bc5b in ?? ()
dotnet/coreclr#13 0xf265746e in ?? ()
dotnet/coreclr#14 0xf71304c7 in CallDescrWorkerInternal () at /home/parjong/projects/dotnet/coreclr/src/vm/i386/asmhelpers.S:444
dotnet/coreclr#15 0xf6eb506d in CallDescrWorker (pCallDescrData=0xffffccc8) at /home/parjong/projects/dotnet/coreclr/src/vm/callhelpers.cpp:146
dotnet/coreclr#16 0xf6eb4e16 in CallDescrWorkerWithHandler (pCallDescrData=0xffffccc8, fCriticalCall=0) at /home/parjong/projects/dotnet/coreclr/src/vm/callhelpers.cpp:89
dotnet/coreclr#17 0xf6eb6de7 in MethodDescCallSite::CallTargetWorker (this=0xffffcdf8, pArguments=0xffffcda0, pReturnValue=0x0, cbReturnValue=0) at /home/parjong/projects/dotnet/coreclr/src/vm/callhelpers.cpp:656
dotnet/coreclr#18 0xf6c79059 in MethodDescCallSite::Call (this=0xffffcdf8, pArguments=0xffffcda0) at /home/parjong/projects/dotnet/coreclr/src/vm/callhelpers.h:433
dotnet/coreclr#19 0xf718c9c3 in RunMain(MethodDesc*, short, int*, REF<PtrArray>*)::$_1::operator()(RunMain(MethodDesc*, short, int*, REF<PtrArray>*)::Param*) const::{lambda(RunMain(MethodDesc*, short, int*, REF<PtrArray>*)::Param*)#1}::operator()(RunMain(MethodDesc*, short, int*, REF<PtrArray>*)::Param*) const (this=0xffffcec0, pParam=0xffffcf78) at /home/parjong/projects/dotnet/coreclr/src/vm/assembly.cpp:2637
dotnet/coreclr#20 0xf7189100 in RunMain(MethodDesc*, short, int*, REF<PtrArray>*)::$_1::operator()(RunMain(MethodDesc*, short, int*, REF<PtrArray>*)::Param*) const (this=0xffffcf70, __EXparam=0xffffcf78)
    at /home/parjong/projects/dotnet/coreclr/src/vm/assembly.cpp:2655
dotnet/coreclr#21 0xf7188ede in RunMain (pFD=0xf62c1a60, numSkipArgs=1, piRetVal=0xffffd034, stringArgs=0xffffd3c8) at /home/parjong/projects/dotnet/coreclr/src/vm/assembly.cpp:2655
dotnet/coreclr#22 0xf718940d in Assembly::ExecuteMainMethod (this=0x80bf220, stringArgs=0xffffd3c8, waitForOtherThreads=1) at /home/parjong/projects/dotnet/coreclr/src/vm/assembly.cpp:2762
dotnet/coreclr#23 0xf6c6ae90 in CorHost2::ExecuteAssembly (this=0x805c138, dwAppDomainId=1, pwzAssemblyPath=0x80bcec0 u"/home/parjong/projects/dotnet/coreclr-analyze/Localloc/bin/Debug/netcoreapp1.0/Localloc.dll", argc=0,
    argv=0x0, pReturnValue=0xffffd5cc) at /home/parjong/projects/dotnet/coreclr/src/vm/corhost.cpp:1349
dotnet/coreclr#24 0xf6bf2fb1 in coreclr_execute_assembly (hostHandle=0x805c138, domainId=1, argc=0, argv=0x0,
    managedAssemblyPath=0x805212c "/home/parjong/projects/dotnet/coreclr-analyze/Localloc/bin/Debug/netcoreapp1.0/Localloc.dll", exitCode=0xffffd5cc)
    at /home/parjong/projects/dotnet/coreclr/src/dlls/mscoree/unixinterface.cpp:376
dotnet/runtime#3858 0x0804c60d in ExecuteManagedAssembly (currentExeAbsolutePath=0x8052014 "/home/parjong/projects/dotnet/dotnet-overlay/Linux.x86.Debug.Debug/corerun",
    clrFilesAbsolutePath=0x80520d4 "/home/parjong/projects/dotnet/dotnet-overlay/Linux.x86.Debug.Debug",
    managedAssemblyAbsolutePath=0x805212c "/home/parjong/projects/dotnet/coreclr-analyze/Localloc/bin/Debug/netcoreapp1.0/Localloc.dll", managedAssemblyArgc=0, managedAssemblyArgv=0x0)
    at /home/parjong/projects/dotnet/coreclr/src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp:420
dotnet/runtime#3859 0x0804b1cf in corerun (argc=2, argv=0xffffd7c4) at /home/parjong/projects/dotnet/coreclr/src/coreclr/hosts/unixcorerun/corerun.cpp:149
dotnet/coreclr#27 0x0804b2da in main (argc=2, argv=0xffffd7c4) at /home/parjong/projects/dotnet/coreclr/src/coreclr/hosts/unixcorerun/corerun.cpp:161

At this point, pFirstGSCookie has the following value:

(gdb) p/x pFirstGSCookie
$1 = 0xffffc9

Here is the value of related variables at the frame 2:

(gdb) frame 2
dotnet/coreclr#2  0xf71117b6 in ExceptionTracker::ProcessOSExceptionNotification (this=0x8084870, pExceptionRecord=0x80e2f94, pContextRecord=0x80e2cc8, pDispatcherContext=0xffffb4a8, dwExceptionFlags=0, sf=...,
    pThread=0x8086938, STState=ExceptionTracker::STS_NewException) at /home/parjong/projects/dotnet/coreclr/src/vm/exceptionhandling.cpp:1823
1823                    cfThisFrame.SetCurGSCookie(pGSCookie);
(gdb) p/x cfThisFrame.pRD->pEbp
$2 = 0xf7c9ba94
(gdb) p/x *cfThisFrame.pRD->pEbp
$3 = 0x1000001
(gdb) p/x ControlPc
$4 = 0xf265bc5b

@parjong
Copy link
Contributor Author

parjong commented Jan 19, 2017

@janvorli FYI, here is the prolog of Localloc:

; Assembly listing for method BringUpTest:Localloc(ubyte)
; Emitting BLENDED_CODE for generic X86 CPU
; debuggable code
; ebp based frame
; fully interruptible
; Final local variable assignments
;
;  V00 arg0         [V00,T02] (  7,   7  )   ubyte  ->  [ebp-0x10]
;  V01 loc0         [V01,T03] (  4,   4  )     int  ->  [ebp-0x14]   must-init
;  V02 loc1         [V02,T00] ( 11,  11  )   ubyte  ->  [ebp-0x18]   must-init
;  V03 loc2         [V03,T08] (  2,   2  )    bool  ->  [ebp-0x1C]   must-init
;  V04 loc3         [V04,T09] (  2,   2  )    bool  ->  [ebp-0x20]   must-init
;  V05 tmp0         [V05,T01] (  6,  12  )     ref  ->  [ebp-0x24]   must-init
;  V06 tmp1         [V06,T04] (  2,   4  )     ref  ->  [ebp-0x28]   must-init
;  V07 tmp2         [V07,T05] (  2,   4  )     ref  ->  [ebp-0x2C]   must-init
;  V08 tmp3         [V08,T06] (  2,   4  )     ref  ->  [ebp-0x30]   must-init
;  V09 tmp4         [V09,T07] (  2,   4  )     ref  ->  [ebp-0x34]   must-init
;* V10 GsCookie     [V10    ] (  0,   0  )     int  ->  zero-ref    do-not-enreg[X] must-init addr-exposed
;  V11 LocAllocSP   [V11    ] (  1,   1  )     int  ->  [ebp-0x0C]   do-not-enreg[X] must-init addr-exposed
;
; Lcl frame size = 48

G_M31705_IG01:
       55           push     ebp
       8BEC         mov      ebp, esp
       57           push     edi
       56           push     esi
       83EC30       sub      esp, 48
       8BF1         mov      esi, ecx
       8D7DC8       lea      edi, [ebp-38H]
       B90C000000   mov      ecx, 12
       33C0         xor      eax, eax
       F3AB         rep stosd
       8BCE         mov      ecx, esi
       8965F4       mov      dword ptr [ebp-0CH], esp
       C745C878563412 mov      dword ptr [ebp-38H], 0x12345678
       894DF0       mov      dword ptr [ebp-10H], ecx

As I understand, it updates GSCookie at ebp - 38H via the following instruction:

C745C878563412 mov      dword ptr [ebp-38H], 0x12345678

parjong referenced this issue in parjong/coreclr Feb 1, 2017
GetGSCookieAddress uses pEbp to get the current frame pointer, but pEbp
is not properly initialized as discussed in #8980.

This commit revises GetGSCookieAddress to use CallerSp (as in other
architectures) to get Frame Pointer in order to fix #8980.
janvorli referenced this issue in dotnet/coreclr Feb 1, 2017
GetGSCookieAddress uses pEbp to get the current frame pointer, but pEbp
is not properly initialized as discussed in #8980.

This commit revises GetGSCookieAddress to use CallerSp (as in other
architectures) to get Frame Pointer in order to fix #8980.
@msftgits msftgits transferred this issue from dotnet/coreclr Jan 31, 2020
@msftgits msftgits added this to the Future milestone Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 26, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.