From cd5bb09d0d19e4e01baeaad7a6cbe14ab2db28bc Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:29:41 +0530 Subject: [PATCH] Fix potential leaks in executePipedCommands (#12478) --- .../java/com/cloud/utils/script/Script.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/utils/src/main/java/com/cloud/utils/script/Script.java b/utils/src/main/java/com/cloud/utils/script/Script.java index 6c62c910648..ffda782edda 100644 --- a/utils/src/main/java/com/cloud/utils/script/Script.java +++ b/utils/src/main/java/com/cloud/utils/script/Script.java @@ -40,9 +40,11 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import org.apache.cloudstack.utils.security.KeyStoreUtils; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.io.IOUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -708,13 +710,31 @@ public class Script implements Callable { return executeCommandForExitValue(0, command); } + private static void cleanupProcesses(AtomicReference> processesRef) { + List processes = processesRef.get(); + if (CollectionUtils.isNotEmpty(processes)) { + for (Process process : processes) { + if (process == null) { + continue; + } + LOGGER.trace(String.format("Cleaning up process [%s] from piped commands.", process.pid())); + IOUtils.closeQuietly(process.getErrorStream()); + IOUtils.closeQuietly(process.getOutputStream()); + IOUtils.closeQuietly(process.getInputStream()); + process.destroyForcibly(); + } + } + } + public static Pair executePipedCommands(List commands, long timeout) { if (timeout <= 0) { timeout = DEFAULT_TIMEOUT; } + final AtomicReference> processesRef = new AtomicReference<>(); Callable> commandRunner = () -> { List builders = commands.stream().map(ProcessBuilder::new).collect(Collectors.toList()); List processes = ProcessBuilder.startPipeline(builders); + processesRef.set(processes); Process last = processes.get(processes.size()-1); try (BufferedReader reader = new BufferedReader(new InputStreamReader(last.getInputStream()))) { String line; @@ -741,6 +761,8 @@ public class Script implements Callable { result.second(ERR_TIMEOUT); } catch (InterruptedException | ExecutionException e) { LOGGER.error("Error executing piped commands", e); + } finally { + cleanupProcesses(processesRef); } return result; }