mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-4298: Fix Trace RMI argument validation.
This commit is contained in:
parent
1bf2e3b151
commit
b5ea1540c3
5 changed files with 237 additions and 13 deletions
|
@ -106,23 +106,28 @@ public interface RemoteMethod {
|
||||||
* primitive. We instead need {@link TraceObject}. I'd add the method to the schema, except that
|
* primitive. We instead need {@link TraceObject}. I'd add the method to the schema, except that
|
||||||
* trace stuff is not in its dependencies.
|
* trace stuff is not in its dependencies.
|
||||||
*
|
*
|
||||||
* @param name the name of the parameter
|
* @param paramName the name of the parameter
|
||||||
|
* @param schName the name of the parameter's schema
|
||||||
* @param sch the type of the parameter
|
* @param sch the type of the parameter
|
||||||
* @param arg the argument
|
* @param arg the argument
|
||||||
*/
|
*/
|
||||||
static void checkType(String name, TargetObjectSchema sch, Object arg) {
|
static void checkType(String paramName, SchemaName schName, TargetObjectSchema sch,
|
||||||
if (sch.getType() != TargetObject.class) {
|
Object arg) {
|
||||||
if (sch.getType().isInstance(arg)) {
|
// if sch is null, it was definitely an object-type schema without context
|
||||||
return;
|
if (sch != null) {
|
||||||
|
if (sch.getType() != TargetObject.class) {
|
||||||
|
if (sch.getType().isInstance(arg)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (arg instanceof TraceObject obj) {
|
||||||
else if (arg instanceof TraceObject obj) {
|
if (sch.isAssignableFrom(obj.getTargetSchema())) {
|
||||||
if (sch.equals(obj.getTargetSchema())) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"For parameter %s: argument %s is not a %s".formatted(name, arg, sch));
|
"For parameter %s: argument %s is not a %s".formatted(paramName, arg, schName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -159,8 +164,9 @@ public interface RemoteMethod {
|
||||||
"All TraceObject parameters must come from the same trace");
|
"All TraceObject parameters must come from the same trace");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetObjectSchema sch = ctx.getSchema(ent.getValue().type());
|
SchemaName schName = ent.getValue().type();
|
||||||
checkType(ent.getKey(), sch, arg);
|
TargetObjectSchema sch = ctx.getSchemaOrNull(schName);
|
||||||
|
checkType(ent.getKey(), schName, sch, arg);
|
||||||
}
|
}
|
||||||
for (Map.Entry<String, Object> ent : arguments.entrySet()) {
|
for (Map.Entry<String, Object> ent : arguments.entrySet()) {
|
||||||
if (!parameters().containsKey(ent.getKey())) {
|
if (!parameters().containsKey(ent.getKey())) {
|
||||||
|
@ -198,6 +204,7 @@ public interface RemoteMethod {
|
||||||
*
|
*
|
||||||
* @param arguments the keyword arguments to the remote method
|
* @param arguments the keyword arguments to the remote method
|
||||||
* @throws IllegalArgumentException if the arguments are not valid
|
* @throws IllegalArgumentException if the arguments are not valid
|
||||||
|
* @return the returned value
|
||||||
*/
|
*/
|
||||||
default Object invoke(Map<String, Object> arguments) {
|
default Object invoke(Map<String, Object> arguments) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -0,0 +1,190 @@
|
||||||
|
/* ###
|
||||||
|
* 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.app.plugin.core.debug.service.tracermi;
|
||||||
|
|
||||||
|
import static ghidra.app.plugin.core.debug.gui.model.DebuggerModelProviderTest.CTX;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import db.Transaction;
|
||||||
|
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
|
||||||
|
import ghidra.app.plugin.core.debug.service.tracermi.TestTraceRmiConnection.TestRemoteMethod;
|
||||||
|
import ghidra.app.plugin.core.debug.service.tracermi.TestTraceRmiConnection.TestRemoteParameter;
|
||||||
|
import ghidra.dbg.target.schema.EnumerableTargetObjectSchema;
|
||||||
|
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
|
||||||
|
import ghidra.debug.api.target.ActionName;
|
||||||
|
import ghidra.debug.api.tracermi.RemoteMethod;
|
||||||
|
import ghidra.trace.model.Lifespan;
|
||||||
|
import ghidra.trace.model.target.*;
|
||||||
|
import ghidra.trace.model.target.TraceObject.ConflictResolution;
|
||||||
|
|
||||||
|
public class RemoteMethodTest extends AbstractGhidraHeadedDebuggerTest {
|
||||||
|
@Test
|
||||||
|
public void testRemoteMethodValidateObjectGivenObject() throws Throwable {
|
||||||
|
RemoteMethod method = new TestRemoteMethod("test", ActionName.name("test"), "Test",
|
||||||
|
"A test method", EnumerableTargetObjectSchema.VOID.getName(),
|
||||||
|
new TestRemoteParameter("obj", EnumerableTargetObjectSchema.OBJECT.getName(), true,
|
||||||
|
null, "Arg1", "An argument"));
|
||||||
|
|
||||||
|
createTrace();
|
||||||
|
|
||||||
|
TraceObject root;
|
||||||
|
try (Transaction tx = tb.startTransaction()) {
|
||||||
|
TraceObjectValue rv = tb.trace.getObjectManager()
|
||||||
|
.createRootObject(CTX.getSchema(new SchemaName("Session")));
|
||||||
|
root = rv.getChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
method.validate(Map.of("obj", root));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoteMethodValidateObjectGivenProcess() throws Throwable {
|
||||||
|
RemoteMethod method = new TestRemoteMethod("test", ActionName.name("test"), "Test",
|
||||||
|
"A test method", EnumerableTargetObjectSchema.VOID.getName(),
|
||||||
|
new TestRemoteParameter("obj", EnumerableTargetObjectSchema.OBJECT.getName(), true,
|
||||||
|
null, "Arg1", "An argument"));
|
||||||
|
|
||||||
|
createTrace();
|
||||||
|
|
||||||
|
TraceObject process;
|
||||||
|
try (Transaction tx = tb.startTransaction()) {
|
||||||
|
tb.trace.getObjectManager().createRootObject(CTX.getSchema(new SchemaName("Session")));
|
||||||
|
process =
|
||||||
|
tb.trace.getObjectManager().createObject(TraceObjectKeyPath.parse("Processes[0]"));
|
||||||
|
process.insert(Lifespan.nowOn(0), ConflictResolution.DENY);
|
||||||
|
}
|
||||||
|
|
||||||
|
method.validate(Map.of("obj", process));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testRemoteMethodValidateObjectGivenInt() throws Throwable {
|
||||||
|
RemoteMethod method = new TestRemoteMethod("test", ActionName.name("test"), "Test",
|
||||||
|
"A test method", EnumerableTargetObjectSchema.VOID.getName(),
|
||||||
|
new TestRemoteParameter("obj", EnumerableTargetObjectSchema.OBJECT.getName(), true,
|
||||||
|
null, "Arg1", "An argument"));
|
||||||
|
|
||||||
|
method.validate(Map.of("obj", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoteMethodValidateProcessGivenProcess() throws Throwable {
|
||||||
|
RemoteMethod method = new TestRemoteMethod("test", ActionName.name("test"), "Test",
|
||||||
|
"A test method", EnumerableTargetObjectSchema.VOID.getName(),
|
||||||
|
new TestRemoteParameter("proc", new SchemaName("Process"), true,
|
||||||
|
null, "Proc1", "A Process argument"));
|
||||||
|
|
||||||
|
createTrace();
|
||||||
|
|
||||||
|
TraceObject process;
|
||||||
|
try (Transaction tx = tb.startTransaction()) {
|
||||||
|
tb.trace.getObjectManager().createRootObject(CTX.getSchema(new SchemaName("Session")));
|
||||||
|
process =
|
||||||
|
tb.trace.getObjectManager().createObject(TraceObjectKeyPath.parse("Processes[0]"));
|
||||||
|
process.insert(Lifespan.nowOn(0), ConflictResolution.DENY);
|
||||||
|
}
|
||||||
|
|
||||||
|
method.validate(Map.of("proc", process));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testRemoteMethodValidateProcessGivenInt() throws Throwable {
|
||||||
|
RemoteMethod method = new TestRemoteMethod("test", ActionName.name("test"), "Test",
|
||||||
|
"A test method", EnumerableTargetObjectSchema.VOID.getName(),
|
||||||
|
new TestRemoteParameter("proc", new SchemaName("Process"), true,
|
||||||
|
null, "Proc1", "A Process argument"));
|
||||||
|
|
||||||
|
// Otherwise "Process" schema doesn't exist
|
||||||
|
createTrace();
|
||||||
|
try (Transaction tx = tb.startTransaction()) {
|
||||||
|
tb.trace.getObjectManager().createRootObject(CTX.getSchema(new SchemaName("Session")));
|
||||||
|
}
|
||||||
|
|
||||||
|
method.validate(Map.of("proc", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoteMethodValidateAnyGivenInteger() throws Throwable {
|
||||||
|
RemoteMethod method = new TestRemoteMethod("test", ActionName.name("test"), "Test",
|
||||||
|
"A test method", EnumerableTargetObjectSchema.VOID.getName(),
|
||||||
|
new TestRemoteParameter("arg", EnumerableTargetObjectSchema.ANY.getName(), true,
|
||||||
|
null, "Arg1", "An argument"));
|
||||||
|
|
||||||
|
method.validate(Map.of("arg", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoteMethodValidateAnyGivenObject() throws Throwable {
|
||||||
|
RemoteMethod method = new TestRemoteMethod("test", ActionName.name("test"), "Test",
|
||||||
|
"A test method", EnumerableTargetObjectSchema.VOID.getName(),
|
||||||
|
new TestRemoteParameter("arg", EnumerableTargetObjectSchema.ANY.getName(), true,
|
||||||
|
null, "Arg1", "An argument"));
|
||||||
|
|
||||||
|
createTrace();
|
||||||
|
|
||||||
|
TraceObject root;
|
||||||
|
try (Transaction tx = tb.startTransaction()) {
|
||||||
|
TraceObjectValue rv = tb.trace.getObjectManager()
|
||||||
|
.createRootObject(CTX.getSchema(new SchemaName("Session")));
|
||||||
|
root = rv.getChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
method.validate(Map.of("arg", root));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoteMethodValidateAnyGivenProcess() throws Throwable {
|
||||||
|
RemoteMethod method = new TestRemoteMethod("test", ActionName.name("test"), "Test",
|
||||||
|
"A test method", EnumerableTargetObjectSchema.VOID.getName(),
|
||||||
|
new TestRemoteParameter("arg", EnumerableTargetObjectSchema.ANY.getName(), true,
|
||||||
|
null, "Arg1", "An argument"));
|
||||||
|
|
||||||
|
createTrace();
|
||||||
|
|
||||||
|
TraceObject process;
|
||||||
|
try (Transaction tx = tb.startTransaction()) {
|
||||||
|
tb.trace.getObjectManager().createRootObject(CTX.getSchema(new SchemaName("Session")));
|
||||||
|
process =
|
||||||
|
tb.trace.getObjectManager().createObject(TraceObjectKeyPath.parse("Processes[0]"));
|
||||||
|
process.insert(Lifespan.nowOn(0), ConflictResolution.DENY);
|
||||||
|
}
|
||||||
|
|
||||||
|
method.validate(Map.of("arg", process));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoteMethodValidateIntegerGivenInteger() throws Throwable {
|
||||||
|
RemoteMethod method = new TestRemoteMethod("test", ActionName.name("test"), "Test",
|
||||||
|
"A test method", EnumerableTargetObjectSchema.VOID.getName(),
|
||||||
|
new TestRemoteParameter("arg", EnumerableTargetObjectSchema.INT.getName(), true,
|
||||||
|
null, "Arg1", "An argument"));
|
||||||
|
|
||||||
|
method.validate(Map.of("arg", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testRemoteMethodValidateIntegerGivenLong() throws Throwable {
|
||||||
|
RemoteMethod method = new TestRemoteMethod("test", ActionName.name("test"), "Test",
|
||||||
|
"A test method", EnumerableTargetObjectSchema.VOID.getName(),
|
||||||
|
new TestRemoteParameter("arg", EnumerableTargetObjectSchema.INT.getName(), true,
|
||||||
|
null, "Arg1", "An argument"));
|
||||||
|
|
||||||
|
method.validate(Map.of("arg", 1L));
|
||||||
|
}
|
||||||
|
}
|
|
@ -51,7 +51,7 @@ import ghidra.trace.model.thread.TraceThread;
|
||||||
|
|
||||||
public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerTest {
|
public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerTest {
|
||||||
|
|
||||||
protected static final SchemaContext CTX;
|
public static final SchemaContext CTX;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -46,6 +46,11 @@ public enum EnumerableTargetObjectSchema implements TargetObjectSchema {
|
||||||
public AttributeSchema getDefaultAttributeSchema() {
|
public AttributeSchema getDefaultAttributeSchema() {
|
||||||
return AttributeSchema.DEFAULT_ANY;
|
return AttributeSchema.DEFAULT_ANY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAssignableFrom(TargetObjectSchema that) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* The least restrictive, but least informative object schema.
|
* The least restrictive, but least informative object schema.
|
||||||
|
@ -63,6 +68,12 @@ public enum EnumerableTargetObjectSchema implements TargetObjectSchema {
|
||||||
public AttributeSchema getDefaultAttributeSchema() {
|
public AttributeSchema getDefaultAttributeSchema() {
|
||||||
return AttributeSchema.DEFAULT_ANY;
|
return AttributeSchema.DEFAULT_ANY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAssignableFrom(TargetObjectSchema that) {
|
||||||
|
// That is has as schema implies it's a TargetObject
|
||||||
|
return true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
TYPE(Class.class),
|
TYPE(Class.class),
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1233,4 +1233,20 @@ public interface TargetObjectSchema {
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("No index between stack and frame");
|
throw new IllegalArgumentException("No index between stack and frame");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this schema can accept a value of the given other schema
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This works analogously to {@link Class#isAssignableFrom(Class)}, except that schemas are
|
||||||
|
* quite a bit less flexible. Only {@link EnumerableTargetObjectSchema#ANY} and
|
||||||
|
* {@link EnumerableTargetObjectSchema#OBJECT} can accept anything other than exactly
|
||||||
|
* themselves.
|
||||||
|
*
|
||||||
|
* @param that
|
||||||
|
* @return true if an object of that schema can be assigned to this schema.
|
||||||
|
*/
|
||||||
|
default boolean isAssignableFrom(TargetObjectSchema that) {
|
||||||
|
return this.equals(that);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue