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/Base/src/main/java/ghidra/app/nav/LocationMemento.java b/Ghidra/Features/Base/src/main/java/ghidra/app/nav/LocationMemento.java index 7b0c9210e7..b61599477f 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 @@ -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,15 +15,16 @@ */ package ghidra.app.nav; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + import ghidra.framework.options.SaveState; import ghidra.program.model.listing.Program; import ghidra.program.util.AddressFieldLocation; import ghidra.program.util.ProgramLocation; import ghidra.util.Msg; import ghidra.util.SystemUtilities; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; +import ghidra.util.classfinder.ClassSearcher; public class LocationMemento { private static final String PROGRAM_PATH = "PROGRAM_PATH_"; @@ -151,8 +152,8 @@ public class LocationMemento { } try { - Class mementoClass = - (Class) Class.forName(className); + Class mementoClass = ClassSearcher.forNameSafe(className, + LocationMemento.class, LocationMemento.class.getClassLoader()); Constructor constructor = mementoClass.getConstructor(SaveState.class, Program[].class); 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 dd5a9aff76..9fa1cf111d 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 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 clazz = - (Class) Class.forName(className); + Class 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 pluginClazz = (Class) clazz; - return loadPlugin(pluginClazz); + Class 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 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 c = ClassSearcher.forNameSafe( + classname, EmulateInstructionStateModifier.class, getClass().getClassLoader()); + Constructor 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 instructionStateModifierClass = - (Class) c; + Class c = ClassSearcher.forNameSafe( + classname, EmulateInstructionStateModifier.class, getClass().getClassLoader()); Constructor 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 0b2acfc995..6f141cf066 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; @@ -298,7 +299,7 @@ public class GProperties { } } else if (tag.equals(GPROPERTIES_TAG)) { - Element element = (Element) elem.getChildren().get(0); + Element element = elem.getChildren().get(0); if (element != null) { map.put(name, new GProperties(element)); } @@ -436,7 +437,13 @@ public class GProperties { Enum getEnumValue(String enumClassName, String value) { try { - Class enumClass = Class.forName(enumClassName).asSubclass(Enum.class); + // 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()); Method m = enumClass.getMethod("valueOf", new Class[] { String.class }); if (m != null) { @@ -821,7 +828,7 @@ public class GProperties { protected Element createElementFromElement(String internalKey, Element internalElement) { Element newElement = createElement("XML", internalKey); - Element internalElementClone = (Element) internalElement.clone(); + Element internalElementClone = internalElement.clone(); newElement.addContent(internalElementClone); return newElement; 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 826b8f12c8..6615aa2e35 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 @@ -182,7 +182,7 @@ public class ClassSearcher { Class c = loadedCache.get(info); if (c == null) { - c = loadExtensionPoint(info.path(), info.name()); + c = loadExtensionPoint(info.path(), ancestorClass, info.name()); if (c == null) { falsePositiveCache.add(info); continue; @@ -272,6 +272,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 forNameSafe(String name, Class sup, ClassLoader loader) + throws ClassNotFoundException { + Class cls = Class.forName(name, false, loader); + Class 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. @@ -626,11 +662,12 @@ 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
@@ -640,7 +677,8 @@ public class ClassSearcher {
 	 * @param className the fully qualified class name
 	 * @return the class if it is an extension point
 	 */
-	private static Class loadExtensionPoint(String path, String className) {
+	private static  Class loadExtensionPoint(String path, Class ancestorClass,
+			String className) {
 
 		if (getExtensionPointSuffix(className) == null) {
 			return null;
@@ -649,7 +687,7 @@ public class ClassSearcher {
 		ClassLoader classLoader = getClassLoader(path);
 
 		try {
-			Class c = Class.forName(className, true, classLoader);
+			Class c = forNameSafe(className, ancestorClass, classLoader);
 			if (isClassOfInterest(c)) {
 				return c;
 			}
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 found = ClassSearcher.forNameSafe(
+			"ghidra.util.classfinder.ClassSearcherTest$ClassWithStaticInitializerSideEffects",
+			TestSuper.class, getClass().getClassLoader());
+		assertNotNull(found);
+		assertEquals(Canary.ALIVE, canary);
+		Constructor 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 6c8f84d556..e7ee4395a9 100644
--- a/Ghidra/Framework/Gui/src/main/java/generic/theme/ThemePreferences.java
+++ b/Ghidra/Framework/Gui/src/main/java/generic/theme/ThemePreferences.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.
@@ -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,8 +48,9 @@ public class ThemePreferences {
 		else if (themeId.startsWith(DiscoverableGTheme.CLASS_PREFIX)) {
 			String className = themeId.substring(DiscoverableGTheme.CLASS_PREFIX.length());
 			try {
-				Class forName = Class.forName(className);
-				return (GTheme) forName.getDeclaredConstructor().newInstance();
+				Class forName =
+					ClassSearcher.forNameSafe(className, GTheme.class, getClass().getClassLoader());
+				return forName.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 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 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 c =
+				ClassSearcher.forNameSafe(element.getAttributeValue(CLASS_ATTRIBUTE),
+					WrappedOption.class, getClass().getClassLoader());
+			Constructor 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 79e982438d..f775772871 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 {
 
@@ -36,8 +37,9 @@ public class WrappedCustomOption implements WrappedOption {
 		String customOptionClassName = saveState.getString("CUSTOM OPTION CLASS", null);
 		valid = false;
 		try {
-			Class c = Class.forName(customOptionClassName);
-			value = (CustomOption) c.getConstructor().newInstance();
+			Class c = ClassSearcher.forNameSafe(customOptionClassName,
+				CustomOption.class, getClass().getClassLoader());
+			value = c.getConstructor().newInstance();
 			value.readState(saveState);
 			valid = true;
 		}
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 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 8c1b6395c6..1a76a064aa 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
@@ -134,7 +134,8 @@ public class GhidraToolTemplate implements ToolTemplate {
 			Element elem = (Element) list.get(i);
 			String className = elem.getAttribute(CLASS_NAME_XML_NAME).getValue();
 			try {
-				dtList.add(Class.forName(className));
+				dtList.add(ClassSearcher
+						.forNameSafe(className, DomainObject.class, getClass().getClassLoader()));
 			}
 			catch (ClassNotFoundException e) {
 				Msg.warn(this, "Tool supported content class not found: " + className);
@@ -181,7 +182,7 @@ public class GhidraToolTemplate implements ToolTemplate {
 		}
 		root.addContent(iconElem);
 
-		root.addContent((Element) (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 812a851428..378be59b64 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
@@ -47,6 +47,7 @@ import ghidra.program.model.util.ProcessorSymbolType;
 import ghidra.sleigh.grammar.SleighPreprocessor;
 import ghidra.sleigh.grammar.SourceFileIndexer;
 import ghidra.util.*;
+import ghidra.util.classfinder.ClassSearcher;
 import ghidra.util.exception.InvalidInputException;
 import ghidra.util.task.TaskMonitor;
 import ghidra.util.xml.SpecXmlUtils;
@@ -1539,18 +1540,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 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 0c55620640..492c21d808 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
@@ -50,6 +50,7 @@ import ghidra.program.model.data.Enum;
 import ghidra.program.model.lang.*;
 import ghidra.program.model.listing.Function;
 import ghidra.util.*;
+import ghidra.util.classfinder.ClassSearcher;
 import ghidra.util.classfinder.ClassTranslator;
 import ghidra.util.datastruct.FixedSizeHashMap;
 import ghidra.util.exception.*;
@@ -2909,9 +2910,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 c;
+				Class c;
 				try {
-					c = Class.forName(classPath);
+					c = ClassSearcher.forNameSafe(classPath, BuiltInDataType.class,
+						getClass().getClassLoader());
 				}
 				catch (ClassNotFoundException | NoClassDefFoundError e) {
 					// Check the classNameMap.
@@ -2920,14 +2922,15 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
 						throw e;
 					}
 					try {
-						c = Class.forName(newClassPath);
+						c = ClassSearcher.forNameSafe(newClassPath, BuiltInDataType.class,
+							getClass().getClassLoader());
 					}
 					catch (ClassNotFoundException e1) {
 						throw e1;
 					}
 				}
 
-				BuiltInDataType bdt = (BuiltInDataType) c.getDeclaredConstructor().newInstance();
+				BuiltInDataType bdt = c.getDeclaredConstructor().newInstance();
 				bdt.setName(name);
 				bdt.setCategoryPath(catPath);
 
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 24bf94bbd9..27b233725d 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
@@ -27,6 +27,7 @@ import ghidra.program.model.util.ObjectPropertyMap;
 import ghidra.program.util.ChangeManager;
 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;
@@ -132,9 +133,10 @@ public class ObjectPropertyMapDB extends PropertyMapDB
 	 */
 	@SuppressWarnings("unchecked")
 	public static Class getSaveableClassForName(String classPath) {
-		Class c = null;
+		Class c = null;
 		try {
-			c = Class.forName(classPath);
+			c = ClassSearcher.forNameSafe(classPath, Saveable.class,
+				ObjectPropertyMapDB.class.getClassLoader());
 		}
 		catch (ClassNotFoundException e) {
 			// Check the classNameMap.
@@ -142,10 +144,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.
 				}
 			}
@@ -158,7 +160,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) 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 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) disassemblerClass;
+			Class 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 injectLibraryClass =
-					(Class) c;
+				Class c = ClassSearcher.forNameSafe(classname,
+					PcodeInjectLibrary.class, getClass().getClassLoader());
 				Constructor 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 b4cfcfe7dd..704201f2e1 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
@@ -279,7 +280,8 @@ public class ProgramLocation implements Cloneable, Comparable {
 		}
 
 		try {
-			Class locationClass = Class.forName(className);
+			Class locationClass = ClassSearcher.forNameSafe(className,
+				ProgramLocation.class, ProgramLocation.class.getClassLoader());
 			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
@@ -288,7 +290,7 @@ public class ProgramLocation implements Cloneable, Comparable {
 				return null;
 			}
 
-			ProgramLocation loc = (ProgramLocation) locationClass.getConstructor().newInstance();
+			ProgramLocation loc = locationClass.getConstructor().newInstance();
 			loc.restoreState(program, saveState);
 			if (loc.getAddress() != null) {
 				return loc;
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 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 clazz =
+				ClassSearcher.forNameSafe(className, LanguagePostUpgradeInstructionHandler.class,
+					SimpleLanguageTranslator.class.getClassLoader());
 			getPostUpgradeInstructionHandler((Program) null, clazz); // test construction
-			return (Class) 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 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) {