Skip to content

Commit 3aeceba

Browse files
authored
Merge pull request fzyzcjy#2653 from alexlapa/post-cobj-double-free
Fix Dart_PostCObject double free
2 parents 3ef9e9d + 573dc44 commit 3aeceba

File tree

6 files changed

+106
-0
lines changed

6 files changed

+106
-0
lines changed

frb_dart/lib/src/ffigen_generated/intermediate/frb_rust.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,10 @@ void frb_free_wire_sync_rust2dart_dco(WireSyncRust2DartDco value);
7272
* This function should never be called manually.
7373
*/
7474
void frb_free_wire_sync_rust2dart_sse(struct WireSyncRust2DartSse value);
75+
76+
/**
77+
* # Safety
78+
*
79+
* This function should never be called manually.
80+
*/
81+
void (*frb_create_shutdown_callback(void))(void*);

frb_dart/lib/src/ffigen_generated/multi_package.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,25 @@ class MultiPackageCBinding {
481481
late final _frb_free_wire_sync_rust2dart_sse =
482482
_frb_free_wire_sync_rust2dart_ssePtr
483483
.asFunction<void Function(WireSyncRust2DartSse)>();
484+
485+
/// # Safety
486+
///
487+
/// This function should never be called manually.
488+
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>
489+
frb_create_shutdown_callback() {
490+
return _frb_create_shutdown_callback();
491+
}
492+
493+
late final _frb_create_shutdown_callbackPtr = _lookup<
494+
ffi.NativeFunction<
495+
ffi.Pointer<
496+
ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>
497+
Function()>>('frb_create_shutdown_callback');
498+
late final _frb_create_shutdown_callback =
499+
_frb_create_shutdown_callbackPtr.asFunction<
500+
ffi.Pointer<
501+
ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>
502+
Function()>();
484503
}
485504

486505
/// A Dart_CObject is used for representing Dart objects as native C

frb_dart/lib/src/generalized_frb_rust_binding/_io.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ class GeneralizedFrbRustBinding {
1010
final MultiPackageCBinding _binding;
1111
final String _externalLibraryDebugInfo;
1212

13+
/// Notifies Rust side of an isolate group shutdown. Initialized in
14+
/// [initShutdownWatcher].
15+
///
16+
/// It is static and initialized only once since there supposed to be only
17+
/// one per isolate.
18+
static _ShutdownWatcher? _shutdownWatcher;
19+
1320
/// {@macro flutter_rust_bridge.only_for_generated_code}
1421
GeneralizedFrbRustBinding(ExternalLibrary externalLibrary)
1522
: _binding = MultiPackageCBinding(externalLibrary.ffiDynamicLibrary),
@@ -25,6 +32,12 @@ class GeneralizedFrbRustBinding {
2532
}
2633
}
2734

35+
/// {@macro flutter_rust_bridge.only_for_generated_code}
36+
void initShutdownWatcher() {
37+
_shutdownWatcher ??=
38+
_ShutdownWatcher(_binding.frb_create_shutdown_callback());
39+
}
40+
2841
/// {@macro flutter_rust_bridge.only_for_generated_code}
2942
void initFrbDartApiDl() =>
3043
_binding.frb_init_frb_dart_api_dl(ffi.NativeApi.initializeApiDLData);
@@ -120,3 +133,13 @@ class GeneralizedFrbRustBinding {
120133
}
121134
}
122135
}
136+
137+
/// {@macro flutter_rust_bridge.only_for_generated_code}
138+
final class _ShutdownWatcher implements ffi.Finalizable {
139+
final ffi.NativeFinalizer _finalizer;
140+
141+
_ShutdownWatcher(ffi.Pointer<ffi.NativeFinalizerFunction> callback)
142+
: _finalizer = ffi.NativeFinalizer(callback) {
143+
_finalizer.attach(this, ffi.Pointer.fromAddress(0));
144+
}
145+
}

frb_dart/lib/src/generalized_frb_rust_binding/_web.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ class GeneralizedFrbRustBinding {
1616
/// {@macro flutter_rust_bridge.only_for_generated_code}
1717
void initFrbDartApiDl() {}
1818

19+
/// {@macro flutter_rust_bridge.only_for_generated_code}
20+
void initShutdownWatcher() {}
21+
1922
/// {@macro flutter_rust_bridge.only_for_generated_code}
2023
void pdeFfiDispatcherPrimary({
2124
required int funcId,

frb_dart/lib/src/main_components/entrypoint.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ class _EntrypointState<A extends BaseApi> {
167167
}) {
168168
_setUpRustToDartCommunication(generalizedFrbRustBinding);
169169
_initializeApiDlData(generalizedFrbRustBinding);
170+
_initializeShutdownWatcher(generalizedFrbRustBinding);
170171
}
171172

172173
void dispose() {
@@ -199,3 +200,8 @@ void _setUpRustToDartCommunication(
199200
void _initializeApiDlData(GeneralizedFrbRustBinding generalizedFrbRustBinding) {
200201
generalizedFrbRustBinding.initFrbDartApiDl();
201202
}
203+
204+
void _initializeShutdownWatcher(
205+
GeneralizedFrbRustBinding generalizedFrbRustBinding) {
206+
generalizedFrbRustBinding.initShutdownWatcher();
207+
}

frb_rust/src/ffi_binding/io.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
use crate::codec::dco::Rust2DartMessageDco;
22
use crate::codec::sse::Rust2DartMessageSse;
33
use crate::codec::Rust2DartMessageTrait;
4+
use crate::misc::atomic::AtomicU64;
45
use crate::platform_types::{WireSyncRust2DartDco, WireSyncRust2DartSse};
6+
use allo_isolate::{
7+
ffi::{DartCObject, DartPort},
8+
store_dart_post_cobject,
9+
};
10+
use std::ffi::c_void;
11+
use std::sync::atomic::Ordering;
512

613
/// # Safety
714
///
@@ -29,3 +36,44 @@ pub unsafe extern "C" fn frb_free_wire_sync_rust2dart_dco(value: WireSyncRust2Da
2936
pub unsafe extern "C" fn frb_free_wire_sync_rust2dart_sse(value: WireSyncRust2DartSse) {
3037
let _ = Rust2DartMessageSse::from_raw_wire_sync(value);
3138
}
39+
40+
/// # Safety
41+
///
42+
/// This function should never be called manually.
43+
#[no_mangle]
44+
pub unsafe extern "C" fn frb_create_shutdown_callback() -> unsafe extern "C" fn(*mut c_void) {
45+
/// Counter for how many active shutdown callbacks there are.
46+
///
47+
/// It is incremented when `frb_shutdown_callback` is returned and
48+
/// decremented when it is called.
49+
static ISOLATES_NUM: AtomicU64 = AtomicU64::new(0);
50+
51+
_ = ISOLATES_NUM.fetch_add(1, Ordering::SeqCst);
52+
53+
/// Called by Dart's `NativeFinalizer` on isolate group shutdown.
54+
unsafe extern "C" fn frb_shutdown_callback(_: *mut c_void) {
55+
unsafe extern "C" fn devnull(_: DartPort, _: *mut DartCObject) -> bool {
56+
// Returning true is wrong since message is not enqueued and this
57+
// might cause memory leaks. But since application is shutting down
58+
// we don't really care and just want it to die silently without
59+
// triggering any send errors.
60+
true
61+
}
62+
63+
let running = ISOLATES_NUM.fetch_sub(1, Ordering::SeqCst);
64+
65+
// If this is the last callback we assume that application is shutting
66+
// down.
67+
if running == 1 {
68+
// So `Dart_PostCObject` won't do anything from now on. We need this
69+
// cause once shutdown have started `Dart_Cleanup` might be called any
70+
// moment from now and `Dart_PostCObject` can only be used before
71+
// `Dart_Cleanup` has been called
72+
// For more information refer to:
73+
// https://github.com/dart-lang/native/issues/2079
74+
store_dart_post_cobject(devnull);
75+
};
76+
}
77+
78+
frb_shutdown_callback
79+
}

0 commit comments

Comments
 (0)