-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Description
If marshalling ilstub compilation is allowed in crossgen2, ansi string marshalling for string allocated in native code leads to "free(): invalid pointer".
Problem is related to out string argument marshalling, which tries to free native_pointer - sizeof(void*).
However, it seems that this problem might arise in SPC.dll even without any changes in crossgen2 because currently all such ilstubs are compiled, see GeneratesPInvoke:
if (((EcmaMethod)method).Module.Equals(method.Context.SystemModule))
return true;Simple example to reproduce:
c# part:
using System;
using System.Runtime.InteropServices;
namespace ndirect
{
class Program
{
[DllImport("tmp.so", EntryPoint = "test_func")]
internal static extern int testFunc(out string value);
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
string a;
testFunc(out a);
Console.WriteLine(a);
}
}
}native part, compiled with gcc -shared -fPIC 1.c -o tmp.so
#include <malloc.h>
#include <string.h>
int test_func(char ** str)
{
char * s = (char*) malloc(100);
*str = s;
strcpy(s, "my test string!");
return strlen(s);
}Output:
Hello World!
free(): invalid pointer
Aborted (core dumped)
Native code of compiled ilstub, which crashes:
10a10: 55 push rbp
UWOP_PUSH_NONVOL RBP(5) 0
10a11: 41 57 push r15
UWOP_PUSH_NONVOL R15(15) 0
10a13: 41 56 push r14
UWOP_PUSH_NONVOL R14(14) 0
10a15: 41 55 push r13
UWOP_PUSH_NONVOL R13(13) 0
10a17: 41 54 push r12
UWOP_PUSH_NONVOL R12(12) 0
10a19: 53 push rbx
UWOP_PUSH_NONVOL RBX(3) 0
10a1a: 48 83 ec 78 sub rsp, 120
UWOP_ALLOC_SMALL 120 0
10a1e: 48 8d ac 24 a0 00 00 00 lea rbp, [rsp + 160]
10a26: 33 c0 xor eax, eax
10a28: 48 89 45 d0 mov qword ptr [rbp - 48], rax
10a2c: 48 89 a5 60 ff ff ff mov qword ptr [rbp - 160], rsp
10a33: 48 89 7d c8 mov qword ptr [rbp - 56], rdi
10a37: 48 8d 7d d0 lea rdi, [rbp - 48]
10a3b: 48 89 7d c0 mov qword ptr [rbp - 64], rdi
10a3f: 48 8d bd 68 ff ff ff lea rdi, [rbp - 152]
10a46: ff 15 ec 08 01 00 call qword ptr [0x21338] // PINVOKE_BEGIN (HELPER)
10a4c: 48 8b 05 25 09 01 00 mov rax, qword ptr [0x21378] // int ndirect.Program.testFunc(ref string) (INDIRECT_PINVOKE_TARGET)
10a53: 48 8b 7d c0 mov rdi, qword ptr [rbp - 64]
10a57: ff 10 call qword ptr [rax]
10a59: 8b d8 mov ebx, eax
10a5b: 48 8d bd 68 ff ff ff lea rdi, [rbp - 152]
10a62: ff 15 d8 08 01 00 call qword ptr [0x21340] // PINVOKE_END (HELPER)
10a68: 48 8b 7d d0 mov rdi, qword ptr [rbp - 48]
10a6c: ff 15 e6 08 01 00 call qword ptr [0x21358] // string System.StubHelpers.AnsiBSTRMarshaler.ConvertToManaged(IntPtr) (METHOD_ENTRY_DEF_TOKEN)
RAX is live
10a72: 48 8b 7d c8 mov rdi, qword ptr [rbp - 56]
RDI is live
10a76: 48 8b f0 mov rsi, rax
RSI is live
sp-56 is dead
10a79: ff 15 b1 08 01 00 call qword ptr [0x21330] // CHECKED_WRITE_BARRIER (HELPER)
RDI is dead
RAX is dead
RSI is dead
10a7f: 90 nop
10a80: 48 8b 7d d0 mov rdi, qword ptr [rbp - 48]
10a84: ff 15 d6 08 01 00 call qword ptr [0x21360] // void System.StubHelpers.AnsiBSTRMarshaler.ClearNative(IntPtr) (METHOD_ENTRY_DEF_TOKEN)
10a8a: 8b c3 mov eax, ebx
10a8c: 48 83 c4 78 add rsp, 120
10a90: 5b pop rbx
10a91: 41 5c pop r12
10a93: 41 5d pop r13
10a95: 41 5e pop r14
10a97: 41 5f pop r15
10a99: 5d pop rbp
10a9a: c3 ret
Patch to allow marshalling ilstub compilation:
commit 129e85a182b9ed8561b3428f647df8dcf93ae49d
Author: Gleb Balykov <g.balykov@samsung.com>
Date: Mon Jun 28 12:53:58 2021 +0300
Partially allow marshalling ILSTUB compilation for ndirect methods using crossgen2 if SPC.dll is in the same bubble
AnsiStringMarshaller is disallowed because it incorrectly handles pointers returned from native code.
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs
index 92ef5346c77..eaa2821ac94 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs
@@ -329,10 +329,8 @@ public sealed override bool GeneratesPInvoke(MethodDesc method)
{
// PInvokes depend on details of the core library, so for now only compile them if:
// 1) We're compiling the core library module, or
- // 2) We're compiling any module, and no marshalling is needed
- //
- // TODO Future: consider compiling PInvokes with complex marshalling in version bubble
- // mode when the core library is included in the bubble.
+ // 2) We're compiling any module, and no marshalling is needed, or
+ // 3) We're compiling any module, and core library module is in the same bubble, and marhaller supports compilation
Debug.Assert(method is EcmaMethod);
@@ -344,6 +342,9 @@ public sealed override bool GeneratesPInvoke(MethodDesc method)
if (((EcmaMethod)method).Module.Equals(method.Context.SystemModule))
return true;
+ if (_versionBubbleModuleSet.Contains(method.Context.SystemModule))
+ return !Marshaller.IsMarshallingNotSupported(method);
+
return !Marshaller.IsMarshallingRequired(method);
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Interop/IL/Marshaller.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Interop/IL/Marshaller.ReadyToRun.cs
index 636c698e8d5..4bc3d1340e2 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Interop/IL/Marshaller.ReadyToRun.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Interop/IL/Marshaller.ReadyToRun.cs
@@ -141,5 +141,37 @@ public static bool IsMarshallingRequired(MethodSignature methodSig, ParameterMet
return false;
}
+
+ public static bool IsMarshallingNotSupported(MethodDesc targetMethod)
+ {
+ Debug.Assert(targetMethod.IsPInvoke);
+
+ if (targetMethod.IsUnmanagedCallersOnly)
+ return true;
+
+ PInvokeMetadata metadata = targetMethod.GetPInvokeMethodMetadata();
+ PInvokeFlags flags = metadata.Flags;
+
+ if (flags.SetLastError)
+ return true;
+
+ if (!flags.PreserveSig)
+ return true;
+
+ if (MarshalHelpers.ShouldCheckForPendingException(targetMethod.Context.Target, metadata))
+ return true;
+
+ var marshallers = GetMarshallersForMethod(targetMethod);
+ for (int i = 0; i < marshallers.Length; i++)
+ {
+ if (marshallers[i].GetType() == typeof(NotSupportedMarshaller))
+ return true;
+ }
+
+ return false;
+ }
}
}More detailed description
This problem happens inside System.StubHelpers.AnsiBSTRMarshaler.ClearNative, which tries to cleanup bstr buffer, and subtracts sizeof(void*) from ptr (Marshal.FreeBSTR in Marshal.Unix.cs actually performs subtraction). This itself happens because System.StubHelpers.AnsiBSTRMarshaler.ConvertToManaged for some reason ignores non-bstr buffers. However, pointer returned from native code is not bstr, but a simple buffer. So, incorrect pointer is passed to free.