From 44b37afa94f2aa6dc575835ac508ed214f6b983a Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Fri, 31 Jul 2026 17:03:13 +0000
Subject: [PATCH] Fix CodeQL warning-severity alerts: process stream ownership and two leaks (#799)
---
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ID2Entry.java | 36 ++++++++---
opendj-server-legacy/src/main/java/org/opends/quicksetup/util/OutputReader.java | 13 ++-
opendj-doc-maven-plugin/src/main/java/org/forgerock/opendj/maven/doc/Utils.java | 21 ++++--
opendj-server-legacy/src/main/java/org/opends/quicksetup/util/ServerController.java | 36 ++++-------
opendj-server-legacy/src/main/java/org/opends/server/tasks/ImportTask.java | 9 +++
opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/InstallerHelper.java | 13 ++--
6 files changed, 76 insertions(+), 52 deletions(-)
diff --git a/opendj-doc-maven-plugin/src/main/java/org/forgerock/opendj/maven/doc/Utils.java b/opendj-doc-maven-plugin/src/main/java/org/forgerock/opendj/maven/doc/Utils.java
index 2294bce..ed6c628 100644
--- a/opendj-doc-maven-plugin/src/main/java/org/forgerock/opendj/maven/doc/Utils.java
+++ b/opendj-doc-maven-plugin/src/main/java/org/forgerock/opendj/maven/doc/Utils.java
@@ -12,6 +12,7 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2015-2016 ForgeRock AS.
+ * Portions Copyright 2026 3A Systems, LLC.
*/
package org.forgerock.opendj.maven.doc;
@@ -75,11 +76,15 @@
* @throws IOException Failed to make the copy.
*/
static void copyFile(File original, File copy) throws IOException {
- copyInputStreamToFile(new FileInputStream(original), copy);
+ try (InputStream input = new FileInputStream(original)) {
+ copyInputStreamToFile(input, copy);
+ }
}
/**
* Copies the content of the original input stream to the copy.
+ * <p>
+ * The original input stream is closed on return, whether the copy succeeded or not.
* @param original The original input stream.
* @param copy The copy.
* @throws IOException Failed to make the copy.
@@ -88,12 +93,14 @@
if (original == null) {
throw new IOException("Could not read input to copy.");
}
- createFile(copy);
- try (OutputStream outputStream = new FileOutputStream(copy)) {
- int bytesRead;
- byte[] buffer = new byte[4096];
- while ((bytesRead = original.read(buffer)) > 0) {
- outputStream.write(buffer, 0, bytesRead);
+ try {
+ createFile(copy);
+ try (OutputStream outputStream = new FileOutputStream(copy)) {
+ int bytesRead;
+ byte[] buffer = new byte[4096];
+ while ((bytesRead = original.read(buffer)) > 0) {
+ outputStream.write(buffer, 0, bytesRead);
+ }
}
} finally {
closeSilently(original);
diff --git a/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/InstallerHelper.java b/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/InstallerHelper.java
index 7f6b80a..49c38ae 100644
--- a/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/InstallerHelper.java
+++ b/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/InstallerHelper.java
@@ -33,7 +33,6 @@
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
-import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
@@ -152,8 +151,7 @@
try
{
process = processBuilder.start();
- final BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream()));
- new OutputReader(err)
+ new OutputReader(process.getErrorStream())
{
@Override
public void processLine(final String line)
@@ -164,8 +162,7 @@
}
}.start();
- final BufferedReader out = new BufferedReader(new InputStreamReader(process.getInputStream()));
- new OutputReader(out)
+ new OutputReader(process.getInputStream())
{
@Override
public void processLine(final String line)
@@ -182,8 +179,10 @@
{
if (process != null)
{
- closeProcessStream(process.getErrorStream(), "error");
- closeProcessStream(process.getOutputStream(), "output");
+ // The error and output streams of the process are owned by the readers started above,
+ // which close them once drained. Only the stream writing to the standard input of the
+ // process, which is never used here, is left to close.
+ closeProcessStream(process.getOutputStream(), "input");
}
}
}
diff --git a/opendj-server-legacy/src/main/java/org/opends/quicksetup/util/OutputReader.java b/opendj-server-legacy/src/main/java/org/opends/quicksetup/util/OutputReader.java
index 5e0c2e7..d27c2b5 100644
--- a/opendj-server-legacy/src/main/java/org/opends/quicksetup/util/OutputReader.java
+++ b/opendj-server-legacy/src/main/java/org/opends/quicksetup/util/OutputReader.java
@@ -19,6 +19,8 @@
package org.opends.quicksetup.util;
import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
@@ -39,16 +41,17 @@
/**
* The protected constructor.
* <p>
- * The reader is consumed until end of stream and then closed by this reader's thread, which is
- * only launched by {@link #start()}.
+ * The stream is consumed until end of stream and then closed by this reader's thread, which is
+ * only launched by {@link #start()}. Wrapping the stream is done by that thread as well, so that
+ * this reader is the sole owner of every resource built on top of the stream.
*
- * @param reader the BufferedReader of the stop process.
+ * @param stream the output stream of the process to read.
*/
- public OutputReader(final BufferedReader reader) {
+ public OutputReader(final InputStream stream) {
thread = new Thread(new Runnable() {
@Override
public void run() {
- try (BufferedReader in = reader) {
+ try (BufferedReader in = new BufferedReader(new InputStreamReader(stream))) {
String line;
while (null != (line = in.readLine())) {
processLine(line);
diff --git a/opendj-server-legacy/src/main/java/org/opends/quicksetup/util/ServerController.java b/opendj-server-legacy/src/main/java/org/opends/quicksetup/util/ServerController.java
index 3a7d24f..a51e1dd 100644
--- a/opendj-server-legacy/src/main/java/org/opends/quicksetup/util/ServerController.java
+++ b/opendj-server-legacy/src/main/java/org/opends/quicksetup/util/ServerController.java
@@ -19,6 +19,7 @@
import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
@@ -181,16 +182,9 @@
logger.info(LocalizableMessage.raw("Launching stop command, argList: "+argList));
Process process = pb.start();
- BufferedReader err =
- new BufferedReader(
- new InputStreamReader(process.getErrorStream()));
- BufferedReader out =
- new BufferedReader(
- new InputStreamReader(process.getInputStream()));
-
/* Create these objects to resend the stop process output to the details area. */
- new StopReader(err, true);
- new StopReader(out, false);
+ new StopReader(process.getErrorStream(), true);
+ new StopReader(process.getInputStream(), false);
int returnValue = process.waitFor();
@@ -388,11 +382,8 @@
String startedId = getStartedId();
Process process = pb.start();
- BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream()));
- BufferedReader out = new BufferedReader(new InputStreamReader(process.getInputStream()));
-
- StartReader errReader = new StartReader(err, startedId, true);
- StartReader outputReader = new StartReader(out, startedId, false);
+ StartReader errReader = new StartReader(process.getErrorStream(), startedId, true);
+ StartReader outputReader = new StartReader(process.getInputStream(), startedId, false);
int returnValue = process.waitFor();
@@ -546,12 +537,11 @@
/**
* The protected constructor.
*
- * @param reader the BufferedReader of the stop process.
- * @param isError a boolean indicating whether the BufferedReader
+ * @param stream the stream of the stop process to read.
+ * @param isError a boolean indicating whether the stream
* corresponds to the standard error or to the standard output.
*/
- public StopReader(final BufferedReader reader,
- final boolean isError) {
+ public StopReader(final InputStream stream, final boolean isError) {
final LocalizableMessage errorTag =
isError ?
INFO_ERROR_READING_ERROROUTPUT.get() :
@@ -562,7 +552,7 @@
@Override
public void run() {
// The reader is owned by this thread, which closes it once the stream is drained.
- try (BufferedReader in = reader) {
+ try (BufferedReader in = new BufferedReader(new InputStreamReader(stream))) {
String line = in.readLine();
while (line != null) {
if (application != null) {
@@ -626,13 +616,13 @@
/**
* The protected constructor.
- * @param reader the BufferedReader of the start process.
+ * @param stream the stream of the start process to read.
* @param startedId the message ID that this class can use to know whether
* the start is over or not.
- * @param isError a boolean indicating whether the BufferedReader
+ * @param isError a boolean indicating whether the stream
* corresponds to the standard error or to the standard output.
*/
- public StartReader(final BufferedReader reader, final String startedId,
+ public StartReader(final InputStream stream, final String startedId,
final boolean isError)
{
final LocalizableMessage errorTag =
@@ -648,7 +638,7 @@
public void run()
{
// The reader is owned by this thread, which closes it once the stream is drained.
- try (BufferedReader in = reader)
+ try (BufferedReader in = new BufferedReader(new InputStreamReader(stream)))
{
String line = in.readLine();
while (line != null)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ID2Entry.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ID2Entry.java
index 73cb4d4..be1fd64 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ID2Entry.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/ID2Entry.java
@@ -260,16 +260,7 @@
{
return Entry.decode(reader, compressedSchema);
}
- InputStream is = reader.asInputStream();
- if ((format & ENCRYPT_ENTRY) == ENCRYPT_ENTRY)
- {
- is = getCryptoManager().getCipherInputStream(is);
- }
- if ((format & COMPRESS_ENTRY) == COMPRESS_ENTRY)
- {
- is = new InflaterInputStream(is);
- }
- try (InputStream entryStream = is)
+ try (InputStream entryStream = newEntryInputStream(reader, format))
{
byte[] data = new byte[encodedEntryLen];
int readBytes;
@@ -295,6 +286,31 @@
}
}
+ /**
+ * Returns the stream to read the encoded entry from, decorated as mandated by the format.
+ * <p>
+ * The returned stream owns the streams it wraps, so closing it closes the whole chain.
+ *
+ * @param reader the reader positioned at the start of the encoded entry.
+ * @param format the format byte of the encoded entry.
+ * @return the stream to read the encoded entry from.
+ * @throws CryptoManagerException If the cipher input stream cannot be created.
+ */
+ private static InputStream newEntryInputStream(ByteSequenceReader reader, int format)
+ throws CryptoManagerException
+ {
+ InputStream is = reader.asInputStream();
+ if ((format & ENCRYPT_ENTRY) == ENCRYPT_ENTRY)
+ {
+ is = getCryptoManager().getCipherInputStream(is);
+ }
+ if ((format & COMPRESS_ENTRY) == COMPRESS_ENTRY)
+ {
+ is = new InflaterInputStream(is);
+ }
+ return is;
+ }
+
private ByteString encode(Entry entry, DataConfig dataConfig) throws DirectoryException
{
encodeVolatile(entry, dataConfig);
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/tasks/ImportTask.java b/opendj-server-legacy/src/main/java/org/opends/server/tasks/ImportTask.java
index f2f12a2..5d69679 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/tasks/ImportTask.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/tasks/ImportTask.java
@@ -328,6 +328,15 @@
}
}
+ if (backend == null)
+ {
+ // Unreachable as long as the checks above hold: a null backend ID implies at least one
+ // include branch, and every include branch either resolves to a backend or is rejected.
+ // The guard mirrors the one in runTask() and keeps the dereference below safe.
+ LocalizableMessage message = ERR_LDIFIMPORT_NO_BACKENDS_FOR_ID.get();
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+ }
+
// Make sure the selected backend will handle all the include branches
defaultIncludeBranches = new ArrayList<>(backend.getBaseDNs());
--
Gitblit v1.10.0