This is an automated email from the ASF dual-hosted git repository. rong pushed a commit to branch test-4 in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 087cc8b2252e167739a78d7a7e82f3e0840ffe0c Author: Steve Yurong Su <[email protected]> AuthorDate: Fri Jun 20 13:36:20 2025 +0800 introduce sandbox classloader --- .../config/executor/ClusterConfigTaskExecutor.java | 33 ++++++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java index d1139585822..0643f5637ba 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java @@ -540,7 +540,7 @@ public class ClusterConfigTaskExecutor implements IConfigTaskExecutor { } final Optional<SettableFuture<ConfigTaskResult>> maybeValidationFailed = - tryReflectAndValidateUDFInSandbox(libRoot, createFunctionStatement, future); + tryReflectAndValidateUDFInThreadSandbox(libRoot, createFunctionStatement, future); if (maybeValidationFailed.isPresent()) { return maybeValidationFailed.get(); } @@ -562,14 +562,16 @@ public class ClusterConfigTaskExecutor implements IConfigTaskExecutor { return future; } - private Optional<SettableFuture<ConfigTaskResult>> tryReflectAndValidateUDFInSandbox( + private Optional<SettableFuture<ConfigTaskResult>> tryReflectAndValidateUDFInThreadSandbox( String libRoot, CreateFunctionStatement createFunctionStatement, SettableFuture<ConfigTaskResult> future) { try { // Execute in a thread sandbox to prevent UDF from blocking the main thread return executeInThreadSandboxWithTimeout( - () -> tryReflectAndValidateUDF(libRoot, createFunctionStatement, future), + () -> + tryReflectAndValidateUDFWithSandboxClassloader( + libRoot, createFunctionStatement, future), CommonDescriptor.getInstance().getConfig().getUdfValidationTimeoutMs(), TimeUnit.MILLISECONDS); } catch (final Exception e) { @@ -611,14 +613,33 @@ public class ClusterConfigTaskExecutor implements IConfigTaskExecutor { } } - private Optional<SettableFuture<ConfigTaskResult>> tryReflectAndValidateUDF( + private Optional<SettableFuture<ConfigTaskResult>> tryReflectAndValidateUDFWithSandboxClassloader( String libRoot, CreateFunctionStatement createFunctionStatement, SettableFuture<ConfigTaskResult> future) { // try to create instance, this request will fail if creation is not successful - try (UDFClassLoader classLoader = new UDFClassLoader(libRoot)) { + try (final UDFClassLoader sandboxClassLoader = + new UDFClassLoader(libRoot) { + @Override + public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { + // Prevent UDF from accessing restricted classes + // Using Process or File in UDF is not allowed + if (name.startsWith("java.lang.Process") || name.startsWith("java.io.File")) { + LOGGER.warn( + "SANDBOX: UDF {} class {} is trying to access restricted class, which is not allowed", + createFunctionStatement.getUdfName(), + name); + throw new SecurityException( + String.format( + "UDF %s is trying to access restricted class %s, which is not allowed", + createFunctionStatement.getUdfName(), name)); + } + return super.loadClass(name, resolve); + } + }) { // ensure that jar file contains the class and the class is a UDF - Class<?> clazz = Class.forName(createFunctionStatement.getClassName(), true, classLoader); + Class<?> clazz = + Class.forName(createFunctionStatement.getClassName(), true, sandboxClassLoader); UDF udf = (UDF) clazz.getDeclaredConstructor().newInstance(); final TSStatus maybeValidationFailed =
