Skip to content

Commit 5c7dbe2

Browse files
authored
Merge pull request #143 from NordSecurity/kristupas/vtable-lifetime
Fix callback vtable lifetime
2 parents e4f18be + b08aab8 commit 5c7dbe2

File tree

2 files changed

+20
-14
lines changed

2 files changed

+20
-14
lines changed

bindgen/src/gen_cs/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,9 +393,9 @@ impl CsCodeOracle {
393393
FfiType::RustBuffer(_) => "RustBuffer".to_string(),
394394
FfiType::ForeignBytes => "ForeignBytes".to_string(),
395395
FfiType::Callback(_) => "IntPtr".to_string(),
396-
FfiType::Reference(typ) => format!("ref {}", self.ffi_type_label(typ, prefix_struct)),
396+
FfiType::Reference(typ) => format!("IntPtr /*{}*/", self.ffi_type_label(typ, prefix_struct)),
397397
FfiType::MutReference(typ) => {
398-
format!("ref {}", self.ffi_type_label(typ, prefix_struct))
398+
format!("IntPtr /*{}*/", self.ffi_type_label(typ, prefix_struct))
399399
}
400400
FfiType::RustCallStatus => "UniffiRustCallStatus".to_string(),
401401
FfiType::Struct(name) => {

bindgen/templates/CallbackInterfaceImpl.cs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ class {{ callback_impl_name }} {
2222

2323
{%- match meth.return_type() %}
2424
{%- when Some with (return_type) %}
25-
@uniffiOutReturn = {{ return_type|ffi_converter_name }}.INSTANCE.Lower(result);
25+
unsafe {
26+
{%- let return_ffi_type = return_type|ffi_type %}
27+
*({{ return_ffi_type|ffi_type_name }}*)uniffiOutReturn = {{ return_type|ffi_converter_name }}.INSTANCE.Lower(result);
28+
}
2629
{%- when None %}
2730
{%- endmatch %}
2831

@@ -109,8 +112,10 @@ class {{ callback_impl_name }} {
109112
}, cts.Token);
110113

111114
var foreignHandle = _UniFFIAsync._foreign_futures_map.Insert(cts);
112-
@uniffiOutReturn.@handle = foreignHandle;
113-
@uniffiOutReturn.@free = Marshal.GetFunctionPointerForDelegate(_UniFFIAsync.UniffiForeignFutureFreeCallback.callback);
115+
unsafe {
116+
(*(_UniFFILib.UniffiForeignFuture*)uniffiOutReturn).handle = foreignHandle;
117+
(*(_UniFFILib.UniffiForeignFuture*)uniffiOutReturn).free = Marshal.GetFunctionPointerForDelegate(_UniFFIAsync.UniffiForeignFutureFreeCallback.callback);;
118+
}
114119
{%- endif %}
115120
} else {
116121
throw new InternalException($"No callback in handlemap '{handle}'");
@@ -128,16 +133,17 @@ static void UniffiFree(ulong @handle) {
128133
{%- endfor %}
129134
static _UniFFILib.UniffiCallbackInterfaceFree _callback_interface_free = new _UniFFILib.UniffiCallbackInterfaceFree(UniffiFree);
130135

131-
public static _UniFFILib.{{ vtable|ffi_type_name }} _vtable = new _UniFFILib.{{ vtable|ffi_type_name }} {
132-
{%- for (ffi_callback, meth) in vtable_methods.iter() %}
133-
{%- let fn_type = format!("_UniFFILib.{}Method", callback_impl_name) %}
134-
{{ meth.name()|var_name() }} = Marshal.GetFunctionPointerForDelegate(_m{{ loop.index0 }}),
135-
{%- endfor %}
136-
@uniffiFree = Marshal.GetFunctionPointerForDelegate(_callback_interface_free)
137-
};
138-
139136
public static void Register() {
140-
_UniFFILib.{{ ffi_init_callback.name() }}(ref {{ callback_impl_name }}._vtable);
137+
_UniFFILib.{{ vtable|ffi_type_name }} _vtable = new _UniFFILib.{{ vtable|ffi_type_name }} {
138+
{%- for (ffi_callback, meth) in vtable_methods.iter() %}
139+
{%- let fn_type = format!("_UniFFILib.{}Method", callback_impl_name) %}
140+
{{ meth.name()|var_name() }} = Marshal.GetFunctionPointerForDelegate(_m{{ loop.index0 }}),
141+
{%- endfor %}
142+
@uniffiFree = Marshal.GetFunctionPointerForDelegate(_callback_interface_free)
143+
};
144+
145+
// Pin vtable to ensure GC does not move the vtable across the heap
146+
_UniFFILib.{{ ffi_init_callback.name() }}(GCHandle.Alloc(_vtable, GCHandleType.Pinned).AddrOfPinnedObject());
141147
}
142148
}
143149

0 commit comments

Comments
 (0)