-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Fix x86 stackwalk for UnmanagedCallersOnly #61075
Fix x86 stackwalk for UnmanagedCallersOnly #61075
Conversation
Yet another similar issue to the recently fixed x86 EH for methods marked with UnmanagedCallersOnly, but called from a managed method using a function pointer. The stack walker was asserting in VerifyValidTransitionFromManagedCode. The REGDISPLAY values that it used for checking the code location were not set for this case, so EIP and ESP were both set to 0. This change fixes it by adding UpdateRegDisplay method to UMThkCallFrame.
Is this a problem from |
If it is possible to call an UnmanagedCallersOnly method via COM from a managed method so that there are no intermediate native frames, then it would probably suffer from the same issue. @AaronRobinsonMSFT can such a case exist? |
@janvorli How about an example of what I think you are saying. Is the following the question? If so, this is completely reasonable code. using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
unsafe class Program
{
struct Vtable
{
public delegate* unmanaged[Stdcall]<Inst*, int> Method1;
}
struct Inst
{
public Vtable* vptr;
}
[UnmanagedCallersOnly(CallConvs = new[] {typeof(CallConvStdcall)})]
static int MethodImpl(Inst* p) => throw new Exception();
static void Main()
{
var vptr = (Vtable*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(Program), sizeof(Vtable));
vptr->Method1 = &MethodImpl;
var inst = new Inst() {vptr = vptr};
// Imagine inst was ambiguous - could be a real COM object
// or a managed COM wrapper we don't know about.
inst.vptr->Method1(&inst);
}
} |
@AaronRobinsonMSFT my question was about real COM calls that use ComMethodFrame. I assume your example doesn't end up using that frame, does it? |
Hmmm, I am actually wondering how the fix in What is the exact set of conditions that triggers the bug? |
Here is the callstack that triggers the problem:
As expected, there is no I think we will get into the same situation with COM. |
I am sorry for confusing the explanation. But I believe the |
@jkotas actually, let me correct myself. After sorting this in my head again, I have found I've confused it in my previous comment and I was right in my original description. The issue is really caused by the fact that we call a managed function marked using the |
src/coreclr/vm/i386/cgenx86.cpp
Outdated
#undef CALLEE_SAVED_REGISTER | ||
|
||
pRD->ControlPC = *PTR_PCODE(pRD->PCTAddr); | ||
UINT cbStackPop = GetMethod()->CbStackPop(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I am reading the code correctly, GetMethod
is going to return the managed method that the UMThunk or ComMethodFrame is wrapping.
Is it the managed method to ask for CbStackPop
? I suspect we need to CbStackPop
for the unmanaged signature with unmanaged view for the arguments here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, yes, you are right, I have forgotten that the unmanaged view can be different.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@janvorli Do we have a test for this? If you can offer a simple strawman example the Interop team can help with adding the appropriate testing assets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AaronRobinsonMSFT to test this fully, we would need:
- A COM test that exposes a managed method via COM and calls it from other managed method
- That method would need to have arguments passed on stack
- The unmanaged signature would need to differ from the managed one in the size of arguments passed on stack.
It would be great if someone from the interop team can help me with creating that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AaronRobinsonMSFT I also have a question about getting the unmanaged signature for COM method. Would the right one be the one returned by the ComCallMethodDesc::GetCallMethodDesc
?
@janvorli is this ok to merge? |
ok, thanks for the update. |
@jkotas @AaronRobinsonMSFT helped me to look more into the |
38bf7d3
to
4e50993
Compare
It would be difficult and useless to attempt to correctly handle the case when the caller is a native code. Getting the stack arguments size for the caller seems to be difficult, since in some cases, we reach the UpdateRegDisplay before the reverse pinvoke IL stub and even its MethodDesc are created. The case that we care about is simpler, as the caller side and callee side signatures are the same, so we can extract the stack arguments size using the callee MethodDesc.
@jkotas I was trying to find a way to get the callee side signature for getting the correct stack arguments size. I have realized that for our problematic case, the caller and callee view of the signatures are the same and we don't need to use the UpdateRegDisplay for the other cases. It would be difficult to attempt to correctly handle the case when the caller So I have modified my change to let the UpdateRegDisplay do its work only for the case when the caller is a managed method. |
It is just a pure accident that the callback has If the signature of the callback was Also, I am puzzled by the check for managed caller.
Yes, I agree. I think we would have to compute ahead of time in |
When a managed caller calls a managed method marked by the
That's the only case when the problem can occur. The stack walker walks to the |
MethodDesc::CbStackPop does not return the correct amount for methods marked with |
UMThkCallFrame is only used for marshaled delegates. It is not used for plain vanilla That makes me think: Can this problem be fixed by deleting |
#63826 . Let's see what the CI says about it. |
That's great! The CI seems to be happy with it. |
Replaced by #63826 |
Yet another similar issue to the recently fixed x86 EH for methods
marked with UnmanagedCallersOnly, but called from a managed method using
a function pointer. The stack walker was asserting in
VerifyValidTransitionFromManagedCode. The REGDISPLAY values that it used
for checking the code location were not set for this case, so EIP and ESP
were both set to 0.
This change fixes it by adding UpdateRegDisplay method to
UMThkCallFrame.
Close #60648
Close #62728