From bd04d7a1be13cd1d054fa8b2f32fabfb87473467 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Mon, 13 Jul 2026 12:56:46 -0500 Subject: [PATCH 1/4] fix: prevent crashes on stop/bootstrap before start Backport of Foundation-Devices' GrapheneOS crash fix. Replace the assert!(!client.is_null()) panics in tor_client_bootstrap and tor_client_set_dormant with graceful null guards so calling these before start (or after stop) returns instead of aborting the process, which crashed the app on GrapheneOS in particular. Also make stop() idempotent: return early when the proxy is already stopped and reset the client state afterwards. Add preemptive-circuit tuning to tor_start to reduce needless circuit building. --- lib/tor_ffi_plugin.dart | 7 +++++++ rust/src/lib.rs | 26 ++++++++++++++++++-------- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/lib/tor_ffi_plugin.dart b/lib/tor_ffi_plugin.dart index 41beb40..08ea5d8 100644 --- a/lib/tor_ffi_plugin.dart +++ b/lib/tor_ffi_plugin.dart @@ -174,9 +174,16 @@ class Tor { /// Stop the proxy. stop() async { + // Return early if already stopped. + if (_proxyPtr == nullptr) { + return; + } + final lib = TorFfiPluginBindings(_lib); lib.tor_proxy_stop(_proxyPtr); _proxyPtr = nullptr; + _bootstrapped = false; + _status = TorStatus.off; } setClientDormant(bool dormant) async { diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 3af430c..75fa863 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -57,6 +57,12 @@ pub unsafe extern "C" fn tor_start( .state_dir(CfgPath::new(state_dir.to_owned())) .cache_dir(CfgPath::new(cache_dir.to_owned())); cfg_builder.address_filter().allow_onion_addrs(true); + cfg_builder + .preemptive_circuits() + .disable_at_threshold(1) + .min_exit_circs_for_port(1) + .initial_predicted_ports() + .clear(); let cfg = unwrap_or_return!(cfg_builder.build(), err_ret); @@ -81,10 +87,12 @@ pub unsafe extern "C" fn tor_start( #[no_mangle] pub unsafe extern "C" fn tor_client_bootstrap(client: *mut c_void) -> bool { - let client = { - assert!(!client.is_null()); - Box::from_raw(client as *mut TorClient) - }; + // Return false if client is null (not started). + if client.is_null() { + return false; + } + + let client = Box::from_raw(client as *mut TorClient); unwrap_or_return!(client.runtime().block_on(client.bootstrap()), false); true @@ -92,10 +100,12 @@ pub unsafe extern "C" fn tor_client_bootstrap(client: *mut c_void) -> bool { #[no_mangle] pub unsafe extern "C" fn tor_client_set_dormant(client: *mut c_void, soft_mode: bool) { - let client = { - assert!(!client.is_null()); - Box::from_raw(client as *mut TorClient) - }; + // Return early if client is null (not started). + if client.is_null() { + return; + } + + let client = Box::from_raw(client as *mut TorClient); let dormant_mode = if soft_mode { DormantMode::Soft From 2895a887625c6de7be5591709d045ad20b3b13de Mon Sep 17 00:00:00 2001 From: sneurlax Date: Mon, 13 Jul 2026 12:57:04 -0500 Subject: [PATCH 2/4] fix: use OS-assigned port for random unused port selection Backport of Foundation-Devices' fix. Bind to port 0 and read back the port the OS assigns instead of guessing a random port and retrying on collision. This is the standard, race-free way to obtain a free port and removes the now-unused dart:math import. --- lib/tor_ffi_plugin.dart | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/tor_ffi_plugin.dart b/lib/tor_ffi_plugin.dart index 08ea5d8..b44477d 100644 --- a/lib/tor_ffi_plugin.dart +++ b/lib/tor_ffi_plugin.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'dart:ffi'; import 'dart:io'; import 'dart:isolate'; -import 'dart:math'; import 'package:ffi/ffi.dart'; import 'package:flutter/foundation.dart'; @@ -199,16 +198,16 @@ class Tor { Pointer _proxyPtr = nullptr; Future _getRandomUnusedPort({List excluded = const []}) async { - var random = Random.secure(); - int potentialPort = 0; + int port = 0; retry: - while (potentialPort <= 0 || excluded.contains(potentialPort)) { - potentialPort = random.nextInt(65535); + while (port == 0 || excluded.contains(port)) { try { - var socket = await ServerSocket.bind("0.0.0.0", potentialPort); + // Bind to port 0 to let the OS assign a free port. + var socket = await ServerSocket.bind("0.0.0.0", 0); + port = socket.port; socket.close(); - return potentialPort; + return port; } catch (_) { continue retry; } From 26d2600f1a41297303c3fadd24476af7cf36bd72 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Mon, 13 Jul 2026 12:57:54 -0500 Subject: [PATCH 3/4] feat(android): add 16 KB page size support Backport of Foundation-Devices' change. Pass the linker flags -Wl,--hash-style=both and -Wl,-z,max-page-size=16384 when building the Rust library for Android so the shared object loads on devices with 16 KB memory pages (required for Android 15+ on such hardware). --- cargokit/build_tool/lib/src/android_environment.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cargokit/build_tool/lib/src/android_environment.dart b/cargokit/build_tool/lib/src/android_environment.dart index 9342964..be3c491 100644 --- a/cargokit/build_tool/lib/src/android_environment.dart +++ b/cargokit/build_tool/lib/src/android_environment.dart @@ -186,7 +186,17 @@ class AndroidEnvironment { if (rustFlags.isNotEmpty) { rustFlags = '$rustFlags\x1f'; } - rustFlags = '$rustFlags-L\x1f$workaroundDir'; + rustFlags = '$rustFlags-L\x1f$workaroundDir\x1f'; + + const pageSizeArgs = [ + "-C", + "link-arg=-Wl,--hash-style=both", + "-C", + "link-arg=-Wl,-z,max-page-size=16384" + ]; + final pageSizeArgsString = pageSizeArgs.join("\x1f"); + + rustFlags = '$rustFlags$pageSizeArgsString'; return rustFlags; } } From 7b4c469846421e352d7c88e483e92730c686e959 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Mon, 13 Jul 2026 12:58:08 -0500 Subject: [PATCH 4/4] fix(android): remove stale package attribute from AndroidManifest Backport of Foundation-Devices' fix. AGP 8+ rejects the package attribute in the manifest; the namespace is already declared in android/build.gradle (com.cypherstack.tor_ffi_plugin). Leaving the attribute in place fails downstream consumers' processDebugManifest. --- android/src/main/AndroidManifest.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 99163d8..a2f47b6 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,3 +1,2 @@ - +