GP-1529: Interrupt emulator when decoding uninitialized memory

This commit is contained in:
Dan 2023-03-29 16:36:17 -04:00
parent 738e662e82
commit 4aa54dd1f9
25 changed files with 274 additions and 100 deletions

View file

@ -18,6 +18,7 @@ package ghidra.app.plugin.core.debug.service.emulation;
import java.util.Map;
import java.util.concurrent.*;
import generic.ULongSpan.ULongSpanSet;
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerDataAccess;
import ghidra.generic.util.datastruct.SemisparseByteArray;
import ghidra.pcode.exec.AccessPcodeExecutionException;
@ -52,28 +53,17 @@ public abstract class AbstractRWTargetPcodeExecutorStatePiece
super(language, space, backing, bytes, written);
}
protected abstract void fillUninitialized(AddressSet uninitialized);
protected abstract ULongSpanSet readUninitializedFromTarget(ULongSpanSet uninitialized);
@Override
public byte[] read(long offset, int size, Reason reason) {
if (backing != null) {
AddressSet uninitialized =
addrSet(bytes.getUninitialized(offset, offset + size - 1));
if (uninitialized.isEmpty()) {
return super.read(offset, size, reason);
}
fillUninitialized(uninitialized);
AddressSetView unknown = backing.intersectUnknown(
addrSet(bytes.getUninitialized(offset, offset + size - 1)));
if (!unknown.isEmpty() && reason == Reason.EXECUTE) {
warnUnknown(unknown);
}
protected ULongSpanSet readUninitializedFromBacking(ULongSpanSet uninitialized) {
uninitialized = readUninitializedFromTarget(uninitialized);
if (uninitialized.isEmpty()) {
return uninitialized;
}
return super.readUninitializedFromBacking(uninitialized);
// TODO: What to flush when bytes in the trace change?
return super.read(offset, size, reason);
}
protected <T> T waitTimeout(CompletableFuture<T> future) {

View file

@ -18,6 +18,8 @@ package ghidra.app.plugin.core.debug.service.emulation;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import generic.ULongSpan;
import generic.ULongSpan.ULongSpanSet;
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerDataAccess;
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerMemoryAccess;
import ghidra.generic.util.datastruct.SemisparseByteArray;
@ -81,27 +83,27 @@ public class RWTargetMemoryPcodeExecutorStatePiece
}
@Override
protected void fillUninitialized(AddressSet uninitialized) {
protected ULongSpanSet readUninitializedFromTarget(ULongSpanSet uninitialized) {
if (space.isUniqueSpace()) {
return;
return uninitialized;
}
AddressSetView unknown;
unknown = backing.intersectUnknown(uninitialized);
AddressSet addrsUninit = addrSet(uninitialized);
unknown = backing.intersectUnknown(addrsUninit);
if (unknown.isEmpty()) {
return;
return uninitialized;
}
if (waitTimeout(backing.readFromTargetMemory(unknown))) {
unknown = backing.intersectUnknown(uninitialized);
if (backing.isLive() && waitTimeout(backing.readFromTargetMemory(unknown))) {
unknown = backing.intersectUnknown(addrsUninit);
if (unknown.isEmpty()) {
return;
return uninitialized;
}
}
if (backing.readFromStaticImages(bytes, unknown)) {
unknown = backing.intersectUnknown(uninitialized);
if (unknown.isEmpty()) {
return;
}
ULongSpan bound = uninitialized.bound();
return bytes.getUninitialized(bound.min(), bound.max());
}
return uninitialized;
}
@Override

View file

@ -18,6 +18,7 @@ package ghidra.app.plugin.core.debug.service.emulation;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import generic.ULongSpan.ULongSpanSet;
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerDataAccess;
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerRegistersAccess;
import ghidra.generic.util.datastruct.SemisparseByteArray;
@ -78,15 +79,14 @@ public class RWTargetRegistersPcodeExecutorStatePiece
}
@Override
protected void fillUninitialized(AddressSet uninitialized) {
if (space.isUniqueSpace()) {
return;
protected ULongSpanSet readUninitializedFromTarget(ULongSpanSet uninitialized) {
if (space.isUniqueSpace() || !backing.isLive()) {
return uninitialized;
}
if (!backing.isLive()) {
return;
}
AddressSetView unknown = backing.intersectUnknown(uninitialized);
AddressSet addrsUninit = addrSet(uninitialized);
AddressSetView unknown = backing.intersectUnknown(addrsUninit);
waitTimeout(backing.readFromTargetRegisters(unknown));
return uninitialized;
}
@Override

View file

@ -353,7 +353,7 @@ public class UnwindAnalysis {
public SymPcodeExecutorState executeToPc(Deque<BlockEdge> to) throws CancelledException {
SymPcodeExecutorState state = new SymPcodeExecutorState(program);
SymPcodeExecutor exec =
SymPcodeExecutor.forProgram(program, state, Reason.EXECUTE, warnings, monitor);
SymPcodeExecutor.forProgram(program, state, Reason.EXECUTE_READ, warnings, monitor);
executePathTo(exec, to);
executeBlockTo(exec, pcBlock.block, pc);
return state;
@ -375,7 +375,7 @@ public class UnwindAnalysis {
public SymPcodeExecutorState executeFromPc(SymPcodeExecutorState state,
Deque<BlockEdge> from) throws CancelledException {
SymPcodeExecutor exec =
SymPcodeExecutor.forProgram(program, state, Reason.EXECUTE, warnings, monitor);
SymPcodeExecutor.forProgram(program, state, Reason.EXECUTE_READ, warnings, monitor);
executeBlockFrom(exec, pcBlock.block, pc);
executePathFrom(exec, from);
return state;

View file

@ -39,6 +39,7 @@ import ghidra.app.plugin.core.debug.service.platform.DebuggerPlatformServicePlug
import ghidra.app.services.DebuggerEmulationService.EmulationResult;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.DebuggerTraceManagerService.ActivationCause;
import ghidra.pcode.exec.DecodePcodeExecutionException;
import ghidra.pcode.exec.InterruptPcodeExecutionException;
import ghidra.pcode.utils.Utils;
import ghidra.program.model.address.Address;
@ -343,13 +344,53 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
.toString(16));
}
@Test
public void testInterruptOnDecodeUninitialized() throws Exception {
createProgram();
intoProject(program);
Assembler asm = Assemblers.getAssembler(program);
Memory memory = program.getMemory();
Address addrText = addr(program, 0x00400000);
Register regPC = program.getRegister("pc");
try (Transaction tx = program.openTransaction("Initialize")) {
MemoryBlock blockText = memory.createInitializedBlock(".text", addrText, 0x1000,
(byte) 0, TaskMonitor.DUMMY, false);
blockText.setExecute(true);
asm.assemble(addrText,
"br 0x003ffffe");
}
programManager.openProgram(program);
waitForSwing();
codeBrowser.goTo(new ProgramLocation(program, addrText));
waitForSwing();
performEnabledAction(codeBrowser.getProvider(), emulationPlugin.actionEmulateProgram, true);
Trace trace = traceManager.getCurrentTrace();
assertNotNull(trace);
TraceThread thread = Unique.assertOne(trace.getThreadManager().getAllThreads());
TraceMemorySpace regs = trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
EmulationResult result = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
TraceSchedule.snap(0), TaskMonitor.DUMMY, Scheduler.oneThread(thread));
assertEquals(TraceSchedule.parse("0:t0-1"), result.schedule());
assertTrue(result.error() instanceof DecodePcodeExecutionException);
long scratch = result.snapshot();
assertEquals(new BigInteger("003ffffe", 16), regs.getViewValue(scratch, regPC).getUnsignedValue());
}
@Test
public void testExecutionBreakpoint() throws Exception {
createProgram();
intoProject(program);
Assembler asm = Assemblers.getAssembler(program);
Memory memory = program.getMemory();
Address addrText = addr(program, 0x000400000);
Address addrText = addr(program, 0x00400000);
Register regPC = program.getRegister("pc");
Register regR0 = program.getRegister("r0");
Register regR1 = program.getRegister("r1");
@ -411,7 +452,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
intoProject(program);
Assembler asm = Assemblers.getAssembler(program);
Memory memory = program.getMemory();
Address addrText = addr(program, 0x000400000);
Address addrText = addr(program, 0x00400000);
Address addrI1;
Address addrI2;
try (Transaction tx = program.openTransaction("Initialize")) {
@ -470,7 +511,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
intoProject(program);
Assembler asm = Assemblers.getAssembler(program);
Memory memory = program.getMemory();
Address addrText = addr(program, 0x000400000);
Address addrText = addr(program, 0x00400000);
Register regPC = program.getRegister("pc");
Register regR0 = program.getRegister("r0");
Register regR1 = program.getRegister("r1");
@ -537,7 +578,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
intoProject(program);
Assembler asm = Assemblers.getAssembler(program);
Memory memory = program.getMemory();
Address addrText = addr(program, 0x000400000);
Address addrText = addr(program, 0x00400000);
Register regPC = program.getRegister("pc");
Register regR0 = program.getRegister("r0");
Register regR1 = program.getRegister("r1");