C#: Extract function pointers

This commit is contained in:
Tamas Vajk
2020-11-03 15:36:39 +01:00
parent fbab8f8539
commit 876123315d
14 changed files with 335 additions and 51 deletions

View File

@@ -4,6 +4,7 @@ using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Semmle.Extraction.Kinds;
using System.IO;
using System;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
@@ -66,7 +67,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
}
break;
default:
// Delegate call; `d()`
// Delegate or function pointer call; `d()`
Create(cx, Syntax.Expression, this, child++);
break;
}
@@ -84,7 +85,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
if (target == null)
{
if (!isDynamicCall && !IsDelegateCall(info))
if (!isDynamicCall && !IsDelegateLikeCall(info))
cx.ModelError(Syntax, "Unable to resolve target for call. (Compilation error?)");
return;
}
@@ -98,7 +99,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
// Either the qualifier (Expression) is dynamic,
// or one of the arguments is dynamic.
var node = (InvocationExpressionSyntax)info.Node;
return !IsDelegateCall(info) &&
return !IsDelegateLikeCall(info) &&
(IsDynamic(info.Context, node.Expression) || node.ArgumentList.Arguments.Any(arg => IsDynamic(info.Context, arg.Expression)));
}
@@ -133,12 +134,22 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
}
}
private static bool IsDelegateCall(ExpressionNodeInfo info)
private static bool IsDelegateLikeCall(ExpressionNodeInfo info)
{
return IsDelegateLikeCall(info, IsDelegateLikeCall);
}
private static bool IsDelegateInvokeCall(ExpressionNodeInfo info)
{
return IsDelegateLikeCall(info, IsDelegateInvoke);
}
private static bool IsDelegateLikeCall(ExpressionNodeInfo info, Func<ISymbol, bool> check)
{
var si = info.SymbolInfo;
if (si.CandidateReason == CandidateReason.OverloadResolutionFailure &&
si.CandidateSymbols.OfType<IMethodSymbol>().All(s => s.MethodKind == MethodKind.DelegateInvoke))
si.CandidateSymbols.All(check))
{
return true;
}
@@ -153,9 +164,26 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
return true;
}
return si.Symbol != null &&
si.Symbol.Kind == SymbolKind.Method &&
((IMethodSymbol)si.Symbol).MethodKind == MethodKind.DelegateInvoke;
return check(si.Symbol);
}
private static bool IsDelegateLikeCall(ISymbol symbol)
{
return IsFunctionPointer(symbol) ||
IsDelegateInvoke(symbol);
}
private static bool IsFunctionPointer(ISymbol symbol)
{
return symbol != null &&
symbol.Kind == SymbolKind.FunctionPointerType;
}
private static bool IsDelegateInvoke(ISymbol symbol)
{
return symbol != null &&
symbol.Kind == SymbolKind.Method &&
((IMethodSymbol)symbol).MethodKind == MethodKind.DelegateInvoke;
}
private static bool IsLocalFunctionInvocation(ExpressionNodeInfo info)
@@ -168,8 +196,10 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
return IsNameof((InvocationExpressionSyntax)info.Node)
? ExprKind.NAMEOF
: IsDelegateCall(info)
? ExprKind.DELEGATE_INVOCATION
: IsDelegateLikeCall(info)
? IsDelegateInvokeCall(info)
? ExprKind.DELEGATE_INVOCATION
: ExprKind.FUNCTION_POINTER_INVOCATION
: IsLocalFunctionInvocation(info)
? ExprKind.LOCAL_FUNCTION_INVOCATION
: ExprKind.METHOD_INVOCATION;

View File

@@ -39,7 +39,7 @@ namespace Semmle.Extraction.CSharp.Entities
var originalDefinition = IsSourceDeclaration ? this : Create(Context, symbol.OriginalDefinition);
var returnType = Type.Create(Context, symbol.ReturnType);
trapFile.local_functions(this, symbol.Name, returnType, originalDefinition);
ExtractRefReturn(trapFile);
ExtractRefReturn(trapFile, symbol, this);
}
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NeedsLabel;

View File

@@ -40,7 +40,9 @@ namespace Semmle.Extraction.CSharp.Entities
foreach (var p in parameters.Zip(originalParameters, (paramSymbol, originalParam) => new { paramSymbol, originalParam }))
{
var original = SymbolEqualityComparer.Default.Equals(p.paramSymbol, p.originalParam) ? null : Parameter.Create(Context, p.originalParam, originalMethod);
var original = SymbolEqualityComparer.Default.Equals(p.paramSymbol, p.originalParam)
? null
: Parameter.Create(Context, p.originalParam, originalMethod);
Parameter.Create(Context, p.paramSymbol, this, original);
}
@@ -110,7 +112,7 @@ namespace Semmle.Extraction.CSharp.Entities
/// <summary>
/// Factored out to share logic between `Method` and `UserOperator`.
/// </summary>
protected static void BuildMethodId(Method m, TextWriter trapFile)
private static void BuildMethodId(Method m, TextWriter trapFile)
{
m.symbol.ReturnType.BuildOrWriteId(m.Context, trapFile, m.symbol);
trapFile.Write(" ");
@@ -324,12 +326,12 @@ namespace Semmle.Extraction.CSharp.Entities
}
}
protected void ExtractRefReturn(TextWriter trapFile)
public static void ExtractRefReturn(TextWriter trapFile, IMethodSymbol method, IEntity element)
{
if (symbol.ReturnsByRef)
trapFile.type_annotation(this, Kinds.TypeAnnotation.Ref);
if (symbol.ReturnsByRefReadonly)
trapFile.type_annotation(this, Kinds.TypeAnnotation.ReadonlyRef);
if (method.ReturnsByRef)
trapFile.type_annotation(element, Kinds.TypeAnnotation.Ref);
if (method.ReturnsByRefReadonly)
trapFile.type_annotation(element, Kinds.TypeAnnotation.ReadonlyRef);
}
protected void PopulateMethod(TextWriter trapFile)

View File

@@ -49,7 +49,7 @@ namespace Semmle.Extraction.CSharp.Entities
PopulateGenerics(trapFile);
Overrides(trapFile);
ExtractRefReturn(trapFile);
ExtractRefReturn(trapFile, symbol, this);
ExtractCompilerGenerated(trapFile);
}

View File

@@ -0,0 +1,44 @@
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
namespace Semmle.Extraction.CSharp.Entities
{
internal class FunctionPointerType : Type<IFunctionPointerTypeSymbol>
{
private FunctionPointerType(Context cx, IFunctionPointerTypeSymbol init)
: base(cx, init)
{
}
public override void WriteId(TextWriter trapFile)
{
symbol.BuildTypeId(Context, trapFile, symbol);
trapFile.Write(";functionpointertype");
}
public override bool NeedsPopulation => true;
public override void Populate(TextWriter trapFile)
{
var unmanagedCallingConventionTypes = symbol.Signature.UnmanagedCallingConventionTypes.Select(nt => Create(Context, nt)).ToArray();
trapFile.function_pointer_calling_conventions(this, (int)symbol.Signature.CallingConvention);
for (var i = 0; i < unmanagedCallingConventionTypes.Length; i++)
{
trapFile.has_unmanaged_calling_conventions(this, i, unmanagedCallingConventionTypes[i].TypeRef);
}
PopulateType(trapFile);
}
public static FunctionPointerType Create(Context cx, IFunctionPointerTypeSymbol symbol) => FunctionPointerTypeFactory.Instance.CreateEntityFromSymbol(cx, symbol);
private class FunctionPointerTypeFactory : ICachedEntityFactory<IFunctionPointerTypeSymbol, FunctionPointerType>
{
public static FunctionPointerTypeFactory Instance { get; } = new FunctionPointerTypeFactory();
public FunctionPointerType Create(Context cx, IFunctionPointerTypeSymbol init) => new FunctionPointerType(cx, init);
}
}
}

View File

@@ -1,6 +1,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Util;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -57,6 +58,7 @@ namespace Semmle.Extraction.CSharp.Entities
case TypeKind.Enum: return Kinds.TypeKind.ENUM;
case TypeKind.Delegate: return Kinds.TypeKind.DELEGATE;
case TypeKind.Pointer: return Kinds.TypeKind.POINTER;
case TypeKind.FunctionPointer: return Kinds.TypeKind.FUNCTION_POINTER;
case TypeKind.Error: return Kinds.TypeKind.UNKNOWN;
default:
cx.ModelError(t, $"Unhandled type kind '{t.TypeKind}'");
@@ -131,23 +133,14 @@ namespace Semmle.Extraction.CSharp.Entities
// This is a delegate.
// The method "Invoke" has the return type.
var invokeMethod = ((INamedTypeSymbol)symbol).DelegateInvokeMethod;
ExtractParametersForDelegateLikeType(trapFile, invokeMethod,
t => trapFile.delegate_return_type(this, t));
}
// Copy the parameters from the "Invoke" method to the delegate type
for (var i = 0; i < invokeMethod.Parameters.Length; ++i)
{
var param = invokeMethod.Parameters[i];
var originalParam = invokeMethod.OriginalDefinition.Parameters[i];
var originalParamEntity = SymbolEqualityComparer.Default.Equals(param, originalParam) ? null :
DelegateTypeParameter.Create(Context, originalParam, Create(Context, ((INamedTypeSymbol)symbol).OriginalDefinition));
DelegateTypeParameter.Create(Context, param, this, originalParamEntity);
}
var returnKey = Create(Context, invokeMethod.ReturnType);
trapFile.delegate_return_type(this, returnKey.TypeRef);
if (invokeMethod.ReturnsByRef)
trapFile.type_annotation(this, Kinds.TypeAnnotation.Ref);
if (invokeMethod.ReturnsByRefReadonly)
trapFile.type_annotation(this, Kinds.TypeAnnotation.ReadonlyRef);
if (symbol is IFunctionPointerTypeSymbol functionPointer)
{
ExtractParametersForDelegateLikeType(trapFile, functionPointer.Signature,
t => trapFile.function_pointer_return_type(this, t));
}
Modifier.ExtractModifiers(Context, trapFile, this, symbol);
@@ -170,6 +163,23 @@ namespace Semmle.Extraction.CSharp.Entities
}
}
private void ExtractParametersForDelegateLikeType(TextWriter trapFile, IMethodSymbol invokeMethod, Action<Type> storeReturnType)
{
for (var i = 0; i < invokeMethod.Parameters.Length; ++i)
{
var param = invokeMethod.Parameters[i];
var originalParam = invokeMethod.OriginalDefinition.Parameters[i];
var originalParamEntity = SymbolEqualityComparer.Default.Equals(param, originalParam)
? null
: DelegateTypeParameter.Create(Context, originalParam, Create(Context, ((INamedTypeSymbol)symbol).OriginalDefinition));
DelegateTypeParameter.Create(Context, param, this, originalParamEntity);
}
var returnKey = Create(Context, invokeMethod.ReturnType);
storeReturnType(returnKey.TypeRef);
Method.ExtractRefReturn(trapFile, invokeMethod, this);
}
/// <summary>
/// Called to extract all members and nested types.
/// This is called on each member of a namespace,
@@ -265,8 +275,7 @@ namespace Semmle.Extraction.CSharp.Entities
symbol != null && symbol.TypeKind == TypeKind.Delegate;
/// <summary>
/// A copy of a delegate "Invoke" method parameter used for the delgate
/// type.
/// A copy of a delegate "Invoke" method or function pointer parameter.
/// </summary>
private class DelegateTypeParameter : Parameter
{

View File

@@ -120,6 +120,7 @@ namespace Semmle.Extraction.Kinds
GT_PATTERN = 123,
LE_PATTERN = 124,
GE_PATTERN = 125,
NOT_PATTERN = 126
NOT_PATTERN = 126,
FUNCTION_POINTER_INVOCATION = 129,
}
}

View File

@@ -34,6 +34,7 @@ namespace Semmle.Extraction.Kinds // lgtm[cs/similar-file]
DYNAMIC = 29,
ARGLIST = 30,
UNKNOWN = 31,
TUPLE = 32
TUPLE = 32,
FUNCTION_POINTER = 33
}
}

View File

@@ -38,6 +38,8 @@ namespace Semmle.Extraction.CSharp.Populators
public override IEntity VisitPointerType(IPointerTypeSymbol symbol) => PointerType.Create(cx, symbol);
public override IEntity VisitFunctionPointerType(IFunctionPointerTypeSymbol symbol) => FunctionPointerType.Create(cx, symbol);
public override IEntity VisitDynamicType(IDynamicTypeSymbol symbol) => DynamicType.Create(cx, symbol);
}
}

View File

@@ -134,6 +134,13 @@ namespace Semmle.Extraction.CSharp
return tp.ContainingSymbol is ITypeSymbol cont
? IdDependsOnImpl(cont)
: SymbolEqualityComparer.Default.Equals(tp.ContainingSymbol, symbol);
case TypeKind.FunctionPointer:
var funptr = (IFunctionPointerTypeSymbol)type;
if (funptr.Signature.Parameters.Any(p => IdDependsOnImpl(p.Type)))
{
return true;
}
return IdDependsOnImpl(funptr.Signature.ReturnType);
default:
return false;
}
@@ -190,6 +197,10 @@ namespace Semmle.Extraction.CSharp
case TypeKind.Dynamic:
trapFile.Write("dynamic");
return;
case TypeKind.FunctionPointer:
var funptr = (IFunctionPointerTypeSymbol)type;
funptr.BuildFunctionPointerTypeId(cx, trapFile, symbolBeingDefined);
return;
default:
throw new InternalError(type, $"Unhandled type kind '{type.TypeKind}'");
}
@@ -265,6 +276,53 @@ namespace Semmle.Extraction.CSharp
trapFile.Write("::");
}
private static void BuildFunctionPointerTypeId(this IFunctionPointerTypeSymbol funptr, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined)
{
trapFile.Write("delegate* ");
trapFile.Write(funptr.Signature.CallingConvention.ToString().ToLowerInvariant());
if (funptr.Signature.UnmanagedCallingConventionTypes.Any())
{
trapFile.Write('[');
trapFile.BuildList(",", funptr.Signature.UnmanagedCallingConventionTypes,
(ta, tb0) => ta.BuildOrWriteId(cx, tb0, symbolBeingDefined)
);
trapFile.Write("]");
}
trapFile.Write('<');
trapFile.BuildList(",", funptr.Signature.Parameters,
(p, trap) =>
{
p.Type.BuildOrWriteId(cx, trap, symbolBeingDefined);
switch (p.RefKind)
{
case RefKind.Out:
trap.Write(" out");
break;
case RefKind.In:
trap.Write(" in");
break;
case RefKind.Ref:
trap.Write(" ref");
break;
}
});
if (funptr.Signature.Parameters.Any())
{
trapFile.Write(",");
}
funptr.Signature.ReturnType.BuildOrWriteId(cx, trapFile, symbolBeingDefined);
if (funptr.Signature.ReturnsByRef)
trapFile.Write(" ref");
if (funptr.Signature.ReturnsByRefReadonly)
trapFile.Write(" readonly ref");
trapFile.Write('>');
}
private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType)
{
if (!constructUnderlyingTupleType && named.IsTupleType)
@@ -394,6 +452,10 @@ namespace Semmle.Extraction.CSharp
ptr.PointedAtType.BuildDisplayName(cx, trapFile);
trapFile.Write('*');
return;
case TypeKind.FunctionPointer:
var funptr = (IFunctionPointerTypeSymbol)type;
funptr.BuildFunctionPointerTypeDisplayName(cx, trapFile);
return;
case TypeKind.TypeParameter:
trapFile.Write(type.Name);
return;
@@ -406,31 +468,90 @@ namespace Semmle.Extraction.CSharp
}
}
public static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, Context cx, TextWriter trapFile, bool constructUnderlyingTupleType)
private static void BuildFunctionPointerTypeDisplayName(this IFunctionPointerTypeSymbol funptr, Context cx, TextWriter trapFile)
{
trapFile.Write("delegate* ");
trapFile.Write(funptr.Signature.CallingConvention.ToString().ToLowerInvariant());
if (funptr.Signature.UnmanagedCallingConventionTypes.Any())
{
trapFile.Write('[');
trapFile.BuildList(
",",
funptr.Signature.UnmanagedCallingConventionTypes,
(t, tb0) => t.BuildDisplayName(cx, tb0));
trapFile.Write("]");
}
trapFile.Write('<');
trapFile.BuildList(",", funptr.Signature.Parameters,
(p, trap) =>
{
p.Type.BuildDisplayName(cx, trapFile);
switch (p.RefKind)
{
case RefKind.Out:
trap.Write(" out");
break;
case RefKind.In:
trap.Write(" in");
break;
case RefKind.Ref:
trap.Write(" ref");
break;
}
});
if (funptr.Signature.Parameters.Any())
{
trapFile.Write(",");
}
funptr.Signature.ReturnType.BuildDisplayName(cx, trapFile);
if (funptr.Signature.ReturnsByRef)
trapFile.Write(" ref");
if (funptr.Signature.ReturnsByRefReadonly)
trapFile.Write(" readonly ref");
trapFile.Write('>');
}
private static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, Context cx, TextWriter trapFile, bool constructUnderlyingTupleType)
{
if (!constructUnderlyingTupleType && namedType.IsTupleType)
{
trapFile.Write('(');
trapFile.BuildList(",", namedType.TupleElements.Select(f => f.Type),
(t, tb0) => t.BuildDisplayName(cx, tb0)
);
trapFile.BuildList(
",",
namedType.TupleElements.Select(f => f.Type),
(t, tb0) => t.BuildDisplayName(cx, tb0));
trapFile.Write(")");
return;
}
if (namedType.IsAnonymousType)
{
namedType.BuildAnonymousName(cx, trapFile);
}
else
{
trapFile.Write(namedType.Name);
}
if (namedType.IsGenericType && namedType.TypeKind != TypeKind.Error && namedType.TypeArguments.Any())
{
trapFile.Write('<');
trapFile.BuildList(",", namedType.TypeArguments, (p, tb0) =>
{
if (IsReallyBound(namedType))
p.BuildDisplayName(cx, tb0);
});
trapFile.BuildList(
",",
namedType.TypeArguments,
(p, tb0) =>
{
if (IsReallyBound(namedType))
{
p.BuildDisplayName(cx, tb0);
}
});
trapFile.Write('>');
}
}

View File

@@ -151,6 +151,11 @@ namespace Semmle.Extraction.CSharp
trapFile.WriteTuple("delegate_return_type", delegateKey, returnType);
}
internal static void function_pointer_return_type(this TextWriter trapFile, Type functionPointer, Type returnType)
{
trapFile.WriteTuple("function_pointer_return_type", functionPointer, returnType);
}
internal static void destructor_location(this TextWriter trapFile, Destructor destructor, Location location)
{
trapFile.WriteTuple("destructor_location", destructor, location);
@@ -476,6 +481,16 @@ namespace Semmle.Extraction.CSharp
trapFile.WriteTuple("specific_type_parameter_nullability", constraints, baseType, nullability);
}
internal static void function_pointer_calling_conventions(this TextWriter trapFile, FunctionPointerType type, int kind)
{
trapFile.WriteTuple("function_pointer_calling_conventions", type, kind);
}
internal static void has_unmanaged_calling_conventions(this TextWriter trapFile, FunctionPointerType type, int index, Type convention)
{
trapFile.WriteTuple("has_unmanaged_calling_conventions", type, index, convention);
}
internal static void stackalloc_array_creation(this TextWriter trapFile, Expression array)
{
trapFile.WriteTuple("stackalloc_array_creation", array);