mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
Merge remote-tracking branch 'origin/GP-6408_ryanmkurtz_demangler-timeout--SQUASHED' into patch
This commit is contained in:
@@ -451,6 +451,13 @@
|
||||
<B>The GNU Demangler</B> adds the following analysis options:
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
<U><B>Timeout (seconds)</B></U> -
|
||||
The maximum amount of seconds to allow the native GNU Demangler process to
|
||||
attempt to demangle a mangled string before failing. Some inputs to the
|
||||
native GNU Demangler program have been shown to not terminate and consume excessive
|
||||
resources. This timeout protects against these inputs.
|
||||
</P>
|
||||
<P>
|
||||
<U><B>Use Deprecated Demangler</B></U> -
|
||||
By default, GCC symbols will be demangled using the most up-to-date demangler
|
||||
|
||||
@@ -66,10 +66,15 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
|
||||
private static final String OPTION_DESCRIPTION_DEMANGLER_FORMAT =
|
||||
"The demangling format to use";
|
||||
|
||||
static final String OPTION_NAME_TIMEOUT_SECONDS = "Timeout (seconds)";
|
||||
private static final String OPTION_DESCRIPTION_TIMEOUT_SECONDS =
|
||||
"The maximum amount of seconds to spend demangling a string";
|
||||
|
||||
private boolean applyFunctionSignature = true;
|
||||
private boolean applyCallingConvention = true;
|
||||
private boolean demangleOnlyKnownPatterns = false;
|
||||
private boolean useStandardReplacements = true;
|
||||
private long timeoutSeconds = GnuDemanglerOptions.DEFAULT_TIMEOUT_SECONDS;
|
||||
private GnuDemanglerFormat demanglerFormat = GnuDemanglerFormat.AUTO;
|
||||
private boolean useDeprecatedDemangler = false;
|
||||
|
||||
@@ -110,6 +115,8 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
|
||||
demanglerFormat, help, OPTION_DESCRIPTION_DEMANGLER_FORMAT,
|
||||
() -> optionsEditor.getFormatEditor());
|
||||
|
||||
options.registerOption(OPTION_NAME_TIMEOUT_SECONDS, timeoutSeconds, help,
|
||||
OPTION_DESCRIPTION_TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -123,6 +130,8 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
|
||||
useStandardReplacements =
|
||||
options.getBoolean(OPTION_NAME_DEMANGLE_USE_STANDARD_REPLACEMENTS,
|
||||
useStandardReplacements);
|
||||
timeoutSeconds = options.getLong(OPTION_NAME_TIMEOUT_SECONDS,
|
||||
GnuDemanglerOptions.DEFAULT_TIMEOUT_SECONDS);
|
||||
demanglerFormat = options.getEnum(OPTION_NAME_DEMANGLER_FORMAT, GnuDemanglerFormat.AUTO);
|
||||
useDeprecatedDemangler =
|
||||
options.getBoolean(OPTION_NAME_USE_DEPRECATED_DEMANGLER, useDeprecatedDemangler);
|
||||
@@ -131,7 +140,7 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
|
||||
@Override
|
||||
protected DemanglerOptions getOptions() {
|
||||
GnuDemanglerOptions options =
|
||||
new GnuDemanglerOptions(demanglerFormat, useDeprecatedDemangler);
|
||||
new GnuDemanglerOptions(demanglerFormat, useDeprecatedDemangler, timeoutSeconds);
|
||||
options.setDoDisassembly(true);
|
||||
options.setApplySignature(applyFunctionSignature);
|
||||
options.setApplyCallingConvention(applyCallingConvention);
|
||||
|
||||
@@ -108,7 +108,7 @@ public class GnuDemangler implements Demangler {
|
||||
try {
|
||||
|
||||
GnuDemanglerNativeProcess process = getNativeProcess(options);
|
||||
String demangled = process.demangle(mangled);
|
||||
String demangled = process.demangle(mangled, options.getTimeoutSeconds());
|
||||
if (demangled == null) {
|
||||
throw new DemangledException(false);
|
||||
}
|
||||
|
||||
@@ -15,16 +15,34 @@
|
||||
*/
|
||||
package ghidra.app.util.demangler.gnu;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ghidra.framework.*;
|
||||
import com.google.common.util.concurrent.SimpleTimeLimiter;
|
||||
|
||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.OSFileNotFoundException;
|
||||
import ghidra.framework.Platform;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
@@ -113,16 +131,39 @@ public class GnuDemanglerNativeProcess {
|
||||
createProcess();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the demangled string}
|
||||
*
|
||||
* @param mangled The string to demangle
|
||||
* @throws IOException if an IO-related error occurred
|
||||
*/
|
||||
public synchronized String demangle(String mangled) throws IOException {
|
||||
return demangle(mangled, true, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the demangled string}
|
||||
*
|
||||
* @param mangled The string to demangle
|
||||
* @param timeoutSeconds The number of seconds to attempt the demangle, or {@code null} for no
|
||||
* timeout
|
||||
* @throws IOException if a timeout or IO-related error occurred
|
||||
*/
|
||||
public synchronized String demangle(String mangled, Long timeoutSeconds) throws IOException {
|
||||
if (isDisposed) {
|
||||
throw new IOException("Demangled process has been terminated.");
|
||||
}
|
||||
return demangle(mangled, true);
|
||||
return demangle(mangled, true, timeoutSeconds);
|
||||
}
|
||||
|
||||
private String demangle(String mangled, boolean restart) throws IOException {
|
||||
private String demangle(String mangled, boolean restart, Long timeoutSeconds)
|
||||
throws IOException {
|
||||
try {
|
||||
return doDemangle(mangled);
|
||||
return doDemangle(mangled, timeoutSeconds);
|
||||
}
|
||||
catch (TimeoutException e) {
|
||||
dispose();
|
||||
throw new IOException("Timeout reached", e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
dispose();
|
||||
@@ -130,14 +171,25 @@ public class GnuDemanglerNativeProcess {
|
||||
throw new IOException("Demangler process is not running.", e);
|
||||
}
|
||||
createProcess();
|
||||
return demangle(mangled, false);
|
||||
return demangle(mangled, false, timeoutSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
private String doDemangle(String mangled) throws IOException {
|
||||
private String doDemangle(String mangled, Long timeoutSeconds)
|
||||
throws TimeoutException, IOException {
|
||||
writer.println(mangled);
|
||||
writer.flush();
|
||||
return reader.readLine();
|
||||
try {
|
||||
return timeoutSeconds != null
|
||||
? SimpleTimeLimiter
|
||||
.create(AutoAnalysisManager.getSharedAnalsysThreadPool()
|
||||
.getExecutorService())
|
||||
.callWithTimeout(reader::readLine, timeoutSeconds, TimeUnit.SECONDS)
|
||||
: reader.readLine();
|
||||
}
|
||||
catch (ExecutionException | InterruptedException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
@@ -161,6 +213,7 @@ public class GnuDemanglerNativeProcess {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
private void createProcess() throws IOException {
|
||||
|
||||
String[] command = buildCommand();
|
||||
@@ -227,9 +280,14 @@ public class GnuDemanglerNativeProcess {
|
||||
// Send a test string over and read the result. If the test string is blank, then
|
||||
// there was an error.
|
||||
//
|
||||
String testResult = doDemangle("test");
|
||||
if (!StringUtils.isBlank(testResult)) {
|
||||
return;
|
||||
try {
|
||||
String testResult = doDemangle("test", null);
|
||||
if (!StringUtils.isBlank(testResult)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (TimeoutException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
InputStream err = process.getErrorStream();
|
||||
|
||||
@@ -44,9 +44,15 @@ public class GnuDemanglerOptions extends DemanglerOptions {
|
||||
*/
|
||||
public static final String GNU_DEMANGLER_DEFAULT = GNU_DEMANGLER_V2_41;
|
||||
|
||||
/**
|
||||
* The default GNU demangler timeout (in seconds)
|
||||
*/
|
||||
public static final long DEFAULT_TIMEOUT_SECONDS = 3;
|
||||
|
||||
private final GnuDemanglerFormat format;
|
||||
private final boolean isDeprecated;
|
||||
private boolean useStandardReplacements;
|
||||
private long timeout;
|
||||
|
||||
/**
|
||||
* Default constructor to use the modern demangler with auto-detect for the format. This
|
||||
@@ -75,9 +81,25 @@ public class GnuDemanglerOptions extends DemanglerOptions {
|
||||
* demangler
|
||||
*/
|
||||
public GnuDemanglerOptions(GnuDemanglerFormat format, boolean isDeprecated) {
|
||||
this(format, isDeprecated, GnuDemanglerOptions.DEFAULT_TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor to specify the format to use, whether to prefer the deprecated format when
|
||||
* both deprecated and modern are available, and the timeout
|
||||
*
|
||||
* @param format the format
|
||||
* @param isDeprecated true if the format is not available in the modern demangler
|
||||
* @param timeoutSeconds the demangler timeout in seconds
|
||||
* @throws IllegalArgumentException if the given format is not available in the deprecated
|
||||
* demangler
|
||||
*/
|
||||
public GnuDemanglerOptions(GnuDemanglerFormat format, boolean isDeprecated,
|
||||
long timeoutSeconds) {
|
||||
this.format = format;
|
||||
this.isDeprecated = isDeprecated;
|
||||
this.useStandardReplacements = true;
|
||||
this.timeout = timeoutSeconds;
|
||||
if (!format.isAvailable(isDeprecated)) {
|
||||
throw new IllegalArgumentException(
|
||||
format.name() + " is not available in the " + getDemanglerName());
|
||||
@@ -91,30 +113,32 @@ public class GnuDemanglerOptions extends DemanglerOptions {
|
||||
public GnuDemanglerOptions(DemanglerOptions copy) {
|
||||
super(copy);
|
||||
|
||||
if (copy instanceof GnuDemanglerOptions) {
|
||||
GnuDemanglerOptions gCopy = (GnuDemanglerOptions) copy;
|
||||
if (copy instanceof GnuDemanglerOptions gCopy) {
|
||||
format = gCopy.format;
|
||||
isDeprecated = gCopy.isDeprecated;
|
||||
timeout = gCopy.timeout;
|
||||
}
|
||||
else {
|
||||
format = GnuDemanglerFormat.AUTO;
|
||||
isDeprecated = false;
|
||||
timeout = DEFAULT_TIMEOUT_SECONDS;
|
||||
}
|
||||
|
||||
this.useStandardReplacements = true;
|
||||
}
|
||||
|
||||
private GnuDemanglerOptions(GnuDemanglerOptions copy, GnuDemanglerFormat format,
|
||||
boolean deprecated) {
|
||||
this(copy, format, deprecated, true);
|
||||
boolean deprecated, long timeoutSeconds) {
|
||||
this(copy, format, deprecated, true, timeoutSeconds);
|
||||
}
|
||||
|
||||
private GnuDemanglerOptions(GnuDemanglerOptions copy, GnuDemanglerFormat format,
|
||||
boolean deprecated, boolean useStandardReplacements) {
|
||||
boolean deprecated, boolean useStandardReplacements, long timeoutSeconds) {
|
||||
super(copy);
|
||||
this.format = format;
|
||||
this.isDeprecated = deprecated;
|
||||
this.useStandardReplacements = useStandardReplacements;
|
||||
this.timeout = timeoutSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,7 +174,7 @@ public class GnuDemanglerOptions extends DemanglerOptions {
|
||||
return this;
|
||||
}
|
||||
if (demanglerFormat.isAvailable(useDeprecated)) {
|
||||
return new GnuDemanglerOptions(this, demanglerFormat, useDeprecated);
|
||||
return new GnuDemanglerOptions(this, demanglerFormat, useDeprecated, this.timeout);
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
demanglerFormat.name() + " is not available in the " + getDemanglerName());
|
||||
@@ -186,6 +210,13 @@ public class GnuDemanglerOptions extends DemanglerOptions {
|
||||
return useStandardReplacements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the demangler timeout (in seconds)}
|
||||
*/
|
||||
public long getTimeoutSeconds() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
//@formatter:off
|
||||
@@ -194,6 +225,7 @@ public class GnuDemanglerOptions extends DemanglerOptions {
|
||||
"\tapplySignature: " + applySignature() + ",\n" +
|
||||
"\tuseStandardReplacements: " + useStandardReplacements + ",\n" +
|
||||
"\tdemangleOnlyKnownPatterns: " + demangleOnlyKnownPatterns() + ",\n" +
|
||||
"\ttimeout (sec): " + timeout + ",\n" +
|
||||
"\tdemanglerName: " + getDemanglerName() + ",\n" +
|
||||
"\tdemanglerApplicationArguments: " + getDemanglerApplicationArguments() + ",\n" +
|
||||
"}";
|
||||
|
||||
@@ -188,6 +188,21 @@ public class GThreadPool {
|
||||
public Executor getExecutor() {
|
||||
return executor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link ExecutorService} used by this thread pool.
|
||||
*
|
||||
* <P>Note: normal usage of this thread pool contraindicates accessing the executor service of
|
||||
* this pool. For managing your own jobs, you should use the method on this class directly.
|
||||
* The intent of this method is to provide access to the executor service so that it may be
|
||||
* passed to other asynchronous APIs.
|
||||
*
|
||||
* @return the executor service
|
||||
*/
|
||||
public ExecutorService getExecutorService() {
|
||||
return executor;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
|
||||
Reference in New Issue
Block a user