From a5d70e229854884d20fcc0ac3f80e3a8732c61fe Mon Sep 17 00:00:00 2001 From: blankll Date: Sat, 10 Oct 2026 10:53:37 +0800 Subject: [PATCH] fix(jdbc-bridge): grant native access to bridge JVM for DuckDB native loads (issue #176) DuckDB's JDBC driver loads a ~107 MB native library via System::load, which Java 24+ flags as a restricted method and future JDKs will deny without --enable-native-access. The warnings also pollute connection error messages surfaced to users (see issue #176 screenshot). The bridge launcher now passes --enable-native-access=ALL-UNNAMED to every bridge JVM, version-gated to JDK 17+ so launch paths without the Java 25 requirement (the settings-page driver download) stay safe on older JREs. Verified end to end with the cached 0.8.8 bridge + DuckDB 1.5.6.0 on Java 25: the issue's exact error reproduces with ssl_mode sent, the connection succeeds without it, and the stderr warnings are gone with the flag. --- .../src/database/jdbc_bridge/launcher.rs | 62 ++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/database/jdbc_bridge/launcher.rs b/src-tauri/src/database/jdbc_bridge/launcher.rs index 16926049..a7f7a58f 100644 --- a/src-tauri/src/database/jdbc_bridge/launcher.rs +++ b/src-tauri/src/database/jdbc_bridge/launcher.rs @@ -39,6 +39,21 @@ impl JdbcBridgeLauncher { super::jre::JreDetector::detect() } + /// JVM options granting native access to classpath code. + /// + /// Drivers like DuckDB load a native library through `System::load`, which + /// Java 24+ flags as a restricted method and future JDKs will deny without + /// `--enable-native-access` (issue #176). The option exists since JDK 17; + /// older JVMs reject it at startup, so it is only passed when the detected + /// Java is new enough (or when the version can't be determined — every + /// supported launch path already requires Java 25+). + fn native_access_args(java_version: Option) -> Vec { + match java_version { + Some(version) if version < 17 => Vec::new(), + _ => vec!["--enable-native-access=ALL-UNNAMED".to_string()], + } + } + fn read_stderr_buffer(buf: &Arc>>) -> String { buf.lock().unwrap_or_else(|e| e.into_inner()).join("\n") } @@ -84,7 +99,11 @@ impl JdbcBridgeLauncher { ) })?; + let java_version = super::jre::system_java_version(&java); let mut cmd = Command::new(&java); + for arg in Self::native_access_args(java_version) { + cmd.arg(arg); + } for arg in jvm_args { cmd.arg(arg); } @@ -147,8 +166,13 @@ impl JdbcBridgeLauncher { } } - let mut child = Command::new(&java) - .args(["-cp", &classpath, "sqlkit.bridge.BridgeMain"]) + let java_version = super::jre::system_java_version(&java); + let mut cmd = Command::new(&java); + for arg in Self::native_access_args(java_version) { + cmd.arg(arg); + } + cmd.args(["-cp", &classpath, "sqlkit.bridge.BridgeMain"]); + let mut child = cmd .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) @@ -568,6 +592,40 @@ mod launcher_registry_tests { launcher_key(Path::new("/tmp/other.jar"), &[]), ); } + + #[test] + fn native_access_args_on_for_modern_java() { + for version in [17u32, 21, 25] { + assert_eq!( + JdbcBridgeLauncher::native_access_args(Some(version)), + vec!["--enable-native-access=ALL-UNNAMED".to_string()], + "Java {} should get native access", + version, + ); + } + } + + #[test] + fn native_access_args_off_for_legacy_java() { + for version in [8u32, 11] { + assert!( + JdbcBridgeLauncher::native_access_args(Some(version)).is_empty(), + "Java {} would reject the option at startup", + version, + ); + } + } + + #[test] + fn native_access_args_on_when_version_unknown() { + // All supported launch paths require Java 25+, so an undetectable + // version still passes the flag — failing closed keeps DuckDB's + // native library load working on future JDKs (issue #176). + assert_eq!( + JdbcBridgeLauncher::native_access_args(None), + vec!["--enable-native-access=ALL-UNNAMED".to_string()], + ); + } } impl Drop for JdbcBridgeLauncher {