classrecovery: avoid AddressOutOfBoundsException on non-pointer p-code constants

Add a best-effort tryToAddr helper that catches AddressOutOfBoundsException and
filters unmapped addresses, and use it from getAssignedAddressFromPcode() to
prevent RecoverClassesFromRTTIScript from aborting on invalid address candidates.
This commit is contained in:
Abramov Evgeniy
2026-01-14 03:18:04 +03:00
committed by ghidra007
parent e31df74ca4
commit f16f7b650e
@@ -20,6 +20,8 @@ import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.ParameterDefinition;
import ghidra.program.model.listing.Function;
@@ -226,42 +228,32 @@ public class DecompilerScriptUtils {
decompInterface.dispose();
}
/**
* Best-effort extraction of an address candidate from decompiler p-code.
* Returns {@code null} if the candidate is invalid or not mapped in program memory.
*/
public Address getAssignedAddressFromPcode(Varnode storedValue) {
long addressOffset;
if (storedValue.isConstant()) {
addressOffset = storedValue.getOffset();
Address possibleAddress = toAddr(addressOffset);
if (possibleAddress == null || !program.getMemory().contains(possibleAddress)) {
return null;
}
return possibleAddress;
return tryToAddr(storedValue.getOffset());
}
PcodeOp valuePcodeOp = storedValue.getDef();
if (valuePcodeOp == null) {
PcodeOp op = storedValue.getDef();
if (op == null) {
return null;
}
if (valuePcodeOp.getOpcode() == PcodeOp.CAST || valuePcodeOp.getOpcode() == PcodeOp.COPY) {
Varnode constantVarnode = valuePcodeOp.getInput(0);
return getAssignedAddressFromPcode(constantVarnode);
int opcode = op.getOpcode();
if (opcode == PcodeOp.CAST || opcode == PcodeOp.COPY) {
return getAssignedAddressFromPcode(op.getInput(0));
}
else if (valuePcodeOp.getOpcode() != PcodeOp.PTRSUB) {
if (opcode != PcodeOp.PTRSUB) {
return null;
}
// don't need to check isConst bc always is
Varnode constantVarnode = valuePcodeOp.getInput(1);
addressOffset = constantVarnode.getOffset();
Address possibleAddress = toAddr(addressOffset);
if (possibleAddress == null || !program.getMemory().contains(possibleAddress)) {
return null;
}
return possibleAddress;
// PTRSUB input(1) is always a constant offset (but may not represent a valid program address)
return tryToAddr(op.getInput(1).getOffset());
}
/**
@@ -302,4 +294,29 @@ public class DecompilerScriptUtils {
return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
}
/**
* Attempts to convert the given offset to an {@link Address} in the default address space.
* <p>
* Decompiler pcode may surface non-pointer constants that heuristics attempt to interpret
* as addresses; this method performs a best-effort conversion and safely ignores
* invalid or unmapped candidates.
* <p>
* Unlike {@link #toAddr(long)}, this method returns {@code null} if the resulting address is not
* contained in the program's memory or if the offset is out of bounds.
*
* @param offset the offset to convert
* @return the address for the given offset, or {@code null} if the offset cannot be represented
* as a valid address in the program's memory
*/
public final Address tryToAddr(long offset) {
AddressSpace space = program.getAddressFactory().getDefaultAddressSpace();
try {
Address addr = space.getAddress(offset);
return program.getMemory().contains(addr) ? addr : null;
}
catch (AddressOutOfBoundsException e) {
return null;
}
}
}