diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceEmulationIntegration.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceEmulationIntegration.java index 7c9c4dc53a..b13dce45e4 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceEmulationIntegration.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/TraceEmulationIntegration.java @@ -761,6 +761,9 @@ public enum TraceEmulationIntegration { value)) { return; } + if (length == 0) { + return; + } Address end = address.addWrap(length - 1); if (address.compareTo(end) <= 0) { written.add(address, end); diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java index 55adbeaf23..07de6a410d 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java @@ -32,6 +32,7 @@ import ghidra.pcode.emu.PcodeEmulator; import ghidra.pcode.emu.PcodeThread; import ghidra.pcode.exec.*; import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; +import ghidra.pcode.exec.trace.TraceEmulationIntegration.TraceWriter; import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressRangeImpl; @@ -55,6 +56,74 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest return new PcodeEmulator(platform.getLanguage(), writer.callbacks()); } + @Test + public void testSetVarSize0() throws Throwable { + try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { + initTrace(tb, """ + RIP = 0x00400000; + """, + List.of()); + + Writer writer = createWriter(tb.host, 0); + PcodeEmulator emu = createEmulator(tb.host, writer); + + emu.getSharedState().setVar(tb.addr(0x00400000), 0, false, tb.arr()); + TraceWriter tw = (TraceWriter) writer; + assertEquals(tb.set(), tw.memWritten); + } + } + + @Test + public void testGetVarSize0Uninit() throws Throwable { + try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { + initTrace(tb, """ + RIP = 0x00400000; + """, + List.of()); + + Writer writer = createWriter(tb.host, 0); + PcodeEmulator emu = createEmulator(tb.host, writer); + PcodeExecutorState state = emu.getSharedState(); + Address addr = tb.addr(0x00400000); + + byte[] result = state.getVar(addr, 0, false, Reason.EXECUTE_READ); + assertArrayEquals(new byte[0], result); + + assertNull(state.getNextEntryInternal(addr.getAddressSpace(), 0)); + } + } + + @Test + public void testGetVarSize0Init() throws Throwable { + try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) { + // Put some known data in a place likely to show in erroneous "uninitialized" set + initTrace(tb, """ + RIP = 0x00400000; + *:8 RIP = 0xdeadbeefcafeface; + """, + List.of()); + + Writer writer = createWriter(tb.host, 0); + PcodeEmulator emu = createEmulator(tb.host, writer); + PcodeExecutorState state = emu.getSharedState(); + Address addr = tb.addr(0x00400000); + state.setVar(addr, 4, false, tb.arr(1, 2, 3, 4)); + + var entry = state.getNextEntryInternal(addr.getAddressSpace(), 0); + assertEquals(0x00400000L, entry.getKey().longValue()); + assertArrayEquals(tb.arr(1, 2, 3, 4), entry.getValue()); + assertNull(state.getNextEntryInternal(addr.getAddressSpace(), 0x00400004)); + + byte[] result = state.getVar(addr, 0, false, Reason.EXECUTE_READ); + assertArrayEquals(tb.arr(), result); + + entry = state.getNextEntryInternal(addr.getAddressSpace(), 0); + assertEquals(0x00400000L, entry.getKey().longValue()); + assertArrayEquals(tb.arr(1, 2, 3, 4), entry.getValue()); + assertNull(state.getNextEntryInternal(addr.getAddressSpace(), 0x00400004)); + } + } + /** * Test a single instruction * diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/AbstractPcodeExecutorState.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/AbstractPcodeExecutorState.java index f56d61ef90..dfb458e619 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/AbstractPcodeExecutorState.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/AbstractPcodeExecutorState.java @@ -16,6 +16,7 @@ package ghidra.pcode.exec; import java.util.Map; +import java.util.Map.Entry; import java.util.stream.Stream; import ghidra.pcode.exec.PcodeArithmetic.Purpose; @@ -103,4 +104,9 @@ public abstract class AbstractPcodeExecutorState implements PcodeExecutorS public void clear() { piece.clear(); } + + @Override + public Entry getNextEntryInternal(AddressSpace space, long offset) { + return piece.getNextEntryInternal(space, offset); + } } diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStatePiece.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStatePiece.java index db6984f6b0..c8119aca59 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStatePiece.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStatePiece.java @@ -15,6 +15,12 @@ */ package ghidra.pcode.exec; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import generic.ULongSpan; +import generic.ULongSpan.ULongSpanSet; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.lang.Language; @@ -45,4 +51,24 @@ public class BytesPcodeExecutorStatePiece protected BytesPcodeExecutorStateSpace newSpace(AddressSpace space) { return new BytesPcodeExecutorStateSpace(language, space, this); } + + @Override + public Entry getNextEntryInternal(AddressSpace space, long offset) { + BytesPcodeExecutorStateSpace s = getForSpace(space, false); + if (s == null) { + return null; + } + ULongSpanSet initialized = s.bytes.getInitialized(0, -1); + ULongSpan span = initialized.spanContaining(offset); + if (span == null) { + var it = initialized.intersecting(ULongSpan.span(offset, -1)).iterator(); + if (!it.hasNext()) { + return null; + } + span = it.next(); + } + byte[] data = new byte[(int) span.length()]; + s.bytes.getData(span.min(), data); + return Map.entry(span.min(), data); + } } diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStateSpace.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStateSpace.java index 92c0a2785a..4462e19fbf 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStateSpace.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStateSpace.java @@ -180,6 +180,9 @@ public class BytesPcodeExecutorStateSpace { * @return the uninitialized offset ranges */ protected AddressSetView computeUninitialized(long offset, int size) { + if (size == 0) { + return new AddressSet(); + } long max = offset + size - 1; if (Long.compareUnsigned(max, space.getMaxAddress().getOffset()) <= 0 && Long.compareUnsigned(offset, max) <= 0) { diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeStateCallbacks.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeStateCallbacks.java index a1b65e064e..5a2d10e8d0 100644 --- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeStateCallbacks.java +++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeStateCallbacks.java @@ -55,6 +55,9 @@ public interface PcodeStateCallbacks { * @return the address set */ static AddressSet rngSet(AddressSpace space, long offset, int length) { + if (length == 0) { + return new AddressSet(); + } Address min = space.getAddress(offset); return new AddressSet(min, min.add(length - 1)); }