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

[mono] inflate function pointer types; hang indirect call wrappers on the <Module> #106837

Merged
merged 3 commits into from
Aug 23, 2024

Conversation

lambdageek
Copy link
Member

  1. We were missing a case for MONO_TYPE_FNPTR in inflate_generic_type

  2. When an indirect call was inside of a method that was part of a generic class, we were creating wrapper methods for the indirect calls parented by the GTD type of the caller. This ended up making common_call_trampoline consider the wrapper as needing an mrgctx. But if the caller was actually a normal closed instance type no mrgctx is set up. So we end up dereferencing a null pointer. Instead, when we create an indirect call wrapper method (which we asserted has no type params and which is always just sets up the GC transition and forwards the arguments to the indirect callee - so it never depends on the generic context), make its parent be the class of the calling assembly. This is consistent with how we cache these indirect methods (in the calling image, by the signature of the indirect callee) - it has nothing to do with the caller class.

Fixes #106811

… the <Module>

1. We were missing a case for `MONO_TYPE_FNPTR` in `inflate_generic_type`

2. When an indirect call was inside of a method that was part of a
generic class, we were creating wrapper methods for the indirect calls
parented by the GTD type of the caller.  This ended up making
common_call_trampoline consider the wrapper as needing an mrgctx.  But
if the caller was actually a normal closed instance type no mrgctx is
set up.  So we end up dereferencing a null pointer.  Instead, when we
create an indirect call wrapper method (which we asserted has no type
params and which is always just sets up the GC transition and forwards
the arguments to the indirect callee - so it never depends on the
generic context), make its parent be the <Module> class of the calling
assembly.  This is consistent with how we cache these indirect
methods (in the calling image, by the signature of the indirect
callee) - it has nothing to do with the caller class.

Fixes dotnet#106811
@lambdageek
Copy link
Member Author

lambdageek commented Aug 22, 2024

Update added.


Need a testcase, I think.

Here's a self-contained repro:

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;


namespace HelloWorld
{
    public static class InteropUtil
    {
        [UnmanagedCallersOnly]
        private static void Bar(int x) => Console.WriteLine ($"Bar {x}");
        
        //[DllImport("libnative.dylib")]
        public unsafe static /*extern*/ IntPtr getNativeFunctionPointer() => (IntPtr)(void*)(delegate* unmanaged<int,void>)&InteropUtil.Bar;
    }

    unsafe public class NativeInvoker<T>
    {
        [MethodImpl(MethodImplOptions.NoInlining)]
        T Foo(T x) => x;
        public void Invoke(IntPtr fptr, T arg)
        {
            T x = Foo(arg);
            var func = (delegate* unmanaged<T, void>)fptr;
            func(x);
        }
    }
    internal class Program
    {
        private static void Main(string[] args)
        {
            var invoker = new NativeInvoker<int>();
            invoker.Invoke(InteropUtil.getNativeFunctionPointer(), 42);
        }
    }
}

src/mono/mono/metadata/class.c Outdated Show resolved Hide resolved
Copy link
Contributor

@kg kg left a comment

Choose a reason for hiding this comment

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

Code looks correct, not certain about the nuances though

Copy link
Member

@jkurdek jkurdek left a comment

Choose a reason for hiding this comment

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

Thanks for looking into that!

src/mono/mono/metadata/class.c Outdated Show resolved Hide resolved
lambdageek and others added 2 commits August 23, 2024 09:34
if the cmods changed, but the sig didn't we want to return `type`.
return NULL only if there's an error or if nothing else changed
@srxqds
Copy link
Contributor

srxqds commented Aug 24, 2024

should this consider merge to release/9.0?

@lambdageek
Copy link
Member Author

/backport to release/9.0

Copy link
Contributor

Started backporting to release/9.0: https://github.com/dotnet/runtime/actions/runs/10539085367

@lambdageek
Copy link
Member Author

/backport to release/8.0-staging

Copy link
Contributor

Started backporting to release/8.0-staging: https://github.com/dotnet/runtime/actions/runs/10568262373

Copy link
Contributor

@lambdageek backporting to release/8.0-staging failed, the patch most likely resulted in conflicts:

$ git am --3way --ignore-whitespace --keep-non-patch changes.patch

Applying: [mono] inflate function pointer types; hang indirect call wrappers on the <Module>
Applying: pay attention to `changed` when inflating
Applying: add regression test
Using index info to reconstruct a base tree...
A	src/tests/Interop/MarshalAPI/FunctionPointer/GenericFunctionPointer.cs
Falling back to patching base and 3-way merge...
CONFLICT (modify/delete): src/tests/Interop/MarshalAPI/FunctionPointer/GenericFunctionPointer.cs deleted in HEAD and modified in add regression test. Version add regression test of src/tests/Interop/MarshalAPI/FunctionPointer/GenericFunctionPointer.cs left in tree.
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config advice.mergeConflict false"
Patch failed at 0003 add regression test
Error: The process '/usr/bin/git' failed with exit code 128

Please backport manually!

Copy link
Contributor

@lambdageek an error occurred while backporting to release/8.0-staging, please check the run log for details!

Error: git am failed, most likely due to a merge conflict.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Mono JIT] common_call_trampoline SIGSEGV when invoking native function pointer
4 participants