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 =

Reply via email to