diff --git a/Ghidra/Configurations/Public_Release/src/global/docs/ChangeHistory.md b/Ghidra/Configurations/Public_Release/src/global/docs/ChangeHistory.md
index 0b0e8f53c2..7caba5dd3d 100644
--- a/Ghidra/Configurations/Public_Release/src/global/docs/ChangeHistory.md
+++ b/Ghidra/Configurations/Public_Release/src/global/docs/ChangeHistory.md
@@ -10,6 +10,7 @@
* _Data Types_. Fixed the Data Type Preview to show the typedef data type before the typedef name. (GP-6858)
* _Debugger:Agents_. Patched a possible out-of-memory vulnerability in TraceRmi. (GP-6718)
* _Debugger:Listing_. Added __Disassemble as MIPS__:_:16e__ for the dynamic listing. (GP-6655)
+* _Framework_. Changed potentially unsafe calls to `Class.forName` to perform additional checks prior to instantiation. (GP-6717)
* _Multi-User_. Increased the default value for serialization filter `maxarray` limit from 32,000 to 200,000. This was done to avoid related class serialization errors when communicating with the Ghidra Server for large repository database files. See `Ghidra/Framework/FileSystem/data/serialFilterREADME.md` - it may be necessary to specify an increased `maxarray` value if related serialization errors occur. (GP-6842)
* _Multi-User_. Corrected memory leak issue related to Ghidra Server, affecting both client and server. Buffer compression logic failed to properly release system resources. (GP-6862)
* _Multi-User:Merge_. Corrected bug in Property List Merge which could cause an exception. (GP-6854)
@@ -19,6 +20,10 @@
* _Processors_. Corrected issue with RISC-V attempting to write to constants. (GP-6849, Issue #9198)
* _PyGhidra_. Handled a possible `NotADirectoryError` in `pyghidra_launcher.py`. (GP-6825)
+### Notable API Changes
+* _BSim_. (GP-6866) The unused methods `FunctionDatabase.getConfigurationTemplates` and `FunctionDatabase.isConfigTemplate` have been removed.
+* _Framework_. (GP-6717) Added `ClassSearcher.forNameSafe` method.
+
# Ghidra 12.1 Change History (May 2026)
### New Features
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/AbstractDebuggerParameterDialog.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/AbstractDebuggerParameterDialog.java
index 6aec34def2..3cf3d7da32 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/AbstractDebuggerParameterDialog.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/AbstractDebuggerParameterDialog.java
@@ -52,7 +52,6 @@ import ghidra.util.layout.PairLayout;
public abstract class AbstractDebuggerParameterDialog
extends DialogComponentProvider
implements PropertyChangeListener {
- static final String KEY_MEMORIZED_ARGUMENTS = "memorizedArguments";
public static class BigIntEditor extends PropertyEditorSupport {
String asText = "";
@@ -435,21 +434,7 @@ public abstract class AbstractDebuggerParameterDialog
extends DialogComponent
}
}
- protected record NameTypePair(String name, Class> type) {
- public static NameTypePair fromString(String name) throws ClassNotFoundException {
- String[] parts = name.split(",", 2);
- if (parts.length != 2) {
- // This appears to be a bad assumption - empty fields results in solitary labels
- return new NameTypePair(parts[0], String.class);
- //throw new IllegalArgumentException("Could not parse name,type");
- }
- return new NameTypePair(parts[0], Class.forName(parts[1]));
- }
-
- public final String encodeString() {
- return name + "," + type.getName();
- }
- }
+ protected record NameTypePair(String name, Class> type) {}
private final BidiMap
paramEditors = new DualLinkedHashBidiMap<>();
@@ -722,46 +707,6 @@ public abstract class AbstractDebuggerParameterDialog
extends DialogComponent
new ValStr<>(editor.getValue(), editor.getAsText()));
}
- public void writeConfigState(SaveState saveState) {
- SaveState subState = new SaveState();
- for (Map.Entry> ent : memorized.entrySet()) {
- NameTypePair ntp = ent.getKey();
- P param = parameters.get(ntp.name());
- if (param == null) {
- continue;
- }
- parameterSaveValue(param, subState, ntp.encodeString(), ent.getValue());
- }
- saveState.putSaveState(KEY_MEMORIZED_ARGUMENTS, subState);
- }
-
- public void readConfigState(SaveState saveState) {
- /**
- * TODO: This method is defunct. It is only used by the DebuggerObjectsProvider, which is
- * now deprecated, but I suspect other providers intend to use this in the same way. If
- * those providers don't manually load/compute initial and default values at the time of
- * prompting, then this will need to be fixed. The decode of the values will need to be
- * delayed until (and repeated every time) parameters are populated.
- */
- SaveState subState = saveState.getSaveState(KEY_MEMORIZED_ARGUMENTS);
- if (subState == null) {
- return;
- }
- for (String name : subState.getNames()) {
- try {
- NameTypePair ntp = NameTypePair.fromString(name);
- P param = parameters.get(ntp.name());
- if (param == null) {
- continue;
- }
- memorized.put(ntp, parameterLoadValue(param, subState, ntp.encodeString()));
- }
- catch (Exception e) {
- Msg.error(this, "Error restoring memorized parameter " + name, e);
- }
- }
- }
-
public void setDescription(String htmlDescription) {
if (htmlDescription == null) {
descriptionLabel.setBorder(BorderFactory.createEmptyBorder());
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/InvocationDialogHelper.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/InvocationDialogHelper.java
index 82b8c66bf7..dcb6d58906 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/InvocationDialogHelper.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/InvocationDialogHelper.java
@@ -27,12 +27,11 @@ import docking.test.AbstractDockingTest;
import ghidra.app.plugin.core.debug.utils.MiscellaneousUtils;
import ghidra.async.SwingExecutorService;
import ghidra.debug.api.ValStr;
-import ghidra.framework.options.SaveState;
public class InvocationDialogHelper> {
- public static
> InvocationDialogHelper
waitFor(
- Class cls) {
+ public static > InvocationDialogHelper
+ waitFor(Class cls) {
D dialog = AbstractDockingTest.waitForDialogComponent(cls);
return new InvocationDialogHelper<>(dialog);
}
@@ -86,16 +85,4 @@ public class InvocationDialogHelper dialog.invoke(null));
}
-
- public SaveState saveState() {
- SaveState parent = new SaveState();
- runSwing(() -> dialog.writeConfigState(parent));
- return parent.getSaveState(AbstractDebuggerParameterDialog.KEY_MEMORIZED_ARGUMENTS);
- }
-
- public void loadState(SaveState state) {
- SaveState parent = new SaveState();
- parent.putSaveState(AbstractDebuggerParameterDialog.KEY_MEMORIZED_ARGUMENTS, state);
- runSwing(() -> dialog.readConfigState(parent));
- }
}
diff --git a/Ghidra/Extensions/Jython/src/main/java/ghidra/jython/JythonScriptProvider.java b/Ghidra/Extensions/Jython/src/main/java/ghidra/jython/JythonScriptProvider.java
index 71b408e7e5..7495d7fcdc 100644
--- a/Ghidra/Extensions/Jython/src/main/java/ghidra/jython/JythonScriptProvider.java
+++ b/Ghidra/Extensions/Jython/src/main/java/ghidra/jython/JythonScriptProvider.java
@@ -40,8 +40,7 @@ public class JythonScriptProvider extends AbstractPythonScriptProvider {
throws GhidraScriptLoadException {
try {
- Class> clazz = Class.forName(JythonScript.class.getName());
- GhidraScript script = (GhidraScript) clazz.getConstructor().newInstance();
+ GhidraScript script = new JythonScript();
script.setSourceFile(sourceFile);
return script;
}
diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/FunctionDatabase.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/FunctionDatabase.java
index 3509b0109b..c1bb78cc6c 100755
--- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/FunctionDatabase.java
+++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/FunctionDatabase.java
@@ -15,13 +15,9 @@
*/
package ghidra.features.bsim.query;
-import java.io.*;
-import java.util.*;
+import java.io.FileNotFoundException;
+import java.io.IOException;
-import javax.xml.parsers.*;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import generic.jar.ResourceFile;
@@ -33,7 +29,6 @@ import ghidra.features.bsim.query.facade.SFOverviewInfo;
import ghidra.features.bsim.query.facade.SFQueryInfo;
import ghidra.features.bsim.query.protocol.*;
import ghidra.framework.Application;
-import ghidra.util.Msg;
public interface FunctionDatabase extends AutoCloseable {
@@ -333,72 +328,6 @@ public interface FunctionDatabase extends AutoCloseable {
return new WeightedLSHCosineVectorFactory();
}
- /**
- * Returns a list of all configuration template files.
- *
- * @return list of template files
- */
- public static List getConfigurationTemplates() {
- List templateFiles = new ArrayList<>();
-
- ResourceFile moduleDataSubDirectory;
- try {
- moduleDataSubDirectory = Application.getModuleDataSubDirectory("");
- File templateDir = new File(moduleDataSubDirectory.getAbsolutePath());
- if (!templateDir.exists()) {
- return Collections.emptyList();
- }
-
- FilenameFilter nameFilter = (dir, name) -> {
- if (!name.endsWith(".xml")) {
- return false;
- }
-
- return true;
- };
-
- File[] files = templateDir.listFiles(nameFilter);
- if (files != null) {
- for (File file : files) {
- if (isConfigTemplate(file)) {
- templateFiles.add(file);
- }
- }
- }
- }
- catch (IOException e) {
- Msg.error(null, "Error retrieving configuration templates", e);
- }
-
- return templateFiles;
- }
-
- /**
- * Determines if a given xml file is a config template. This is done by opening the file
- * and checking for the presence of a {@code } root tag.
- *
- * @param file the file to inspect
- * @return true if the file is config template
- */
- static boolean isConfigTemplate(File file) {
-
- try {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- DocumentBuilder builder = factory.newDocumentBuilder();
- Document doc = builder.parse(file);
-
- Element rootElem = doc.getDocumentElement();
- if (rootElem.getTagName().equals("dbconfig")) {
- return true;
- }
- }
- catch (ParserConfigurationException | SAXException | IOException e) {
- Msg.error(null, "Error inspecting xml file", e);
- }
-
- return false;
- }
-
/**
* Get the maximum number of functions to be queried per staged query when searching
* for similar functions.
diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/description/DescriptionManager.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/description/DescriptionManager.java
index c6ad517abd..fb19d51428 100755
--- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/description/DescriptionManager.java
+++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/description/DescriptionManager.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -225,9 +225,6 @@ public class DescriptionManager {
*/
public ExecutableRecord newExecutableRecord(String md5, String enm, String cnm, String arc,
Date dt, String repo, String path, RowKey id) throws LSHException {
- if (md5.length() != 32) {
- throw new LSHException("MD5 field must be exactly 32 hex characters");
- }
ExecutableRecord newexe = new ExecutableRecord(md5, enm, cnm, arc, dt, id, repo, path);
if (!exerec.add(newexe)) {
ExecutableRecord oldexe = exerec.floor(newexe);
diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/description/ExecutableRecord.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/description/ExecutableRecord.java
index 5a920bd31e..61f41af730 100755
--- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/description/ExecutableRecord.java
+++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/description/ExecutableRecord.java
@@ -19,6 +19,8 @@ import java.io.IOException;
import java.io.Writer;
import java.net.URL;
import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import generic.hash.SimpleCRC32;
import ghidra.features.bsim.query.LSHException;
@@ -53,6 +55,9 @@ public class ExecutableRecord implements Comparable {
public static final int METADATA_PATH = 32;
public static final int METADATA_LIBR = 64;
+ private static final String md5Regex = "[a-fA-F0-9]{32}";
+ private static final Pattern md5Matcher = Pattern.compile(md5Regex);
+
private final String md5sum; // The MD5 hash of the executable
private final String executableName; // The name of the executable
private final String architecture; // The architecture on which the executable runs
@@ -77,6 +82,13 @@ public class ExecutableRecord implements Comparable {
public List catinsert; // Non-null, if there are only insertions
}
+ private static void checkValidMD5(String md5) {
+ Matcher matcher = md5Matcher.matcher(md5);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException("Invalid MD5 hash string: " + md5);
+ }
+ }
+
/**
* Convert a 32-bit integer to hexadecimal ascii representation
* @param val is the integer to encode
@@ -125,8 +137,10 @@ public class ExecutableRecord implements Comparable {
/**
* Constructor for searching within a DescriptionManager
* @param md5 is hash of executable being searched for
+ * @throws IllegalArgumentException if {@code md5} is not a valid md5 hash string
*/
protected ExecutableRecord(String md5) {
+ checkValidMD5(md5);
md5sum = md5;
executableName = "";
architecture = "";
@@ -151,9 +165,11 @@ public class ExecutableRecord implements Comparable {
* @param id is the row id of the record
* @param repo is the repository containing the executable (may be null)
* @param path is the path to the executable (may be null)
+ * @throws IllegalArgumentException if {@code md5} is not a valid MD5 hash string
*/
public ExecutableRecord(String md5, String execName, String compilerName, String architecture,
Date date, RowKey id, String repo, String path) {
+ checkValidMD5(md5);
this.md5sum = md5;
this.executableName = execName;
this.architecture = architecture;
@@ -177,9 +193,11 @@ public class ExecutableRecord implements Comparable {
* @param id is the row id of the record
* @param repo is the repository containing the executable (may be null)
* @param pth is the path to the executable (may be null)
+ * @throws IllegalArgumentException if {@code md5} is not a valid MD5 hash string.
*/
public ExecutableRecord(String md5, String enm, String cnm, String arc, Date dt,
List uc, RowKey id, String repo, String pth) {
+ checkValidMD5(md5);
md5sum = md5;
executableName = enm;
architecture = arc;
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/nav/LocationMemento.java b/Ghidra/Features/Base/src/main/java/ghidra/app/nav/LocationMemento.java
index 4097d73e43..5bb322e1bf 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/nav/LocationMemento.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/nav/LocationMemento.java
@@ -24,6 +24,7 @@ import ghidra.program.util.AddressFieldLocation;
import ghidra.program.util.ProgramLocation;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
+import ghidra.util.classfinder.ClassSearcher;
public class LocationMemento {
private static final String PROGRAM_PATH = "PROGRAM_PATH_";
@@ -147,34 +148,25 @@ public class LocationMemento {
return null;
}
- ClassLoader loader = LocationMemento.class.getClassLoader();
try {
- Class> clazz = Class.forName(className, false, loader);
- if (!LocationMemento.class.isAssignableFrom(clazz)) {
- Msg.error(LocationMemento.class, "Class is not a LocationMemento: " + clazz);
- return null;
- }
-
- Class extends LocationMemento> mementoClass = clazz.asSubclass(LocationMemento.class);
+ Class extends LocationMemento> mementoClass = ClassSearcher.forNameSafe(className,
+ LocationMemento.class, LocationMemento.class.getClassLoader());
Constructor extends LocationMemento> constructor =
mementoClass.getConstructor(SaveState.class, Program[].class);
return constructor.newInstance(saveState, programs);
}
catch (ClassNotFoundException e) {
- // class must have been deleted or renamed
+ // this can happen for locations created by plugins that are no longer installed
+ Msg.info(LocationMemento.class,
+ "Unable to identify saved location, class not found: " + className);
}
- catch (InstantiationException e) {
- Msg.showError(ProgramLocation.class, null, "Programming Error", "Class " + className +
- " must have public constructor!", e);
- }
- catch (IllegalAccessException e) {
- Msg.showError(ProgramLocation.class, null, "Programming Error", "Class " + className +
+ catch (InstantiationException | IllegalAccessException e) {
+ Msg.showError(LocationMemento.class, null, "Programming Error", "Class " + className +
" must have public constructor!", e);
}
catch (NoSuchMethodException e) {
- Msg.showError(ProgramLocation.class, null, "Programming Error", "Class " + className +
+ Msg.showError(LocationMemento.class, null, "Programming Error", "Class " + className +
" must have a public constructor that takes a SaveState " + "and a Program[]!", e);
- e.printStackTrace();
}
catch (InvocationTargetException e) {
Throwable cause = e.getCause();
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java
index 53b2db3974..a516aee929 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java
@@ -54,6 +54,7 @@ import ghidra.program.model.listing.Program;
import ghidra.program.util.GhidraProgramUtilities;
import ghidra.program.util.ProgramLocation;
import ghidra.util.*;
+import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
import utilities.util.FileUtilities;
@@ -774,21 +775,21 @@ public class HeadlessAnalyzer {
classLoaderForDotClassScripts =
URLClassLoader.newInstance(urls.toArray(new URL[0]));
- Class> c = Class.forName(className, true, classLoaderForDotClassScripts);
+ ClassSearcher.forNameSafe(className, GhidraScript.class,
+ classLoaderForDotClassScripts);
- if (GhidraScript.class.isAssignableFrom(c)) {
- // No issues, but return null, which signifies we don't actually have a
- // ResourceFile to associate with the script name
- return null;
- }
-
- Msg.error(this,
- "REPORT SCRIPT ERROR: java class '" + className + "' is not a GhidraScript");
+ // No issues, but return null, which signifies we don't actually have a
+ // ResourceFile to associate with the script name
+ return null;
}
catch (ClassNotFoundException e) {
Msg.error(this,
"REPORT SCRIPT ERROR: java class not found for '" + className + "'");
}
+ catch (ClassCastException e) {
+ Msg.error(this,
+ "REPORT SCRIPT ERROR: java class '" + className + "' is not a GhidraScript");
+ }
throw new IllegalArgumentException("Invalid script: " + scriptName);
}
@@ -901,13 +902,14 @@ public class HeadlessAnalyzer {
}
String className = scriptName.substring(0, scriptName.length() - 6);
- Class> c = Class.forName(className, true, classLoaderForDotClassScripts);
+ Class extends GhidraScript> c = ClassSearcher.forNameSafe(className,
+ GhidraScript.class, classLoaderForDotClassScripts);
// Get parent folder to pass to GhidraScript
File parentFile = new File(c.getResource(c.getSimpleName() + ".class").toURI())
.getParentFile();
- currScript = (GhidraScript) c.getConstructor().newInstance();
+ currScript = c.getConstructor().newInstance();
currScript.setScriptArgs(scriptArgs);
if (options.propertiesFilePaths.size() > 0) {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/CodeComparisonViewState.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/CodeComparisonViewState.java
index 9c64c5b3f6..09486c2b83 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/CodeComparisonViewState.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/codecompare/panel/CodeComparisonViewState.java
@@ -20,6 +20,7 @@ import java.util.Map.Entry;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.PluginTool;
+import ghidra.util.classfinder.ClassSearcher;
/**
* A state object to save settings each type of comparison view known by the system. This class
@@ -73,9 +74,8 @@ public class CodeComparisonViewState {
String[] names = classStates.getNames();
for (String className : names) {
try {
- @SuppressWarnings("unchecked")
- Class extends CodeComparisonView> clazz =
- (Class extends CodeComparisonView>) Class.forName(className);
+ Class extends CodeComparisonView> clazz = ClassSearcher.forNameSafe(className,
+ CodeComparisonView.class, getClass().getClassLoader());
SaveState classState = classStates.getSaveState(className);
states.put(clazz, classState);
}
diff --git a/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java b/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java
index 9bb943ba02..8d5690a49b 100644
--- a/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java
+++ b/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java
@@ -15,7 +15,8 @@
*/
package help.screenshot;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import java.awt.*;
import java.awt.geom.GeneralPath;
@@ -80,6 +81,7 @@ import ghidra.program.util.ProgramSelection;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.TestEnv;
import ghidra.util.ColorUtils;
+import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.UsrException;
import ghidra.util.task.TaskMonitor;
@@ -1355,10 +1357,9 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
public Plugin loadPlugin(String className) {
try {
- Class> clazz = Class.forName(className);
- @SuppressWarnings("unchecked")
- Class extends Plugin> pluginClazz = (Class extends Plugin>) clazz;
- return loadPlugin(pluginClazz);
+ Class extends Plugin> clazz =
+ ClassSearcher.forNameSafe(className, Plugin.class, getClass().getClassLoader());
+ return loadPlugin(clazz);
}
catch (ClassCastException e) {
e.printStackTrace();
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePlugins2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePlugins2Test.java
index bb7cba6f52..ab2ad9cd29 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePlugins2Test.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePlugins2Test.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,8 +15,8 @@
*/
package ghidra.framework.plugintool.dialog;
-import static org.hamcrest.collection.IsEmptyCollection.*;
-import static org.hamcrest.core.IsNot.*;
+import static org.hamcrest.collection.IsEmptyCollection.empty;
+import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.*;
import java.util.*;
@@ -31,6 +31,7 @@ import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.*;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.TestEnv;
+import ghidra.util.classfinder.ClassSearcher;
import resources.Icons;
/**
@@ -372,9 +373,9 @@ public class ManagePlugins2Test extends AbstractGhidraHeadedIntegrationTest {
private void loadPlugin(String name) {
try {
- Class> clazz = Class.forName(name);
- Plugin plugin =
- (Plugin) clazz.getDeclaredConstructor(PluginTool.class).newInstance(tool);
+ Class extends Plugin> clazz =
+ ClassSearcher.forNameSafe(name, Plugin.class, getClass().getClassLoader());
+ Plugin plugin = clazz.getDeclaredConstructor(PluginTool.class).newInstance(tool);
loadedPlugins.add(plugin);
}
catch (Exception e) {
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/test/TestKeyEventDispatcher.java b/Ghidra/Framework/Docking/src/main/java/docking/test/TestKeyEventDispatcher.java
index 4c3d4996a1..baeb0fbdf4 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/test/TestKeyEventDispatcher.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/test/TestKeyEventDispatcher.java
@@ -22,7 +22,6 @@ import javax.swing.SwingUtilities;
import docking.FocusOwnerProvider;
import generic.test.TestUtils;
-import ghidra.util.Msg;
/**
* A class that helps to delegate key events to the system override key event dispatcher. This
@@ -64,30 +63,22 @@ public class TestKeyEventDispatcher {
}
private static KeyEventDispatcher getOverriddenKeyEventDispatcher() {
-
// Note: our custom key event dispatcher has package access, so we cannot refer to
// it directly
- try {
- Class> clazz = Class.forName("docking.KeyBindingOverrideKeyEventDispatcher");
- Object customDispatcher = TestUtils.getInstanceField("instance", clazz);
- if (customDispatcher == null) {
- return null; // not installed
- }
-
- //
- // Dependency Inject our own focus provider so that we can force the event
- // dispatcher to deliver events to our component
- //
- TestUtils.invokeInstanceMethod("setFocusOwnerProvider", customDispatcher,
- FocusOwnerProvider.class, focusProvider);
-
- return (KeyEventDispatcher) customDispatcher;
- }
- catch (ClassNotFoundException e) {
- Msg.error(TestKeyEventDispatcher.class, "Unable to find the system KeyEventDispatcher",
- e);
- return null;
+ Class> clazz = docking.KeyBindingOverrideKeyEventDispatcher.class;
+ Object customDispatcher = TestUtils.getInstanceField("instance", clazz);
+ if (customDispatcher == null) {
+ return null; // not installed
}
+
+ //
+ // Dependency Inject our own focus provider so that we can force the event
+ // dispatcher to deliver events to our component
+ //
+ TestUtils.invokeInstanceMethod("setFocusOwnerProvider", customDispatcher,
+ FocusOwnerProvider.class, focusProvider);
+
+ return (KeyEventDispatcher) customDispatcher;
}
private static class TestFocusOwnerProvider implements FocusOwnerProvider {
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/ModifiedPcodeThread.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/ModifiedPcodeThread.java
index efcb48dd9d..5e750d609d 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/ModifiedPcodeThread.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emu/ModifiedPcodeThread.java
@@ -34,6 +34,7 @@ import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
import ghidra.util.Msg;
+import ghidra.util.classfinder.ClassSearcher;
/**
* A p-code thread which incorporates per-architecture state modifiers
@@ -232,16 +233,11 @@ public class ModifiedPcodeThread extends DefaultPcodeThread {
return null;
}
try {
- Class> c = Class.forName(classname);
- if (!EmulateInstructionStateModifier.class.isAssignableFrom(c)) {
- Msg.error(this,
- "Language " + language.getLanguageID() + " does not specify a valid " +
- GhidraLanguagePropertyKeys.EMULATE_INSTRUCTION_STATE_MODIFIER_CLASS);
- throw new RuntimeException(classname + " does not implement interface " +
- EmulateInstructionStateModifier.class.getName());
- }
- Constructor> constructor = c.getConstructor(Emulate.class);
- return (EmulateInstructionStateModifier) constructor.newInstance(emulate);
+ Class extends EmulateInstructionStateModifier> c = ClassSearcher.forNameSafe(
+ classname, EmulateInstructionStateModifier.class, getClass().getClassLoader());
+ Constructor extends EmulateInstructionStateModifier> constructor =
+ c.getConstructor(Emulate.class);
+ return constructor.newInstance(emulate);
}
catch (Exception e) {
Msg.error(this, "Language " + language.getLanguageID() + " does not specify a valid " +
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emulate/Emulate.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emulate/Emulate.java
index a8c28bd66e..c0ef5fca99 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emulate/Emulate.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/emulate/Emulate.java
@@ -31,6 +31,7 @@ import ghidra.program.model.listing.Instruction;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
import ghidra.util.Msg;
+import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@@ -113,18 +114,10 @@ public class Emulate {
return;
}
try {
- Class> c = Class.forName(classname);
- if (!EmulateInstructionStateModifier.class.isAssignableFrom(c)) {
- Msg.error(this,
- "Language " + language.getLanguageID() + " does not specify a valid " +
- GhidraLanguagePropertyKeys.EMULATE_INSTRUCTION_STATE_MODIFIER_CLASS);
- throw new RuntimeException(classname + " does not implement interface " +
- EmulateInstructionStateModifier.class.getName());
- }
- Class extends EmulateInstructionStateModifier> instructionStateModifierClass =
- (Class extends EmulateInstructionStateModifier>) c;
+ Class extends EmulateInstructionStateModifier> c = ClassSearcher.forNameSafe(
+ classname, EmulateInstructionStateModifier.class, getClass().getClassLoader());
Constructor extends EmulateInstructionStateModifier> constructor =
- instructionStateModifierClass.getConstructor(Emulate.class);
+ c.getConstructor(Emulate.class);
instructionStateModifier = constructor.newInstance(this);
}
catch (Exception e) {
diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/Application.java b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/Application.java
index 4c753f4dc5..ad170f33b1 100644
--- a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/Application.java
+++ b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/Application.java
@@ -252,7 +252,7 @@ public class Application {
private ResourceFile getModuleForClass(String className) {
try {
- Class> callersClass = Class.forName(className);
+ Class> callersClass = Class.forName(className, false, getClass().getClassLoader());
return getModuleForClass(callersClass);
}
catch (ClassNotFoundException e) {
diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/GProperties.java b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/GProperties.java
index 8c8debe80a..529f64baab 100644
--- a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/GProperties.java
+++ b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/GProperties.java
@@ -33,6 +33,7 @@ import com.google.gson.*;
import ghidra.util.Msg;
import ghidra.util.NumericUtilities;
+import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.AssertException;
import ghidra.util.xml.XmlUtilities;
import utilities.util.FileUtilities;
@@ -433,17 +434,16 @@ public class GProperties {
}
Enum> getEnumValue(String enumClassName, String value) {
-
- ClassLoader loader = getClass().getClassLoader();
try {
- Class> clazz = Class.forName(enumClassName, false, loader);
- if (!Enum.class.isAssignableFrom(clazz)) {
- Msg.error(this, "Class is not an Enum: " + clazz);
- return null;
- }
+ // It would be better to restrict this to some interface we define. Otherwise, we ought
+ // to examine the static initializers of every enum in our classpath. Luckily, the
+ // constructor invocations are all defined by the enum values, and so cannot be randomly
+ // invoked. Famous last words: I doubt there's anything of concern in any enum's static
+ // initializer....
+ Class> enumClass =
+ ClassSearcher.forNameSafe(enumClassName, Enum.class, getClass().getClassLoader());
- // Note: calling valueOf() will trigger class initialization
- Method m = clazz.getMethod("valueOf", new Class[] { String.class });
+ Method m = enumClass.getMethod("valueOf", new Class[] { String.class });
if (m != null) {
return (Enum>) m.invoke(null, new Object[] { value });
}
diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/TestSuiteUtilities.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/TestSuiteUtilities.java
index 99bc0bce20..4e9af75d1c 100644
--- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/TestSuiteUtilities.java
+++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/TestSuiteUtilities.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -21,7 +21,6 @@ import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
-import ghidra.util.exception.AssertException;
import junit.framework.TestSuite;
import utilities.util.FileUtilities;
@@ -34,11 +33,7 @@ public class TestSuiteUtilities {
private static Class> TEST_CASE_CLASS = createTestClass();
private static Class> createTestClass() {
- try {
- return Class.forName("junit.framework.TestCase");
- } catch (ClassNotFoundException e) {
- throw new AssertException();
- }
+ return junit.framework.TestCase.class;
}
/**
diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.java
index 23d25f7554..9cf7bb677c 100644
--- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.java
+++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.java
@@ -274,6 +274,42 @@ public class ClassSearcher {
}
+ /**
+ * Get the named class in a safe(r) manner.
+ *
+ * This addresses the concern that many tools and database entries can refer to custom types,
+ * e.g., options and properties, that may permit an attacker to construct instances of arbitrary
+ * class, those constructors perhaps implementing gadgets that could be used in an exploit
+ * chain.
+ *
+ * The pattern to "mitigate" this is to ensure the found class implements a given interface or
+ * extends from a given class, before invoking a constructor. Ideally, this check can
+ * be applied before class initialization. While much narrower, static initializers may
+ * also provide gadgets. This method implements the pattern which avoids class initialization
+ * until the type has been checked.
+ *
+ * WARNING: Do not pass {@link Object}, a standard JDK interface, or any interface from a
+ * dependency like apache-commons. The purpose of the super type is to narrow the set of
+ * acceptable classes to those we control or that a user has intentionally installed/loaded as a
+ * Ghidra extension. Thus, the static initializers and constructors of all subclasses should be
+ * reviewed carefully.
+ *
+ * @param the super type
+ * @param name the class binary name, as in {@link Class#forName(String, boolean, ClassLoader)}.
+ * @param sup the super type
+ * @param loader the class loader, or {@code null} to use the bootstrap loader.
+ * @return the found class as a subclass of the given type
+ * @throws ClassNotFoundException if the class cannot be found
+ * @throws ClassCastException if the found class is not a sub type of the given type
+ */
+ public static Class extends T> forNameSafe(String name, Class sup, ClassLoader loader)
+ throws ClassNotFoundException {
+ Class> cls = Class.forName(name, false, loader);
+ Class extends T> sub = cls.asSubclass(sup);
+ Class.forName(name, true, loader); // Initialize it this time
+ return sub;
+ }
+
/**
* Add a change listener that will be notified when the classpath
* is searched for new classes.
@@ -631,11 +667,11 @@ public class ClassSearcher {
/**
* If the given class name matches the known extension name patterns, then this method will try
- * to load that class using the provided path. Extensions may be loaded using their own
- * class loader, depending on the system property
+ * to load that class using the provided path. Extensions may be loaded using their own
+ * class loader, depending on the system property
* {@link GhidraClassLoader#ENABLE_RESTRICTED_EXTENSIONS_PROPERTY}.
*
- * Examples:
+ * Examples:
*
* /foo/bar/baz/file.jar fully.qualified.ClassName
* /foo/bar/baz/bin fully.qualified.ClassName
diff --git a/Ghidra/Framework/Generic/src/test/java/ghidra/util/classfinder/ClassSearcherTest.java b/Ghidra/Framework/Generic/src/test/java/ghidra/util/classfinder/ClassSearcherTest.java
new file mode 100644
index 0000000000..e3187136e1
--- /dev/null
+++ b/Ghidra/Framework/Generic/src/test/java/ghidra/util/classfinder/ClassSearcherTest.java
@@ -0,0 +1,80 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.util.classfinder;
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Constructor;
+
+import org.junit.Test;
+
+public class ClassSearcherTest {
+ public enum Canary {
+ ALIVE, DEAD;
+ }
+
+ public static Canary canary;
+
+ public interface TestSuper {
+ }
+
+ public interface WrongSuper {
+ }
+
+ public static class ClassWithStaticInitializerSideEffects implements TestSuper {
+ static {
+ canary = Canary.DEAD;
+ }
+ }
+
+ /**
+ * Test that our safe(r) version of {@link Class#forName(String)} does not invoke any static
+ * initializer.
+ *
+ * @throws Exception because
+ */
+ @Test
+ public void testForNameSafeNoClinit() throws Exception {
+ canary = Canary.ALIVE;
+
+ Class extends TestSuper> found = ClassSearcher.forNameSafe(
+ "ghidra.util.classfinder.ClassSearcherTest$ClassWithStaticInitializerSideEffects",
+ TestSuper.class, getClass().getClassLoader());
+ assertNotNull(found);
+ assertEquals(Canary.ALIVE, canary);
+ Constructor extends TestSuper> constructor = found.getConstructor();
+ assertEquals(Canary.ALIVE, canary);
+ TestSuper instance = constructor.newInstance();
+ assertNotNull(instance);
+ assertEquals(Canary.DEAD, canary);
+ }
+
+ @Test
+ public void testForNameSafeNoClinitErr() throws Exception {
+ canary = Canary.ALIVE;
+
+ try {
+ ClassSearcher.forNameSafe(
+ "ghidra.util.classfinder.ClassSearcherTest$ClassWithStaticInitializerSideEffects",
+ WrongSuper.class, getClass().getClassLoader());
+ fail("Should not have permitted the class");
+ }
+ catch (ClassCastException e) {
+ // pass
+ }
+ assertEquals(Canary.ALIVE, canary);
+ }
+}
diff --git a/Ghidra/Framework/Gui/src/main/java/generic/theme/ThemePreferences.java b/Ghidra/Framework/Gui/src/main/java/generic/theme/ThemePreferences.java
index 75edc6f2ec..35bb3c6a8c 100644
--- a/Ghidra/Framework/Gui/src/main/java/generic/theme/ThemePreferences.java
+++ b/Ghidra/Framework/Gui/src/main/java/generic/theme/ThemePreferences.java
@@ -20,6 +20,7 @@ import java.io.IOException;
import ghidra.framework.preferences.Preferences;
import ghidra.util.Msg;
+import ghidra.util.classfinder.ClassSearcher;
/**
* Reads and writes current theme info to preferences
@@ -47,15 +48,9 @@ public class ThemePreferences {
else if (themeId.startsWith(DiscoverableGTheme.CLASS_PREFIX)) {
String className = themeId.substring(DiscoverableGTheme.CLASS_PREFIX.length());
try {
- ClassLoader loader = getClass().getClassLoader();
- Class> clazz = Class.forName(className, false, loader);
- if (!GTheme.class.isAssignableFrom(clazz)) {
- Msg.showError(GTheme.class, null, "Can't Load Previous Theme",
- "Theme class name does not point to a GTheme instance: " + className);
- return ThemeManager.getDefaultTheme();
- }
-
- return (GTheme) clazz.getDeclaredConstructor().newInstance();
+ Class extends GTheme> clazz =
+ ClassSearcher.forNameSafe(className, GTheme.class, getClass().getClassLoader());
+ return clazz.getDeclaredConstructor().newInstance();
}
catch (Exception e) {
Msg.showError(GTheme.class, null, "Can't Load Previous Theme",
diff --git a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/FileOptions.java b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/FileOptions.java
index bf681a3589..829115f528 100644
--- a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/FileOptions.java
+++ b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/FileOptions.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -25,6 +25,7 @@ import org.apache.commons.io.FilenameUtils;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
+import ghidra.util.classfinder.ClassSearcher;
public class FileOptions extends AbstractOptions {
@@ -53,8 +54,9 @@ public class FileOptions extends AbstractOptions {
public CustomOption readCustomOption(GProperties properties) {
String customOptionClassName = properties.getString("CUSTOM_OPTION_CLASS", null);
try {
- Class> c = Class.forName(customOptionClassName);
- CustomOption customOption = (CustomOption) c.getDeclaredConstructor().newInstance();
+ Class extends CustomOption> c = ClassSearcher.forNameSafe(customOptionClassName,
+ CustomOption.class, getClass().getClassLoader());
+ CustomOption customOption = c.getDeclaredConstructor().newInstance();
customOption.readState(properties);
return customOption;
}
diff --git a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/OptionType.java b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/OptionType.java
index e358029ee4..d9a2d7f16a 100644
--- a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/OptionType.java
+++ b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/OptionType.java
@@ -27,6 +27,7 @@ import org.jdom2.input.SAXBuilder;
import org.jdom2.output.XMLOutputter;
import ghidra.util.Msg;
+import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.xml.GenericXMLOutputter;
import ghidra.util.xml.XmlUtilities;
@@ -231,8 +232,9 @@ public enum OptionType {
String customOptionClassName =
saveState.getString(CustomOption.CUSTOM_OPTION_CLASS_NAME_KEY, null);
try {
- Class> c = Class.forName(customOptionClassName);
- CustomOption option = (CustomOption) c.getConstructor().newInstance();
+ Class extends CustomOption> c = ClassSearcher.forNameSafe(customOptionClassName,
+ CustomOption.class, OptionType.class.getClassLoader());
+ CustomOption option = c.getConstructor().newInstance();
option.readState(saveState);
return option;
}
diff --git a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/ToolOptions.java b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/ToolOptions.java
index c08fb8c46f..4f8582e952 100644
--- a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/ToolOptions.java
+++ b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/ToolOptions.java
@@ -30,6 +30,7 @@ import org.jdom2.Element;
import ghidra.util.*;
import ghidra.util.bean.opteditor.OptionsVetoException;
+import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.AssertException;
/**
@@ -151,9 +152,11 @@ public class ToolOptions extends AbstractOptions {
continue; // shouldn't happen
}
- Class> c = Class.forName(element.getAttributeValue(CLASS_ATTRIBUTE));
- Constructor> constructor = c.getDeclaredConstructor();
- WrappedOption wo = (WrappedOption) constructor.newInstance();
+ Class extends WrappedOption> c =
+ ClassSearcher.forNameSafe(element.getAttributeValue(CLASS_ATTRIBUTE),
+ WrappedOption.class, getClass().getClassLoader());
+ Constructor extends WrappedOption> constructor = c.getDeclaredConstructor();
+ WrappedOption wo = constructor.newInstance();
wo.readState(new SaveState(element));
if (wo instanceof WrappedCustomOption wrappedCustom && !wrappedCustom.isValid()) {
continue;
diff --git a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/WrappedCustomOption.java b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/WrappedCustomOption.java
index f6f155ca12..3f35cafe5c 100644
--- a/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/WrappedCustomOption.java
+++ b/Ghidra/Framework/Gui/src/main/java/ghidra/framework/options/WrappedCustomOption.java
@@ -16,6 +16,7 @@
package ghidra.framework.options;
import ghidra.util.Msg;
+import ghidra.util.classfinder.ClassSearcher;
public class WrappedCustomOption implements WrappedOption {
@@ -33,28 +34,21 @@ public class WrappedCustomOption implements WrappedOption {
@Override
public void readState(SaveState saveState) {
- String className = saveState.getString("CUSTOM OPTION CLASS", null);
+ String customOptionClassName = saveState.getString("CUSTOM OPTION CLASS", null);
valid = false;
try {
-
- ClassLoader loader = getClass().getClassLoader();
- Class> clazz = Class.forName(className, false, loader);
- if (!CustomOption.class.isAssignableFrom(clazz)) {
- Msg.error(this, "Can't create custom option instance; not a CustomOpiton: " +
- className);
- return;
- }
-
- value = (CustomOption) clazz.getConstructor().newInstance();
+ Class extends CustomOption> c = ClassSearcher.forNameSafe(customOptionClassName,
+ CustomOption.class, getClass().getClassLoader());
+ value = c.getConstructor().newInstance();
value.readState(saveState);
valid = true;
}
catch (ClassNotFoundException e) {
Msg.info(this,
- "Custom option class '%s' does not exist".formatted(className));
+ "Custom option class '%s' does not exist".formatted(customOptionClassName));
}
catch (Exception e) {
- Msg.error(this, "Can't create custom option instance for: " + className, e);
+ Msg.error(this, "Can't create custom option instance for: " + customOptionClassName, e);
}
}
diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/ToolUtils.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/ToolUtils.java
index cad16dcc4b..568bad7d20 100644
--- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/ToolUtils.java
+++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/ToolUtils.java
@@ -25,10 +25,13 @@ import org.jdom2.*;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.XMLOutputter;
+import com.sun.source.util.Plugin;
+
import ghidra.framework.model.ProjectManager;
import ghidra.framework.model.ToolTemplate;
import ghidra.framework.project.tool.GhidraToolTemplate;
import ghidra.util.*;
+import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.AssertException;
import ghidra.util.xml.GenericXMLOutputter;
import ghidra.util.xml.XmlUtilities;
@@ -175,7 +178,7 @@ public class ToolUtils {
// check to see if we can still find the plugin class (it may have
// been removed)
try {
- Class.forName(value);
+ ClassSearcher.forNameSafe(value, Plugin.class, ToolUtils.class.getClassLoader());
}
catch (Throwable t) {
// oh well, leave it out
diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginUtils.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginUtils.java
index 92a690fa93..dd8ce1aa99 100644
--- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginUtils.java
+++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginUtils.java
@@ -85,11 +85,8 @@ public class PluginUtils {
}
}
- Class> tmpClass = Class.forName(pluginClassName);
- if (!Plugin.class.isAssignableFrom(tmpClass)) {
- throw new PluginException(
- "Class " + pluginClassName + " is not derived from Plugin");
- }
+ Class extends Plugin> tmpClass = ClassSearcher.forNameSafe(pluginClassName,
+ Plugin.class, PluginUtils.class.getClassLoader());
return tmpClass.asSubclass(Plugin.class);
}
catch (ClassNotFoundException e) {
@@ -134,8 +131,8 @@ public class PluginUtils {
}
if (defaultProviderClassName != null) {
try {
- Class> tmpClass = Class.forName(defaultProviderClassName);
- return tmpClass.asSubclass(Plugin.class);
+ return ClassSearcher.forNameSafe(defaultProviderClassName, Plugin.class,
+ PluginUtils.class.getClassLoader());
}
catch (ClassCastException cce) {
Msg.error(PluginUtils.class,
@@ -145,7 +142,6 @@ public class PluginUtils {
catch (ClassNotFoundException e) {
throw new AssertException(
"default provider class for " + serviceClass.getName() + " not found!");
-
}
}
return null;
diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/GhidraToolTemplate.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/GhidraToolTemplate.java
index b995f92be7..70769da083 100644
--- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/GhidraToolTemplate.java
+++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/GhidraToolTemplate.java
@@ -22,11 +22,11 @@ import javax.swing.ImageIcon;
import org.jdom2.Element;
import docking.util.image.ToolIconURL;
-import ghidra.framework.model.Project;
-import ghidra.framework.model.ToolTemplate;
+import ghidra.framework.model.*;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.Msg;
import ghidra.util.NumericUtilities;
+import ghidra.util.classfinder.ClassSearcher;
/**
* Implementation for a tool template that has the class names of the
@@ -135,8 +135,8 @@ public class GhidraToolTemplate implements ToolTemplate {
Element elem = (Element) list.get(i);
String className = elem.getAttribute(CLASS_NAME_XML_NAME).getValue();
try {
- // no need to perform static initialization; clients only check isAssignableFrom()
- dtList.add(Class.forName(className, false, loader));
+ dtList.add(ClassSearcher
+ .forNameSafe(className, DomainObject.class, loader));
}
catch (ClassNotFoundException e) {
Msg.warn(this, "Tool supported content class not found: " + className);
@@ -182,8 +182,7 @@ public class GhidraToolTemplate implements ToolTemplate {
iconElem.setText(NumericUtilities.convertBytesToString(iconURL.getIconBytes()));
}
root.addContent(iconElem);
-
- root.addContent((toolElement.clone()));
+ root.addContent(toolElement.clone());
return root;
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java
index a696a6394a..3576bad85d 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java
@@ -45,6 +45,7 @@ import ghidra.program.model.pcode.*;
import ghidra.program.model.util.ProcessorSymbolType;
import ghidra.sleigh.grammar.SourceFileIndexer;
import ghidra.util.*;
+import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.exception.TimeoutException;
import ghidra.util.task.PreserveStateWrappingTaskMonitor;
@@ -1492,18 +1493,10 @@ public class SleighLanguage implements Language {
return;
}
try {
- Class> helperClass = Class.forName(className);
- if (!ParallelInstructionLanguageHelper.class.isAssignableFrom(helperClass)) {
- Msg.error(this,
- "Invalid Class specified for " +
- GhidraLanguagePropertyKeys.PARALLEL_INSTRUCTION_HELPER_CLASS + " (" +
- helperClass.getName() + "): " + description.getSpecFile());
- }
- else {
- parallelHelper =
- (ParallelInstructionLanguageHelper) helperClass.getDeclaredConstructor()
- .newInstance();
- }
+ Class extends ParallelInstructionLanguageHelper> helperClass =
+ ClassSearcher.forNameSafe(className, ParallelInstructionLanguageHelper.class,
+ getClass().getClassLoader());
+ parallelHelper = helperClass.getDeclaredConstructor().newInstance();
}
catch (Exception e) {
throw new SleighException("Failed to instantiate " +
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java
index ccbacf5583..371f9c7acd 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java
@@ -51,6 +51,7 @@ import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Function;
import ghidra.util.*;
import ghidra.util.Lock.Closeable;
+import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.classfinder.ClassTranslator;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
@@ -2491,7 +2492,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
/**
* Queue a datatype to deleted in response to another datatype being deleted.
- * @param id datatype ID to be removed
+ * @param dt datatype to be deleted
+ * @param id datatype ID to be deleted
*/
protected void addDataTypeToDelete(DataType dt, long id) {
if (dt instanceof DataTypeDB dbDt) {
@@ -2807,9 +2809,10 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
String classPath = record.getString(BuiltinDBAdapter.BUILT_IN_CLASSNAME_COL);
String name = record.getString(BuiltinDBAdapter.BUILT_IN_NAME_COL);
try {
- Class> clazz;
+ Class extends BuiltInDataType> clazz;
try {
- clazz = Class.forName(classPath);
+ clazz = ClassSearcher.forNameSafe(classPath, BuiltInDataType.class,
+ getClass().getClassLoader());
}
catch (ClassNotFoundException | NoClassDefFoundError e) {
// Check the classNameMap.
@@ -2818,15 +2821,15 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
throw e;
}
try {
- clazz = Class.forName(newClassPath);
+ clazz = ClassSearcher.forNameSafe(newClassPath, BuiltInDataType.class,
+ getClass().getClassLoader());
}
catch (ClassNotFoundException e1) {
throw e1;
}
}
- BuiltInDataType bdt =
- (BuiltInDataType) clazz.getDeclaredConstructor().newInstance();
+ BuiltInDataType bdt = clazz.getDeclaredConstructor().newInstance();
bdt.setName(name);
bdt.setCategoryPath(catPath);
@@ -4541,7 +4544,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
* Check for cached equivalence of a type contained within this datatype manager
* against another datatype. Every call to this method when {@code null} is
* returned must be following by an invocation of
- * {@link #putCachedEquivalence(DataTypeDB, DataType, boolean)} once an
+ * {@link #putCachedEquivalence(DataTypeDB, DataType, Boolean)} once an
* equivalence determination has been made. The number of outstanding calls to
* this method will be tracked. When the outstanding call count returns to zero
* the cache will be cleared.
@@ -4554,7 +4557,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
* @return true, false or {@code null} if unknown. A {@code null} value mandates
* that the caller make a determination and put the result into the
* cache when known (see
- * {@link #putCachedEquivalence(DataTypeDB, DataType, boolean)}.
+ * {@link #putCachedEquivalence(DataTypeDB, DataType, Boolean)}.
*/
Boolean getCachedEquivalence(DataTypeDB dataTypeDB, DataType dataType) {
EquivalenceCache cache = equivalenceCache.get();
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/ObjectPropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/ObjectPropertyMapDB.java
index 782a6185a9..f14fb987e4 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/ObjectPropertyMapDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/ObjectPropertyMapDB.java
@@ -28,6 +28,7 @@ import ghidra.program.util.ChangeManager;
import ghidra.util.Lock.Closeable;
import ghidra.util.Msg;
import ghidra.util.Saveable;
+import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.classfinder.ClassTranslator;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
@@ -133,9 +134,10 @@ public class ObjectPropertyMapDB extends PropertyMapDB
*/
@SuppressWarnings("unchecked")
public static Class extends Saveable> getSaveableClassForName(String classPath) {
- Class> c = null;
+ Class extends Saveable> c = null;
try {
- c = Class.forName(classPath);
+ c = ClassSearcher.forNameSafe(classPath, Saveable.class,
+ ObjectPropertyMapDB.class.getClassLoader());
}
catch (ClassNotFoundException e) {
// Check the classNameMap.
@@ -143,10 +145,10 @@ public class ObjectPropertyMapDB extends PropertyMapDB
if (newClassPath != null) {
classPath = newClassPath;
try {
- c = Class.forName(newClassPath);
+ c = ClassSearcher.forNameSafe(newClassPath, Saveable.class,
+ ObjectPropertyMapDB.class.getClassLoader());
}
catch (ClassNotFoundException e1) {
-
// Since we can't get the class, at least handle it generically.
}
}
@@ -159,7 +161,7 @@ public class ObjectPropertyMapDB extends PropertyMapDB
"Object property class does not implement Saveable interface: " + classPath);
}
// If unable to get valid Saveable class use generic implementation
- return (c != null) ? (Class extends Saveable>) c : GenericSaveable.class;
+ return (c != null) ? c : GenericSaveable.class;
}
/**
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/disassemble/Disassembler.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/disassemble/Disassembler.java
index 876bf6978f..479cc8150a 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/disassemble/Disassembler.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/disassemble/Disassembler.java
@@ -37,6 +37,7 @@ import ghidra.program.util.AbstractProgramContext;
import ghidra.program.util.ProgramContextImpl;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
+import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@@ -1864,29 +1865,28 @@ public class Disassembler implements DisassemblerConflictHandler {
}
- @SuppressWarnings("unchecked")
private static Class extends Disassembler> getLanguageSpecificDisassembler(Language parser) {
String className = parser.getProperty(GhidraLanguagePropertyKeys.CUSTOM_DISASSEMBLER_CLASS);
if (className == null) {
return null;
}
try {
- Class> disassemblerClass = Class.forName(className);
- if (!Disassembler.class.isAssignableFrom(disassemblerClass)) {
- Msg.error(Disassembler.class,
- "Invalid Class specified for " +
- GhidraLanguagePropertyKeys.CUSTOM_DISASSEMBLER_CLASS + " (" +
- disassemblerClass.getName() + "): " +
- parser.getLanguageDescription().getLanguageID());
- return null;
- }
- return (Class extends Disassembler>) disassemblerClass;
+ Class extends Disassembler> disassemblerClass = ClassSearcher.forNameSafe(className,
+ Disassembler.class, Disassembler.class.getClassLoader());
+ return disassemblerClass;
}
catch (ClassNotFoundException e) {
throw new RuntimeException("Invalid Class specified for " +
GhidraLanguagePropertyKeys.CUSTOM_DISASSEMBLER_CLASS + " (" + className + "): " +
parser.getLanguageDescription().getLanguageID(), e);
}
+ catch (ClassCastException e) {
+ Msg.error(Disassembler.class,
+ "Invalid Class specified for " +
+ GhidraLanguagePropertyKeys.CUSTOM_DISASSEMBLER_CLASS + " (" + className +
+ "): " +
+ parser.getLanguageDescription().getLanguageID());
+ return null;
+ }
}
-
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java
index 9b36c235c5..ab14bef53a 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java
@@ -38,6 +38,7 @@ import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
+import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*;
@@ -249,7 +250,6 @@ public class BasicCompilerSpec implements CompilerSpec {
restoreXml(parser);
}
- @SuppressWarnings("unchecked")
private void buildInjectLibrary() {
String classname =
language.getProperty(GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS);
@@ -258,18 +258,10 @@ public class BasicCompilerSpec implements CompilerSpec {
}
else {
try {
- Class> c = Class.forName(classname);
- if (!PcodeInjectLibrary.class.isAssignableFrom(c)) {
- Msg.error(this,
- "Language " + language.getLanguageID() + " does not specify a valid " +
- GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS);
- throw new RuntimeException(classname + " does not implement interface " +
- PcodeInjectLibrary.class.getName());
- }
- Class extends PcodeInjectLibrary> injectLibraryClass =
- (Class extends PcodeInjectLibrary>) c;
+ Class extends PcodeInjectLibrary> c = ClassSearcher.forNameSafe(classname,
+ PcodeInjectLibrary.class, getClass().getClassLoader());
Constructor extends PcodeInjectLibrary> constructor =
- injectLibraryClass.getConstructor(SleighLanguage.class);
+ c.getConstructor(SleighLanguage.class);
pcodeInject = constructor.newInstance(language);
}
catch (Exception e) {
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java
index 5d22182486..dc42ebde3b 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java
@@ -22,6 +22,7 @@ import ghidra.framework.options.SaveState;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.util.Msg;
+import ghidra.util.classfinder.ClassSearcher;
/**
* ProgramLocation provides information about a location in a program in the most
@@ -278,10 +279,10 @@ public class ProgramLocation implements Cloneable, Comparable {
return null;
}
- ClassLoader loader = ProgramLocation.class.getClassLoader();
try {
+ Class extends ProgramLocation> locationClass = ClassSearcher.forNameSafe(className,
+ ProgramLocation.class, ProgramLocation.class.getClassLoader());
- Class> locationClass = Class.forName(className, false, loader);
if (locationClass.isInterface()) {
// This check is needed due to a refactoring that has changed a class into an
// interface. The class name may have been saved into the tool. Upon restoring we
@@ -290,13 +291,7 @@ public class ProgramLocation implements Cloneable, Comparable {
return null;
}
- if (!ProgramLocation.class.isAssignableFrom(locationClass)) {
- Msg.error(ProgramLocation.class,
- "Class is not a ProgramLocation: " + locationClass);
- return null;
- }
-
- ProgramLocation loc = (ProgramLocation) locationClass.getConstructor().newInstance();
+ ProgramLocation loc = locationClass.getConstructor().newInstance();
loc.restoreState(program, saveState);
if (loc.getAddress() != null) {
return loc;
@@ -305,6 +300,8 @@ public class ProgramLocation implements Cloneable, Comparable {
}
catch (ClassNotFoundException e) {
// this can happen for locations created by plugins that are no longer installed
+ Msg.info(ProgramLocation.class,
+ "Unable to restore program location, class not found: " + className);
}
catch (InstantiationException | IllegalAccessException | NoSuchMethodException e) {
Msg.showError(ProgramLocation.class, null, "Programming Error",
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/SimpleLanguageTranslator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/SimpleLanguageTranslator.java
index 3eb1f52598..9f38bc8a6a 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/SimpleLanguageTranslator.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/SimpleLanguageTranslator.java
@@ -33,6 +33,7 @@ import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.IncompatibleLanguageException;
import ghidra.program.model.listing.Program;
+import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import ghidra.util.xml.XmlUtilities;
@@ -386,18 +387,18 @@ class SimpleLanguageTranslator extends LanguageTranslatorAdapter {
}
}
- @SuppressWarnings("unchecked")
private static Class extends LanguagePostUpgradeInstructionHandler> parsePostUpgradeHandlerEntry(
Element element) throws SAXException {
-
String className = element.getAttributeValue("class");
if (className == null) {
throw new SAXException(element.getName() + " must specify 'class' attribute");
}
try {
- Class> clazz = Class.forName(className);
+ Class extends LanguagePostUpgradeInstructionHandler> clazz =
+ ClassSearcher.forNameSafe(className, LanguagePostUpgradeInstructionHandler.class,
+ SimpleLanguageTranslator.class.getClassLoader());
getPostUpgradeInstructionHandler((Program) null, clazz); // test construction
- return (Class extends LanguagePostUpgradeInstructionHandler>) clazz;
+ return clazz;
}
catch (Exception e) {
if (e instanceof SAXException) {
diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/TraceRmiLaunchDialogTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/TraceRmiLaunchDialogTest.java
index f44bbb2c4e..2cf7449bc9 100644
--- a/Ghidra/Test/DebuggerIntegrationTest/src/test/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/TraceRmiLaunchDialogTest.java
+++ b/Ghidra/Test/DebuggerIntegrationTest/src/test/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/TraceRmiLaunchDialogTest.java
@@ -24,7 +24,8 @@ import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
-import org.junit.*;
+import org.junit.Before;
+import org.junit.Test;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
import ghidra.app.plugin.core.debug.gui.InvocationDialogHelper;
@@ -32,7 +33,6 @@ import ghidra.app.plugin.core.debug.gui.tracermi.launcher.ScriptAttributesParser
import ghidra.async.SwingExecutorService;
import ghidra.debug.api.ValStr;
import ghidra.debug.api.tracermi.LaunchParameter;
-import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.AutoConfigState.PathIsDir;
import ghidra.framework.plugintool.AutoConfigState.PathIsFile;
@@ -146,37 +146,6 @@ public class TraceRmiLaunchDialogTest extends AbstractGhidraHeadedDebuggerTest {
result.h.invoke();
}
- @Test
- public void testIntSaveHexValue() throws Throwable {
- PromptResult result = prompt(PARAM_INT);
- result.h.setArgAsString(PARAM_INT, "0x11");
- result.h.invoke();
-
- SaveState state = result.h.saveState();
- assertEquals("0x11", state.getString("some_int,java.math.BigInteger", null));
- }
-
- @Test
- @Ignore
- public void testIntLoadHexValue() throws Throwable {
- /**
- * TODO: This is a bit out of order. However, the dialog cannot load/decode from the state
- * until it has the parameters. Worse, to check that user input was valid, the dialog
- * verifies that the value it gets back matches the text in the box, because if it doesn't,
- * then the editor must have failed to parse/decode the value. Currently, loading the state
- * while the dialog box has already populated its values, does not modify the contents of
- * any editor, so the text will not match, causing this test to fail.
- */
- PromptResult result = prompt(PARAM_INT);
- SaveState state = new SaveState();
- state.putString("some_int,java.math.BigInteger", "0x11");
- result.h.loadState(state);
- result.h.invoke();
-
- Map> args = waitOn(result.args);
- assertEquals(Map.of("some_int", intVal(17, "0x11")), args);
- }
-
@Test
public void testBoolDefaultValue() throws Throwable {
PromptResult result = prompt(PARAM_BOOL);
diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/headless/MyHeadlessGraphicsEnvironment.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/headless/MyHeadlessGraphicsEnvironment.java
index 6424cf94ab..60770e9392 100644
--- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/headless/MyHeadlessGraphicsEnvironment.java
+++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/headless/MyHeadlessGraphicsEnvironment.java
@@ -1,13 +1,12 @@
/* ###
* IP: GHIDRA
- * REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,8 +17,10 @@ package ghidra.app.util.headless;
import java.awt.*;
import java.awt.image.BufferedImage;
+import java.lang.reflect.InvocationTargetException;
import java.util.Locale;
+import ghidra.util.classfinder.ClassSearcher;
import sun.java2d.HeadlessGraphicsEnvironment;
public class MyHeadlessGraphicsEnvironment extends GraphicsEnvironment {
@@ -78,17 +79,23 @@ public class MyHeadlessGraphicsEnvironment extends GraphicsEnvironment {
private void getRealGraphicsEnvironemnt() {
try {
- localEnv = (GraphicsEnvironment) Class.forName(preferredGraphicsEnv).newInstance();
+ localEnv = ClassSearcher
+ .forNameSafe(preferredGraphicsEnv, GraphicsEnvironment.class,
+ getClass().getClassLoader())
+ .getConstructor()
+ .newInstance();
if (isHeadless()) {
localEnv = new HeadlessGraphicsEnvironment(localEnv);
}
- } catch (ClassNotFoundException e) {
+ }
+ catch (ClassNotFoundException e) {
throw new Error("Could not find class: " + preferredGraphicsEnv);
- } catch (InstantiationException e) {
+ }
+ catch (InstantiationException | NoSuchMethodException | InvocationTargetException e) {
throw new Error("Could not instantiate Graphics Environment: " + preferredGraphicsEnv);
- } catch (IllegalAccessException e) {
+ }
+ catch (IllegalAccessException e) {
throw new Error("Could not access Graphics Environment: " + preferredGraphicsEnv);
}
}
-
}
diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/headless/MyHeadlessToolkit.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/headless/MyHeadlessToolkit.java
index 98d081f7e9..00eb0295f6 100644
--- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/headless/MyHeadlessToolkit.java
+++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/app/util/headless/MyHeadlessToolkit.java
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -22,10 +22,12 @@ import java.awt.datatransfer.Clipboard;
import java.awt.font.TextAttribute;
import java.awt.im.InputMethodHighlight;
import java.awt.image.*;
+import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Map;
import java.util.Properties;
+import ghidra.util.classfinder.ClassSearcher;
import sun.awt.HeadlessToolkit;
public class MyHeadlessToolkit extends Toolkit {
@@ -163,22 +165,23 @@ public class MyHeadlessToolkit extends Toolkit {
private void getRealToolkit() {
- Class> cls = null;
+ Class extends Toolkit> cls = null;
try {
try {
- cls = Class.forName(preferredToolkit);
+ cls = ClassSearcher.forNameSafe(preferredToolkit, Toolkit.class,
+ getClass().getClassLoader());
}
catch (ClassNotFoundException ee) {
throw new AWTError("Toolkit not found: " + preferredToolkit);
}
if (cls != null) {
- localToolKit = (Toolkit) cls.newInstance();
+ localToolKit = cls.getConstructor().newInstance();
if (GraphicsEnvironment.isHeadless()) {
localToolKit = new HeadlessToolkit(localToolKit);
}
}
}
- catch (InstantiationException e) {
+ catch (InstantiationException | NoSuchMethodException | InvocationTargetException e) {
throw new AWTError("Could not instantiate Toolkit: " + preferredToolkit);
}
catch (IllegalAccessException e) {