From a37529601eb5710ca3f1f6fdb759b550ef04cffa Mon Sep 17 00:00:00 2001 From: iHsin Date: Mon, 15 Jun 2026 19:11:43 +0800 Subject: [PATCH 1/2] fix: detach tun fd ownership to prevent fdsan double-close crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use ParcelFileDescriptor.detachFd() instead of .fd when passing the TUN file descriptor to Rust. This transfers FD ownership to the Rust side, so the subsequent vpnInterface?.close() in cleanup() no longer closes the same FD — avoiding the fdsan SIGABRT. "fdsan: attempted to close file descriptor X, expected to be unowned, actually owned by ParcelFileDescriptor" Signed-off-by: iHsin Assisted-by: OpenClaw:deepseek-v4-flash --- app/src/main/java/rs/clash/android/service/TunService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/rs/clash/android/service/TunService.kt b/app/src/main/java/rs/clash/android/service/TunService.kt index d4b49ba..0d750ca 100644 --- a/app/src/main/java/rs/clash/android/service/TunService.kt +++ b/app/src/main/java/rs/clash/android/service/TunService.kt @@ -112,7 +112,7 @@ class TunService : VpnService() { builder.allowBypass() vpnInterface = builder.establish() - tunFd = vpnInterface?.fd + tunFd = vpnInterface?.detachFd() val fd = tunFd ?: run { Log.e("clash", "VPN interface fd is null, aborting") stopVpn() From ed5de63d579daa11ea860c6ec3e935c84c0309e1 Mon Sep 17 00:00:00 2001 From: iHsin Date: Mon, 15 Jun 2026 19:11:49 +0800 Subject: [PATCH 2/2] chore: bump version to 0.4.1 Signed-off-by: iHsin --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7273867..f4a20cc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,7 +5,7 @@ plugins { alias(libs.plugins.ktlint) } -val baseVersionName = "0.4.0" +val baseVersionName = "0.4.1" val Project.verName: String get() = "${baseVersionName}$versionNameSuffix.${exec("git rev-parse --short HEAD")}" val Project.verCode: Int get() = exec("git rev-list --count HEAD").toInt() val Project.isDevVersion: Boolean get() = exec("git tag -l v$baseVersionName").isEmpty()