mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +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
|
||||
* 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 arg the argument
|
||||
*/
|
||||
static void checkType(String name, TargetObjectSchema sch, Object arg) {
|
||||
if (sch.getType() != TargetObject.class) {
|
||||
if (sch.getType().isInstance(arg)) {
|
||||
return;
|
||||
static void checkType(String paramName, SchemaName schName, TargetObjectSchema sch,
|
||||
Object arg) {
|
||||
// if sch is null, it was definitely an object-type schema without context
|
||||
if (sch != null) {
|
||||
if (sch.getType() != TargetObject.class) {
|
||||
if (sch.getType().isInstance(arg)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (arg instanceof TraceObject obj) {
|
||||
if (sch.equals(obj.getTargetSchema())) {
|
||||
return;
|
||||
else if (arg instanceof TraceObject obj) {
|
||||
if (sch.isAssignableFrom(obj.getTargetSchema())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
TargetObjectSchema sch = ctx.getSchema(ent.getValue().type());
|
||||
checkType(ent.getKey(), sch, arg);
|
||||
SchemaName schName = ent.getValue().type();
|
||||
TargetObjectSchema sch = ctx.getSchemaOrNull(schName);
|
||||
checkType(ent.getKey(), schName, sch, arg);
|
||||
}
|
||||
for (Map.Entry<String, Object> ent : arguments.entrySet()) {
|
||||
if (!parameters().containsKey(ent.getKey())) {
|
||||
|
@ -198,6 +204,7 @@ public interface RemoteMethod {
|
|||
*
|
||||
* @param arguments the keyword arguments to the remote method
|
||||
* @throws IllegalArgumentException if the arguments are not valid
|
||||
* @return the returned value
|
||||
*/
|
||||
default Object invoke(Map<String, Object> arguments) {
|
||||
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 {
|
||||
|
||||
protected static final SchemaContext CTX;
|
||||
public static final SchemaContext CTX;
|
||||
|
||||
static {
|
||||
try {
|
||||
|
|
|
@ -46,6 +46,11 @@ public enum EnumerableTargetObjectSchema implements TargetObjectSchema {
|
|||
public AttributeSchema getDefaultAttributeSchema() {
|
||||
return AttributeSchema.DEFAULT_ANY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssignableFrom(TargetObjectSchema that) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The least restrictive, but least informative object schema.
|
||||
|
@ -63,6 +68,12 @@ public enum EnumerableTargetObjectSchema implements TargetObjectSchema {
|
|||
public AttributeSchema getDefaultAttributeSchema() {
|
||||
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),
|
||||
/**
|
||||
|
|
|
@ -1233,4 +1233,20 @@ public interface TargetObjectSchema {
|
|||
}
|
||||
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