{"record":{"id":"fdd90096ace45863","repo":"dotnet/runtime","slug":"mono-marshal-safehandles-missing-managed-conv-out","errorCode":null,"errorMessage":"mono/marshal: SafeHandles missing MANAGED_CONV_OUT\\n","messagePattern":"mono/marshal: SafeHandles missing MANAGED_CONV_OUT\\\\n","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/mono/mono/component/marshal-ilgen.c","lineNumber":2042,"sourceCode":"\n\t\t/* Create return value */\n\t\tcb_to_mono->methodBuilder.emit_op (mb, CEE_NEWOBJ, ctor);\n\t\tcb_to_mono->methodBuilder.emit_stloc (mb, 3);\n\n\t\t/* Set the return.handle to the value, am using ldflda, not sure if thats a good idea */\n\t\tcb_to_mono->methodBuilder.emit_ldloc (mb, 3);\n\t\tcb_to_mono->methodBuilder.emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoSafeHandle, handle));\n\t\tcb_to_mono->methodBuilder.emit_ldloc (mb, intptr_handle_slot);\n\t\tcb_to_mono->methodBuilder.emit_byte (mb, CEE_STIND_I);\n\t\tbreak;\n\t}\n\n\tcase MARSHAL_ACTION_MANAGED_CONV_IN:\n\t\tfprintf (stderr, \"mono/marshal: SafeHandles missing MANAGED_CONV_IN\\n\");\n\t\tbreak;\n\n\tcase MARSHAL_ACTION_MANAGED_CONV_OUT:\n\t\tfprintf (stderr, \"mono/marshal: SafeHandles missing MANAGED_CONV_OUT\\n\");\n\t\tbreak;\n\n\tcase MARSHAL_ACTION_MANAGED_CONV_RESULT:\n\t\tfprintf (stderr, \"mono/marshal: SafeHandles missing MANAGED_CONV_RESULT\\n\");\n\t\tbreak;\n\tdefault:\n\t\tprintf (\"Unhandled case for MarshalAction: %d\\n\", action);\n\t}\n\treturn conv_arg;\n}\n\nstatic int\nemit_marshal_handleref_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,\n\t\t\tMonoMarshalSpec *spec, int conv_arg,\n\t\t\tMonoType **conv_arg_type, MarshalAction action)\n{\n\tMonoMethodBuilder *mb = m->mb;\n","sourceCodeStart":2024,"sourceCodeEnd":2060,"githubUrl":"https://github.com/dotnet/runtime/blob/60108ba66eb7d1d12f595480091b4ad80a24b172/src/mono/mono/component/marshal-ilgen.c#L2024-L2060","documentation":"Companion to the MANAGED_CONV_IN case in emit_marshal_safehandle_ilgen: the MARSHAL_ACTION_MANAGED_CONV_OUT action (copying a SafeHandle's state back out after a reverse P/Invoke call returns) is unimplemented. The marshaller prints this to stderr and emits no IL, so outbound state propagation for the SafeHandle is silently skipped.","triggerScenarios":"A reverse P/Invoke (native->managed) call has a SafeHandle passed by-reference or with out semantics, so the marshaller must write the handle back after the managed call; reaching this case means that write-back IL was not emitted.","commonSituations":"Mono reverse P/Invoke with a 'ref SafeHandle' or 'out SafeHandle' parameter; COM interop where the managed method mutates a SafeHandle that must flow back to native; code ported from CoreCLR that relies on full SafeHandle two-way marshalling.","solutions":["Do not pass SafeHandle by ref/out across the native->managed boundary on Mono; use IntPtr and re-wrap.","Perform handle write-back manually via a second callback or a shared native slot.","Move the SafeHandle ownership fully to one side of the boundary to avoid bidirectional marshalling.","Use a runtime that implements the MANAGED_CONV_OUT path for SafeHandle (CoreCLR/NativeAOT)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Flag ref/out SafeHandle on reverse-P/Invoke delegates (Mono MANAGED_CONV_OUT unsupported).\nstatic IEnumerable<ParameterInfo> CheckByRefSafeHandles(Assembly asm) {\n    foreach (var t in asm.GetTypes())\n      foreach (var m in t.GetMethods(BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance)) {\n        if (m.GetCustomAttribute<UnmanagedFunctionPointerAttribute>() == null) continue;\n        foreach (var p in m.GetParameters())\n          if ((p.ParameterType.IsByRef) &&\n              typeof(SafeHandle).IsAssignableFrom(p.ParameterType.GetElementType()))\n            yield return p;\n      }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Avoid ref/out SafeHandle across the native->managed boundary on Mono.","Marshal a ref IntPtr and reconstruct the SafeHandle manually in managed code.","Keep SafeHandle ownership on one side of the boundary.","Build-time scan interop signatures for by-ref SafeHandles."],"tags":["mono","marshal","safehandle","reverse-pinvoke","interop","ilgen"],"backgroundTag":null,"analyzedSha":"60108ba66eb7d1d12f595480091b4ad80a24b172","analyzedAt":"2026-08-10T18:54:11.478Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}