Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ private void ExtractMethod(MethodDefinition method, int typeId) {
// Write access flags
trap.WriteTuple("cil_method_access_flags", methodId, (int)method.Attributes);

// Write parameter type signature for overload-precise identification
var methodParamTypes = string.Join(",",
method.Parameters.Select(p => p.ParameterType.FullName.Replace('/', '.')));
trap.WriteTuple("il_method_param_signature", methodId, $"({methodParamTypes})");

if (method.HasBody) {
ExtractMethodBody(method, methodId);
}
Expand Down Expand Up @@ -182,6 +187,10 @@ private void ExtractMethodBody(MethodDefinition method, int methodId) {
var targetMethodName = $"{declaringTypeName}.{methodRef.Name}";
trap.WriteTuple("il_call_target_unresolved", instrId, targetMethodName);
trap.WriteTuple("il_number_of_arguments", instrId, methodRef.Parameters.Count);
// Emit parameter type signature for overload-precise matching
var paramTypes = string.Join(",",
methodRef.Parameters.Select(p => p.ParameterType.FullName.Replace('/', '.')));
trap.WriteTuple("il_call_target_param_signature", instrId, $"({paramTypes})");
if(methodRef.MethodReturnType.ReturnType.MetadataType is not Mono.Cecil.MetadataType.Void) {
trap.WriteTuple("il_call_has_return_value", instrId);
}
Expand Down
32 changes: 32 additions & 0 deletions binary/extractor/cil/semmlecode.binary.dbscheme
Original file line number Diff line number Diff line change
Expand Up @@ -2467,6 +2467,28 @@ il_call_target_unresolved(
string target_method_name: string ref
);

/**
* Parameter type signature for method definitions.
* The param_signature is a parenthesized, comma-separated list of fully-qualified
* parameter type names, e.g. "(System.String,System.Int32)" or "()" for no parameters.
* This enables overload-precise identification of methods during export.
*/
il_method_param_signature(
int method: @method ref,
string param_signature: string ref
);

/**
* Parameter type signature for unresolved method call targets.
* The param_signature is a parenthesized, comma-separated list of fully-qualified
* parameter type names, e.g. "(System.String,System.Int32)" or "()" for no parameters.
* This enables overload-precise matching of call targets.
*/
il_call_target_param_signature(
int instruction: @il_instruction ref,
string param_signature: string ref
);

il_field_operand(
int instruction: @il_instruction ref,
string declaring_type_name: string ref,
Expand Down Expand Up @@ -2990,3 +3012,13 @@ jvm_stack_slot(
int slot: int ref,
int producer_id: @jvm_instruction ref
);

/**
* Parameter type signature for JVM method call targets.
* The param_signature is a parenthesized, comma-separated list of human-readable
* parameter type names, e.g. "(Object,long,long)" or "()" for no parameters.
*/
jvm_call_target_param_signature(
int instruction: @jvm_instruction ref,
string param_signature: string ref
);
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ private void ExtractMethod(Method method, int typeId, ClassFile classFile, strin
// Extract access flags as raw bitmask
trap.WriteTuple("jvm_method_access_flags", methodId, (int)method.AccessFlags);

// Write parameter type signature for overload-precise identification
var descriptorUtf8ForSig = classFile.Constants.Get(method.Descriptor);
trap.WriteTuple("il_method_param_signature", methodId, ParseParamSignature(descriptorUtf8ForSig.Value));

// Check if this is a static method (for parameter indexing)
bool isStatic = (method.AccessFlags & AccessFlag.Static) != 0;

Expand Down Expand Up @@ -647,6 +651,12 @@ private void ExtractMethodRef(Instruction instr, int instrId, ClassFile classFil
int paramCount = CountParameters(descriptor);
trap.WriteTuple("jvm_number_of_arguments", instrId, paramCount);

// Write parameter type signature for overload-precise matching
if (!string.IsNullOrEmpty(descriptor))
{
trap.WriteTuple("jvm_call_target_param_signature", instrId, ParseParamSignature(descriptor));
}

if (!IsVoidReturn(descriptor))
{
trap.WriteTuple("jvm_call_has_return_value", instrId);
Expand Down Expand Up @@ -782,6 +792,66 @@ private static int CountParameters(string descriptor)
return count;
}

/// <summary>
/// Converts a JVM method descriptor to a parenthesized, comma-separated
/// parameter type signature, e.g. "(Ljava/lang/Object;JJ)V" becomes
/// "(Object,long,long)".
/// </summary>
private static string ParseParamSignature(string descriptor)
{
if (!descriptor.StartsWith("("))
return "(*)";

int closeParenIdx = descriptor.IndexOf(')');
if (closeParenIdx < 0)
return "(*)";

var paramPart = descriptor.Substring(1, closeParenIdx - 1);
var types = new System.Collections.Generic.List<string>();
int i = 0;
while (i < paramPart.Length)
{
int arrayDims = 0;
while (i < paramPart.Length && paramPart[i] == '[')
{
arrayDims++;
i++;
}

if (i >= paramPart.Length)
break;

string baseType;
char c = paramPart[i];
switch (c)
{
case 'B': baseType = "byte"; i++; break;
case 'C': baseType = "char"; i++; break;
case 'D': baseType = "double"; i++; break;
case 'F': baseType = "float"; i++; break;
case 'I': baseType = "int"; i++; break;
case 'J': baseType = "long"; i++; break;
case 'S': baseType = "short"; i++; break;
case 'Z': baseType = "boolean"; i++; break;
case 'L':
int semiIdx = paramPart.IndexOf(';', i);
if (semiIdx < 0) semiIdx = paramPart.Length;
// Extract class name, convert / to ., strip leading L
baseType = paramPart.Substring(i + 1, semiIdx - i - 1).Replace('/', '.');
i = semiIdx + 1;
break;
default:
baseType = "?";
i++;
break;
}

types.Add(baseType + new string('[', arrayDims) + new string(']', arrayDims));
}

return "(" + string.Join(",", types) + ")";
}

private static bool IsVoidReturn(string descriptor)
{
return descriptor.EndsWith(")V");
Expand Down
56 changes: 56 additions & 0 deletions binary/extractor/jvm/semmlecode.binary.dbscheme
Original file line number Diff line number Diff line change
Expand Up @@ -2467,6 +2467,28 @@ il_call_target_unresolved(
string target_method_name: string ref
);

/**
* Parameter type signature for method definitions.
* The param_signature is a parenthesized, comma-separated list of fully-qualified
* parameter type names, e.g. "(System.String,System.Int32)" or "()" for no parameters.
* This enables overload-precise identification of methods during export.
*/
il_method_param_signature(
int method: @method ref,
string param_signature: string ref
);

/**
* Parameter type signature for unresolved method call targets.
* The param_signature is a parenthesized, comma-separated list of fully-qualified
* parameter type names, e.g. "(System.String,System.Int32)" or "()" for no parameters.
* This enables overload-precise matching of call targets.
*/
il_call_target_param_signature(
int instruction: @il_instruction ref,
string param_signature: string ref
);

il_field_operand(
int instruction: @il_instruction ref,
string declaring_type_name: string ref,
Expand Down Expand Up @@ -2966,3 +2988,37 @@ jvm_method_access_flags(
unique int method: @method ref,
int flags: int ref
);

/**
* Stack height at entry to a JVM instruction.
* This is computed by abstract interpretation during extraction.
*/
jvm_stack_height(
unique int instr: @jvm_instruction ref,
int height: int ref
);

/**
* Maps a stack slot at a specific instruction to the instruction that produced the value.
* slot 0 is the top of the stack, slot 1 is below that, etc.
* producer_id is the instruction ID that pushed this value onto the stack.
*
* This allows QL to determine data flow through the operand stack without
* expensive recursive CFG traversal.
*/
#keyset[instr, slot]
jvm_stack_slot(
int instr: @jvm_instruction ref,
int slot: int ref,
int producer_id: @jvm_instruction ref
);

/**
* Parameter type signature for JVM method call targets.
* The param_signature is a parenthesized, comma-separated list of human-readable
* parameter type names, e.g. "(Object,long,long)" or "()" for no parameters.
*/
jvm_call_target_param_signature(
int instruction: @jvm_instruction ref,
string param_signature: string ref
);
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ class CilMethod extends @method {
result.getIndex() = i
}

/** Gets the parenthesized parameter type signature, e.g. `(System.String,System.Int32)`. */
string getParamSignature() { il_method_param_signature(this, result) }

CilType getDeclaringType() { methods(this, _, _, result) }

Location getLocation() { none() } // TODO: Extract
Expand Down Expand Up @@ -430,6 +433,9 @@ abstract class CilCallOrNewObject extends CilInstruction {
final int getNumberOfArguments() { il_number_of_arguments(this, result) }

final string getExternalName() { il_call_target_unresolved(this, result) }

/** Gets the parenthesized parameter type signature, e.g. `(System.String,System.Int32)`. */
final string getParamSignature() { il_call_target_param_signature(this, result) }
}

abstract class CilCall extends CilCallOrNewObject {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ class JvmMethod extends @method {

private string getSignature() { methods(this, _, result, _) }

/** Gets the parenthesized parameter type signature, e.g. `(Object,long,long)`. */
string getParamSignature() { il_method_param_signature(this, result) }

predicate isVoid() { this.getSignature().matches("%)V") }

JvmInstruction getAnInstruction() { jvm_instruction_method(result, this) }
Expand Down Expand Up @@ -1209,6 +1212,8 @@ class JvmPutfield extends @jvm_putfield, JvmFieldStore { }
abstract class JvmInvoke extends JvmInstruction {
string getCallTarget() { jvm_call_target_unresolved(this, result) }

string getParamSignature() { jvm_call_target_param_signature(this, result) }

int getNumberOfArguments() { jvm_number_of_arguments(this, result) }

predicate hasReturnValue() { jvm_call_has_return_value(this) }
Expand Down
6 changes: 6 additions & 0 deletions binary/ql/lib/semmle/code/binary/ast/ir/IR.qll
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ private module FinalInstruction {

predicate isPublic() { super.isPublic() }

/** Gets the parenthesized parameter type signature, e.g. `(System.String,System.Int32)`. */
string getParamSignature() { result = super.getParamSignature() }

/**
* Gets the fully qualified name of this method in the format:
* "Namespace.ClassName.MethodName".
Expand Down Expand Up @@ -302,6 +305,9 @@ private module FinalInstruction {
class ExternalRefInstruction extends Instruction instanceof Instruction::ExternalRefInstruction {
string getExternalName() { result = super.getExternalName() }

/** Gets the parenthesized parameter type signature, e.g. `(System.String,System.Int32)`. */
string getExternalParamSignature() { result = super.getExternalParamSignature() }

cached
predicate hasFullyQualifiedName(string namespace, string className, string methodName) {
exists(string s, string r |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,8 @@ class Function extends TFunction {

predicate isPublic() { f.isPublic() }

/** Gets the parenthesized parameter type signature, e.g. `(System.String,System.Int32)`. */
string getParamSignature() { result = f.getParamSignature() }

Type getDeclaringType() { result.getAFunction() = this }
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ class ExternalRefInstruction extends Instruction {

string getExternalName() { result = te.getExternalName(tag) }

/** Gets the parenthesized parameter type signature, e.g. `(System.String,System.Int32)`. */
string getExternalParamSignature() { result = te.getExternalParamSignature(tag) }

final override string getImmediateValue() { result = this.getExternalName() }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,12 @@ abstract class TranslatedElement extends TTranslatedElement {
*/
string getExternalName(InstructionTag tag) { none() }

/**
* Gets the parameter type signature for an external call with the given tag, e.g.
* `(System.String,System.Int32)`. This `tag` must refer to an `ExternalRef` instruction.
*/
string getExternalParamSignature(InstructionTag tag) { none() }

/**
* Gets the name of the field referenced by an instruction with the given tag. This `tag` must refer to
* a `FieldAddress` instruction (that is, an instruction for which
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ abstract class TranslatedFunction extends TranslatedElement {

abstract string getName();

/** Gets the parenthesized parameter type signature, e.g. `(System.String,System.Int32)`. */
abstract string getParamSignature();

final override string toString() { result = "Translation of " + this.getName() }

abstract predicate isProgramEntryPoint();
Expand Down Expand Up @@ -116,6 +119,9 @@ class TranslatedX86Function extends TranslatedFunction, TTranslatedX86Function {

final override predicate isPublic() { entry instanceof Raw::X86ExportedEntryInstruction }

// x86 does not have parameter type signatures
final override string getParamSignature() { result = "*" }

final override predicate hasOrdering(LocalVariableTag tag, int ordering) {
exists(Raw::X86Register r | tag = X86RegisterTag(r) |
// TODO: This hardcodes X64 calling convention for Windows
Expand Down Expand Up @@ -217,6 +223,8 @@ class TranslatedCilMethod extends TranslatedFunction, TTranslatedCilMethod {

override string getName() { result = method.getName() }

override string getParamSignature() { result = method.getParamSignature() }

override predicate isProgramEntryPoint() { none() }

override predicate isPublic() { method.isPublic() }
Expand Down Expand Up @@ -321,6 +329,8 @@ class TranslatedJvmMethod extends TranslatedFunction, TTranslatedJvmMethod {

override string getName() { result = method.getName() }

override string getParamSignature() { result = method.getParamSignature() }

override predicate isProgramEntryPoint() { none() }

override predicate isPublic() { method.isPublic() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2169,6 +2169,12 @@ class TranslatedCilCall extends TranslatedCilInstruction, TTranslatedCilCall {
result = instr.getExternalName()
}

override string getExternalParamSignature(InstructionTag tag) {
not exists(instr.getTarget()) and
tag = CilCallTargetTag() and
result = instr.getParamSignature()
}

override Instruction getChildSuccessor(TranslatedElement child, SuccessorType succType) { none() }

override Instruction getSuccessor(InstructionTag tag, SuccessorType succType) {
Expand Down Expand Up @@ -2432,6 +2438,12 @@ class TranslatedNewObject extends TranslatedCilInstruction, TTranslatedNewObject
result = instr.getExternalName()
}

override string getExternalParamSignature(InstructionTag tag) {
not exists(instr.getConstructor()) and
tag = CilCallTargetTag() and
result = instr.getParamSignature()
}

override predicate hasTempVariable(TempVariableTag tag) {
tag = CilNewObjInitVarTag()
or
Expand Down Expand Up @@ -2757,6 +2769,11 @@ class TranslatedJvmInvoke extends TranslatedJvmInstruction, TTranslatedJvmInvoke
result = instr.getCallTarget()
}

final override string getExternalParamSignature(InstructionTag tag) {
tag = JvmCallTargetTag() and
result = instr.getParamSignature()
}

override Instruction getChildSuccessor(TranslatedElement child, SuccessorType succType) { none() }

override Instruction getSuccessor(InstructionTag tag, SuccessorType succType) {
Expand Down
Loading
Loading