diff --git a/src/main/java/edu/uiowa/cs/clc/kind2/api/ApiUtil.java b/src/main/java/edu/uiowa/cs/clc/kind2/api/ApiUtil.java index e074184..fc31f63 100644 --- a/src/main/java/edu/uiowa/cs/clc/kind2/api/ApiUtil.java +++ b/src/main/java/edu/uiowa/cs/clc/kind2/api/ApiUtil.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.List; import edu.uiowa.cs.clc.kind2.Kind2Exception; @@ -38,12 +39,28 @@ public static File writeLustreFile(String program) { /** * Writes an interpreter input to a temporary file for Kind 2 to read. + *
+ * In safe mode, this file is mounted into a container that runs as another user, so all users
+ * can read the file. Its directory, which only the current user can enter, keeps the file from
+ * other users of the host.
*
* @param program the interpreter input text
* @return the file the input was written to
*/
public static File writeInterpreterFile(String program) {
- return writeTempFile("kind2-api-", ".json", program);
+ File file = null;
+ try {
+ File directory = Files.createTempDirectory("kind2-api-").toFile();
+ file = new File(directory, "interpreter-input.json");
+ file.createNewFile();
+ if (program != null) {
+ Util.writeToFile(program, file);
+ }
+ file.setReadable(true, false);
+ return file;
+ } catch (IOException e) {
+ throw new Kind2Exception("Cannot write to file: " + file, e);
+ }
}
/**
diff --git a/src/main/java/edu/uiowa/cs/clc/kind2/api/Kind2Api.java b/src/main/java/edu/uiowa/cs/clc/kind2/api/Kind2Api.java
index 2b52f39..5c2e456 100644
--- a/src/main/java/edu/uiowa/cs/clc/kind2/api/Kind2Api.java
+++ b/src/main/java/edu/uiowa/cs/clc/kind2/api/Kind2Api.java
@@ -13,6 +13,7 @@
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
+import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -46,6 +47,11 @@ public class Kind2Api {
private static final String DEFAULT_SAFE_MODE_MEMORY_USAGE = "2g";
private static final String DEFAULT_SAFE_MODE_SWAP_USAGE = DEFAULT_SAFE_MODE_MEMORY_USAGE;
private static final int DEFAULT_SAFE_MODE_MAX_RUNS = 2;
+ // Counts threads as well as processes. SMTInterpol starts a multi-threaded JVM per solver
+ // instance, and reached about 500 on a small program, while the other solvers stay near 40.
+ private static final int SAFE_MODE_PIDS_LIMIT = 1024;
+ // An unprivileged user that exists in the image's base system.
+ private static final String SAFE_MODE_USER = "65534:65534";
private static final long DOCKER_COMMAND_TIMEOUT_SECONDS = 10;
private static final long DOCKER_EXIT_GRACE_MILLIS = 2000;
// Calls that cannot be cancelled (the interpreter, and execute without a monitor) give up
@@ -343,6 +349,15 @@ public String interpret(String program, String main, String json) {
*/
private String runInterpreter(File interpreterFile, Path lustreFile, String program, String main) {
String containerName = newContainerName();
+ if (containerName != null && lustreFile != null) {
+ // The container's user may not be able to read the file, but this process can.
+ try {
+ program = new String(Files.readAllBytes(lustreFile), java.nio.charset.StandardCharsets.UTF_8);
+ lustreFile = null;
+ } catch (IOException e) {
+ throw new Kind2Exception("Cannot read Lustre file: " + lustreFile, e);
+ }
+ }
List
+ * Containers run as an unprivileged user with no capabilities and a read-only file system, so
+ * the Kind 2 executable and the solvers must be readable by all users, and executables must be
+ * executable by all users.
+ *
* Default: false
* @param safeMode whether or not to enable safe mode.
*/
diff --git a/src/test/java/edu/uiowa/cs/clc/kind2/api/SafeModeTest.java b/src/test/java/edu/uiowa/cs/clc/kind2/api/SafeModeTest.java
new file mode 100644
index 0000000..27faa9e
--- /dev/null
+++ b/src/test/java/edu/uiowa/cs/clc/kind2/api/SafeModeTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2026, Board of Trustees of the University of Iowa
+ * All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License. See LICENSE in the project root for license information.
+ */
+
+package edu.uiowa.cs.clc.kind2.api;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+class SafeModeTest {
+ private static void assertHasOption(List