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

Exception handling on Windows causing access violation with ARC #222

Closed
triplef opened this issue Jan 18, 2022 · 7 comments
Closed

Exception handling on Windows causing access violation with ARC #222

triplef opened this issue Jan 18, 2022 · 7 comments

Comments

@triplef
Copy link
Member

triplef commented Jan 18, 2022

Throwing/catching an exception in ARC-enabled code on Windows causes an access violation. I’ve reproduced this in various environments/projects and both x64 and x86 with the same results. While debug builds will usually crash almost immediately, release builds can sometimes continue execution for a bit before crashing.

I also tried using Application Verifier and Page Heap to find the cause, but unfortunately I never got any more useful info. The stack trace is usually corrupt and/or unhelpful.

This can for example be reproduced with the ObjCXXEHInterop_arc test, which is currently disabled for Windows due to the failure:

Exception thrown at 0x00007FFA9FC522C8 (objc.dll) in ObjCXXEHInterop_arc.exe: 0xE06D7363: Microsoft C++ Exception (parameters: 0x0000000019930520, 0x0000003160DFF6A8, 0x0000003160DFF5D8, 0x0000003160DFF6A7).
ObjCXXEHInterop_arc.exe has triggered a breakpoint.

ObjCXXEHInterop_arc.exe has triggered a breakpoint.

Exception thrown at 0x00007FFA9FC25280 (vcruntime140d.dll) in ObjCXXEHInterop_arc.exe: 0xC0000005: Access violation executing location 0x00007FFA9FC25280.

Any thoughts on how to debug this further would be appreciated.

@davidchisnall
Copy link
Member

Normally I'd debug this kind of thing in WinDbg using its time-travel mode: get to the crash, look where the pointer to the faulting address came from, and then run in reverse with a watchpoint set to that address and see where it happened. It may be that we're failing to retain / release over exception throwing (though I thought the compiler did that in ARC mode?), but if you can find out what 0x00007FFA9FC25280 used to point to with the time-travel debugger then you can see where it was deallocated.

@triplef
Copy link
Member Author

triplef commented Jan 19, 2022

That sounds like a good plan. Unfortunately I haven’t really used WinDbg before and are not really getting anywhere with it...

For starters, could you tell me how to find out where the pointer to the faulting address came from?

I used this tutorial as guideline and are getting a list of events that shows the C++ exception that is thrown directly followed by the access violation exception. It would be great if you could give me some pointers where to go from there.

One interesting tidbit maybe is that on x86 the faulting address is always 0x100:

    [0xf]            : Exception 0xE06D7363 of type CPlusPlus at PC: 0X76F68A80
    [0x10]           : Exception 0xC0000005 of type Hardware at PC: 0X100

Whereas on x64 it’s always some actual memory address:

    [0x10]           : Exception 0xE06D7363 of type CPlusPlus at PC: 0X7FFB3B901020
    [0x11]           : Exception 0x80000003 of type Hardware at PC: 0X7FF6C72C10EE

weliveindetail added a commit to weliveindetail/llvm-project that referenced this issue Jun 14, 2022
Unwinding ObjC code with automatic reference counting involves calls to intrinsics like `llvm.obj.retain` and `llvm.objc.destroyWeak`. Exception handling on Windows is based on funclets and WinEHPrepare only allows calls to `nounwind` intrinsics. This works just fine, except for ObjC `nounwind` intrinsics, because these are lowered into regular function calls in an earlier stage already (i.e. PreISelIntrinsicLoweringPass). Thus, WinEHPrepare accidentally drops them as implausible instructions and silently truncates certain funclets. Eventually, this causes crashes during unwinding on Windows with the GNUstep ObjC runtime: gnustep/libobjc2#222

This patch forces the emission of a "funclet" bundle operand for calls to ObjC ARC intrinsics during codegen and lets PreISelIntrinsicLowering carry it over onto lowered replacement calls. This way all ObjC ARC calls survive WinEHPrepare through the FuncletBundleOperand check. The latter had to be adjusted in order to allow funclet pads to get optimized away.

Differential Revision: https://reviews.llvm.org/D124762
weliveindetail added a commit to weliveindetail/llvm-project that referenced this issue Jun 20, 2022
…er to function calls

WinEHPrepare marks any function call from EH funclets as unreachable, if it's not a nounwind intrinsic and has no proper funclet bundle operand. This affects ARC intrinsics on Windows, because they are lowered to regular function calls in the PreISelIntrinsicLowering pass. It caused silent binary truncations and crashes during unwinding with the GNUstep ObjC runtime: gnustep/libobjc2#222

This patch adds a new function `llvm::IntrinsicInst::mayLowerToFunctionCall()` that aims to collect all affected intrinsic IDs.
* Clang CodeGen uses it to determine whether or not it must emit a funclet bundle operand.
* PreISelIntrinsicLowering asserts that the function returns true for all ObjC runtime calls it lowers.
* LLVM uses it to determine whether or not a funclet bundle operand must be propagated to inlined call sites.

Differential Revision: https://reviews.llvm.org/D128190
weliveindetail added a commit to llvm/llvm-project that referenced this issue Jul 26, 2022
…er to function calls in the course of IR transforms

WinEHPrepare marks any function call from EH funclets as unreachable, if it's not a nounwind intrinsic or has no proper funclet bundle operand. This
affects ARC intrinsics on Windows, because they are lowered to regular function calls in the PreISelIntrinsicLowering pass. It caused silent binary truncations and crashes during unwinding with the GNUstep ObjC runtime: gnustep/libobjc2#222

This patch adds a new function `llvm::IntrinsicInst::mayLowerToFunctionCall()` that aims to collect all affected intrinsic IDs.
* Clang CodeGen uses it to determine whether or not it must emit a funclet bundle operand.
* PreISelIntrinsicLowering asserts that the function returns true for all ObjC runtime calls it lowers.
* LLVM uses it to determine whether or not a funclet bundle operand must be propagated to inlined call sites.

Reviewed By: theraven

Differential Revision: https://reviews.llvm.org/D128190
@triplef
Copy link
Member Author

triplef commented Sep 15, 2022

We got this fixed in Clang 15:
https://reviews.llvm.org/D128190

@triplef triplef closed this as completed Sep 15, 2022
triplef added a commit to gnustep/tools-windows-msvc that referenced this issue Sep 27, 2022
@weliveindetail
Copy link
Contributor

I was about to propose a change that enabled the test on Windows when using Clang 15+ as the host compiler, when I realized that it still crashes! The nested try-catch in this test actually triggers another problem that was not covered in my fix from https://reviews.llvm.org/D128190

@weliveindetail
Copy link
Contributor

Review for a fix/workaround candidate: https://reviews.llvm.org/D134866

@triplef triplef reopened this Oct 7, 2022
@weliveindetail
Copy link
Contributor

I was working on a PR to enable the ObjCXXEHInterop_arc tests on Windows in Clang 15+ when I realized that there is a second side-effect that we didn't fix with the above patch. It does affect the test here, so I didn't sent the PR yet. More info and the start of what may become a more general discussion on the concepts behind WinEH: https://reviews.llvm.org/D134866

mem-frob pushed a commit to draperlaboratory/hope-llvm-project that referenced this issue Oct 7, 2022
…er to function calls in the course of IR transforms

WinEHPrepare marks any function call from EH funclets as unreachable, if it's not a nounwind intrinsic or has no proper funclet bundle operand. This
affects ARC intrinsics on Windows, because they are lowered to regular function calls in the PreISelIntrinsicLowering pass. It caused silent binary truncations and crashes during unwinding with the GNUstep ObjC runtime: gnustep/libobjc2#222

This patch adds a new function `llvm::IntrinsicInst::mayLowerToFunctionCall()` that aims to collect all affected intrinsic IDs.
* Clang CodeGen uses it to determine whether or not it must emit a funclet bundle operand.
* PreISelIntrinsicLowering asserts that the function returns true for all ObjC runtime calls it lowers.
* LLVM uses it to determine whether or not a funclet bundle operand must be propagated to inlined call sites.

Reviewed By: theraven

Differential Revision: https://reviews.llvm.org/D128190
@triplef
Copy link
Member Author

triplef commented Oct 4, 2023

This was fully fixed in Clang 16.

@triplef triplef closed this as completed Oct 4, 2023
arichardson pushed a commit to CTSRD-CHERI/llvm-project that referenced this issue Jan 9, 2024
…er to function calls in the course of IR transforms

WinEHPrepare marks any function call from EH funclets as unreachable, if it's not a nounwind intrinsic or has no proper funclet bundle operand. This
affects ARC intrinsics on Windows, because they are lowered to regular function calls in the PreISelIntrinsicLowering pass. It caused silent binary truncations and crashes during unwinding with the GNUstep ObjC runtime: gnustep/libobjc2#222

This patch adds a new function `llvm::IntrinsicInst::mayLowerToFunctionCall()` that aims to collect all affected intrinsic IDs.
* Clang CodeGen uses it to determine whether or not it must emit a funclet bundle operand.
* PreISelIntrinsicLowering asserts that the function returns true for all ObjC runtime calls it lowers.
* LLVM uses it to determine whether or not a funclet bundle operand must be propagated to inlined call sites.

Reviewed By: theraven

Differential Revision: https://reviews.llvm.org/D128190
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants