Merge remote-tracking branch 'origin/patch'

This commit is contained in:
ghidra1
2026-05-27 16:13:13 -04:00
38 changed files with 341 additions and 413 deletions
@@ -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
@@ -52,7 +52,6 @@ import ghidra.util.layout.PairLayout;
public abstract class AbstractDebuggerParameterDialog<P> 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<P> 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<P, PropertyEditor> paramEditors = new DualLinkedHashBidiMap<>();
@@ -722,46 +707,6 @@ public abstract class AbstractDebuggerParameterDialog<P> extends DialogComponent
new ValStr<>(editor.getValue(), editor.getAsText()));
}
public void writeConfigState(SaveState saveState) {
SaveState subState = new SaveState();
for (Map.Entry<NameTypePair, ValStr<?>> 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());
@@ -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<P, D extends AbstractDebuggerParameterDialog<P>> {
public static <P, D extends AbstractDebuggerParameterDialog<P>> InvocationDialogHelper<P, D> waitFor(
Class<D> cls) {
public static <P, D extends AbstractDebuggerParameterDialog<P>> InvocationDialogHelper<P, D>
waitFor(Class<D> cls) {
D dialog = AbstractDockingTest.waitForDialogComponent(cls);
return new InvocationDialogHelper<>(dialog);
}
@@ -86,16 +85,4 @@ public class InvocationDialogHelper<P, D extends AbstractDebuggerParameterDialog
public void invoke() {
runSwing(() -> 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));
}
}
@@ -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;
}
@@ -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<File> getConfigurationTemplates() {
List<File> 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 <dbconfig>} 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.
@@ -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);
@@ -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<ExecutableRecord> {
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<ExecutableRecord> {
public List<CategoryRecord> 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<ExecutableRecord> {
/**
* 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<ExecutableRecord> {
* @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<ExecutableRecord> {
* @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<CategoryRecord> uc, RowKey id, String repo, String pth) {
checkValidMD5(md5);
md5sum = md5;
executableName = enm;
architecture = arc;
@@ -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();
@@ -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) {
@@ -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);
}
@@ -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();
@@ -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) {
@@ -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 {
@@ -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<T> extends DefaultPcodeThread<T> {
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 " +
@@ -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) {
@@ -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) {
@@ -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 });
}
@@ -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;
}
/**
@@ -274,6 +274,42 @@ public class ClassSearcher {
}
/**
* Get the named class in a safe(r) manner.
* <p>
* 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.
* <p>
* The pattern to "mitigate" this is to ensure the found class implements a given interface or
* extends from a given class, <em>before</em> invoking a constructor. Ideally, this check can
* be applied before <em>class initialization</em>. While much narrower, static initializers may
* also provide gadgets. This method implements the pattern which avoids class initialization
* until the type has been checked.
* <p>
* <b>WARNING:</b> 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 <T> 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 <T> Class<? extends T> forNameSafe(String name, Class<T> 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}.
* <p>
* Examples:
* Examples:
* <pre>
* /foo/bar/baz/file.jar fully.qualified.ClassName
* /foo/bar/baz/bin fully.qualified.ClassName
@@ -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);
}
}
@@ -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",
@@ -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;
}
@@ -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;
}
@@ -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;
@@ -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);
}
}
@@ -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
@@ -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;
@@ -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;
}
@@ -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 " +
@@ -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. <br>
@@ -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();
@@ -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<T extends Saveable> extends PropertyMapDB<T>
*/
@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<T extends Saveable> extends PropertyMapDB<T>
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<T extends Saveable> extends PropertyMapDB<T>
"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;
}
/**
@@ -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;
}
}
}
@@ -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) {
@@ -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;
/**
* <CODE>ProgramLocation</CODE> provides information about a location in a program in the most
@@ -278,10 +279,10 @@ public class ProgramLocation implements Cloneable, Comparable<ProgramLocation> {
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<ProgramLocation> {
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<ProgramLocation> {
}
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",
@@ -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) {
@@ -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 <em>will not</em> 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<String, ValStr<?>> args = waitOn(result.args);
assertEquals(Map.of("some_int", intVal(17, "0x11")), args);
}
@Test
public void testBoolDefaultValue() throws Throwable {
PromptResult result = prompt(PARAM_BOOL);
@@ -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);
}
}
}
@@ -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) {