mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
C#: Extract function pointer types from CIL
This commit is contained in:
@@ -8,6 +8,7 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
ValueOrRefType,
|
||||
TypeParameter,
|
||||
Array,
|
||||
Pointer
|
||||
Pointer,
|
||||
FunctionPointer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection.Metadata;
|
||||
|
||||
namespace Semmle.Extraction.CIL.Entities
|
||||
{
|
||||
internal sealed class FunctionPointerType : Type, IParameterizable, ICustomModifierReceiver
|
||||
{
|
||||
private readonly MethodSignature<Type> signature;
|
||||
|
||||
public FunctionPointerType(Context cx, MethodSignature<Type> signature) : base(cx)
|
||||
{
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public override CilTypeKind Kind => CilTypeKind.FunctionPointer;
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
using var id = new StringWriter();
|
||||
WriteName(
|
||||
id.Write,
|
||||
t => id.Write(t.Name),
|
||||
signature
|
||||
);
|
||||
return id.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public override Namespace? ContainingNamespace => Cx.GlobalNamespace;
|
||||
|
||||
public override Type? ContainingType => null;
|
||||
|
||||
public override int ThisTypeParameterCount => throw new System.NotImplementedException();
|
||||
|
||||
public override IEnumerable<Type> TypeParameters => throw new System.NotImplementedException();
|
||||
|
||||
public override Type Construct(IEnumerable<Type> typeArguments) => throw new System.NotImplementedException();
|
||||
|
||||
public override void WriteAssemblyPrefix(TextWriter trapFile) { }
|
||||
|
||||
public override void WriteId(TextWriter trapFile, bool inContext)
|
||||
{
|
||||
WriteName(
|
||||
trapFile.Write,
|
||||
t => t.WriteId(trapFile, inContext),
|
||||
signature
|
||||
);
|
||||
}
|
||||
|
||||
internal static void WriteName<TType>(Action<string> write, Action<TType> writeType, MethodSignature<TType> signature)
|
||||
{
|
||||
write("delegate* ");
|
||||
write(GetCallingConvention(signature.Header.CallingConvention));
|
||||
write("<");
|
||||
foreach (var pt in signature.ParameterTypes)
|
||||
{
|
||||
writeType(pt);
|
||||
write(",");
|
||||
}
|
||||
writeType(signature.ReturnType);
|
||||
write(">");
|
||||
}
|
||||
|
||||
internal static string GetCallingConvention(SignatureCallingConvention callingConvention)
|
||||
{
|
||||
if (callingConvention == SignatureCallingConvention.Default)
|
||||
{
|
||||
return "managed";
|
||||
}
|
||||
|
||||
if (callingConvention == SignatureCallingConvention.Unmanaged)
|
||||
{
|
||||
return "unmanaged";
|
||||
}
|
||||
|
||||
return $"unmanaged[{callingConvention}]";
|
||||
}
|
||||
|
||||
public override IEnumerable<IExtractionProduct> Contents
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var c in base.Contents)
|
||||
{
|
||||
yield return c;
|
||||
}
|
||||
|
||||
var retType = signature.ReturnType;
|
||||
if (retType is ModifiedType mt)
|
||||
{
|
||||
retType = mt.Unmodified;
|
||||
yield return Tuples.cil_custom_modifiers(this, mt.Modifier, mt.IsRequired);
|
||||
}
|
||||
yield return Tuples.cil_function_pointer_return_type(this, retType);
|
||||
|
||||
yield return Tuples.cil_function_pointer_calling_conventions(this, signature.Header.CallingConvention);
|
||||
|
||||
var i = 0;
|
||||
foreach (var p in signature.ParameterTypes)
|
||||
{
|
||||
var t = p;
|
||||
if (t is ModifiedType mtparam)
|
||||
{
|
||||
t = mtparam.Unmodified;
|
||||
yield return Tuples.cil_custom_modifiers(this, mtparam.Modifier, mtparam.IsRequired);
|
||||
}
|
||||
yield return Cx.Populate(new Parameter(Cx, this, i++, t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Semmle.Extraction.CIL.Entities
|
||||
{
|
||||
internal interface IParameterizable : IEntity
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
/// <summary>
|
||||
/// A method entity.
|
||||
/// </summary>
|
||||
internal abstract class Method : TypeContainer, IMember, ICustomModifierReceiver
|
||||
internal abstract class Method : TypeContainer, IMember, ICustomModifierReceiver, IParameterizable
|
||||
{
|
||||
protected MethodTypeParameter[]? genericParams;
|
||||
protected GenericContext gc;
|
||||
|
||||
@@ -37,6 +37,13 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
|
||||
public override void WriteAssemblyPrefix(TextWriter trapFile) => throw new NotImplementedException();
|
||||
|
||||
public override void WriteId(TextWriter trapFile, bool inContext) => throw new NotImplementedException();
|
||||
public override void WriteId(TextWriter trapFile, bool inContext)
|
||||
{
|
||||
Unmodified.WriteId(trapFile, inContext);
|
||||
trapFile.Write(IsRequired ? " modreq" : " modopt");
|
||||
trapFile.Write("(");
|
||||
Modifier.WriteId(trapFile, inContext);
|
||||
trapFile.Write(")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
/// </summary>
|
||||
internal sealed class Parameter : LabelledEntity
|
||||
{
|
||||
private readonly Method method;
|
||||
private readonly IParameterizable method;
|
||||
private readonly int index;
|
||||
private readonly Type type;
|
||||
|
||||
public Parameter(Context cx, Method m, int i, Type t) : base(cx)
|
||||
public Parameter(Context cx, IParameterizable m, int i, Type t) : base(cx)
|
||||
{
|
||||
method = m;
|
||||
index = i;
|
||||
|
||||
@@ -48,10 +48,20 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
|
||||
private struct FnPtr : ITypeSignature
|
||||
{
|
||||
private readonly MethodSignature<ITypeSignature> signature;
|
||||
|
||||
public FnPtr(MethodSignature<ITypeSignature> signature)
|
||||
{
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public void WriteId(TextWriter trapFile, GenericContext gc)
|
||||
{
|
||||
trapFile.Write("<method signature>");
|
||||
FunctionPointerType.WriteName(
|
||||
trapFile.Write,
|
||||
t => t.WriteId(trapFile, gc),
|
||||
signature
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +72,7 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
new ByRef(elementType);
|
||||
|
||||
ITypeSignature ISignatureTypeProvider<ITypeSignature, object>.GetFunctionPointerType(MethodSignature<ITypeSignature> signature) =>
|
||||
new FnPtr();
|
||||
new FnPtr(signature);
|
||||
|
||||
private class Instantiation : ITypeSignature
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
elementType; // ??
|
||||
|
||||
Type ISignatureTypeProvider<Type, GenericContext>.GetFunctionPointerType(MethodSignature<Type> signature) =>
|
||||
cx.ErrorType; // Don't know what to do !!
|
||||
cx.Populate(new FunctionPointerType(cx, signature));
|
||||
|
||||
Type IConstructedTypeProvider<Type>.GetGenericInstantiation(Type genericType, ImmutableArray<Type> typeArguments) =>
|
||||
genericType.Construct(typeArguments);
|
||||
|
||||
@@ -86,6 +86,12 @@ namespace Semmle.Extraction.CIL
|
||||
internal static Tuple cil_method(Method method, string name, Type declType, Type returnType) =>
|
||||
new Tuple("cil_method", method, name, declType, returnType);
|
||||
|
||||
internal static Tuple cil_function_pointer_return_type(FunctionPointerType fnptr, Type returnType) =>
|
||||
new Tuple("cil_function_pointer_return_type", fnptr, returnType);
|
||||
|
||||
internal static Tuple cil_function_pointer_calling_conventions(FunctionPointerType fnptr, System.Reflection.Metadata.SignatureCallingConvention callingConvention) =>
|
||||
new Tuple("cil_function_pointer_calling_conventions", fnptr, (int)callingConvention);
|
||||
|
||||
internal static Tuple cil_method_implementation(MethodImplementation impl, Method method, Assembly assembly) =>
|
||||
new Tuple("cil_method_implementation", impl, method, assembly);
|
||||
|
||||
@@ -101,7 +107,7 @@ namespace Semmle.Extraction.CIL
|
||||
internal static Tuple cil_newslot(Method method) =>
|
||||
new Tuple("cil_newslot", method);
|
||||
|
||||
internal static Tuple cil_parameter(Parameter p, Method m, int i, Type t) =>
|
||||
internal static Tuple cil_parameter(Parameter p, IParameterizable m, int i, Type t) =>
|
||||
new Tuple("cil_parameter", p, m, i, t);
|
||||
|
||||
internal static Tuple cil_parameter_in(Parameter p) =>
|
||||
|
||||
@@ -1695,6 +1695,7 @@ case @cil_type.kind of
|
||||
| 1 = @cil_typeparameter
|
||||
| 2 = @cil_array_type
|
||||
| 3 = @cil_pointer_type
|
||||
| 4 = @cil_function_pointer_type
|
||||
;
|
||||
|
||||
cil_type(
|
||||
@@ -1713,6 +1714,10 @@ cil_array_type(
|
||||
int element_type: @cil_type ref,
|
||||
int rank: int ref);
|
||||
|
||||
cil_function_pointer_return_type(
|
||||
unique int id: @cil_function_pointer_type ref,
|
||||
int return_type: @cil_type ref);
|
||||
|
||||
cil_method(
|
||||
unique int id: @cil_method,
|
||||
string name: string ref,
|
||||
@@ -1746,12 +1751,13 @@ cil_field(
|
||||
@cil_variable = @cil_field | @cil_stack_variable;
|
||||
@cil_stack_variable = @cil_local_variable | @cil_parameter;
|
||||
@cil_member = @cil_method | @cil_type | @cil_field | @cil_property | @cil_event;
|
||||
@cil_custom_modifier_receiver = @cil_method | @cil_property | @cil_parameter | @cil_field; // todo: add function pointer type
|
||||
@cil_custom_modifier_receiver = @cil_method | @cil_property | @cil_parameter | @cil_field | @cil_function_pointer_type;
|
||||
@cil_parameterizable = @cil_method | @cil_function_pointer_type;
|
||||
|
||||
#keyset[method, index]
|
||||
#keyset[parameterizable, index]
|
||||
cil_parameter(
|
||||
unique int id: @cil_parameter,
|
||||
int method: @cil_method ref,
|
||||
int parameterizable: @cil_parameterizable ref,
|
||||
int index: int ref,
|
||||
int param_type: @cil_type ref);
|
||||
|
||||
@@ -1796,6 +1802,10 @@ cil_local_variable(
|
||||
int index: int ref,
|
||||
int var_type: @cil_type ref);
|
||||
|
||||
cil_function_pointer_calling_conventions(
|
||||
int id: @cil_function_pointer_type ref,
|
||||
int kind: int ref);
|
||||
|
||||
// CIL handlers (exception handlers etc).
|
||||
|
||||
case @cil_handler.kind of
|
||||
|
||||
52
csharp/ql/test/library-tests/cil/functionPointers/Class1.cs_
Normal file
52
csharp/ql/test/library-tests/cil/functionPointers/Class1.cs_
Normal file
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class FnPointer
|
||||
{
|
||||
public unsafe static class Program
|
||||
{
|
||||
static delegate*<int> pointer = &M0;
|
||||
|
||||
public static int M0()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void M1(delegate*<ref int, out object?, int> f)
|
||||
{
|
||||
int i = 42;
|
||||
int j = f(ref i, out object? o);
|
||||
}
|
||||
|
||||
static void M2<T>(delegate* unmanaged[Stdcall/*, StdcallSuppressGCTransition*/]<ref int, out object?, T, void> f) where T : new()
|
||||
{
|
||||
int i = 42;
|
||||
f(ref i, out object? o, new T());
|
||||
}
|
||||
|
||||
static void M3(delegate* managed<ref int, out object?, in int, ref int> f)
|
||||
{
|
||||
int i = 42;
|
||||
ref int j = ref f(ref i, out object? o, in i);
|
||||
}
|
||||
|
||||
static void M4<T>(delegate*<T, int> f) where T : new()
|
||||
{
|
||||
int j = f(new T());
|
||||
}
|
||||
|
||||
static void M5(delegate*<B, A> f, delegate*<A, B> ff)
|
||||
{
|
||||
M5(ff, ff); // implicit conversion due to implicit reference conversion
|
||||
}
|
||||
|
||||
static void M6(delegate*<int*, void*> f, delegate*<void*, int*> ff)
|
||||
{
|
||||
M6(ff, ff); // implicit conversion due to implicit pointer conversion
|
||||
}
|
||||
|
||||
class A { }
|
||||
class B : A { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// semmle-extractor-options: --cil
|
||||
|
||||
using System;
|
||||
|
||||
class Test
|
||||
{
|
||||
}
|
||||
BIN
csharp/ql/test/library-tests/cil/functionPointers/fnptr.dll
Normal file
BIN
csharp/ql/test/library-tests/cil/functionPointers/fnptr.dll
Normal file
Binary file not shown.
Reference in New Issue
Block a user