dotnet / corert

This repo contains CoreRT, an experimental .NET Core runtime optimized for AOT (ahead of time compilation) scenarios, with the accompanying compiler toolchain.
http://dot.net
MIT License
2.91k stars 508 forks source link

The code below will cause program crashing during runtime #7505

Open hez2010 opened 5 years ago

hez2010 commented 5 years ago

Bug: The code below will cause program crashing during runtime

dotnet add package Microsoft.DotNet.ILCompiler -v 1.0.0-alpha-*
dotnet publish -c Release -r win-x64
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;

namespace TypeTest
{
    public struct GenericNum<T> : IEquatable<T>, IComparable<T>, IComparable where T : struct
    {
        private static Func<T, T, T> add;
        private static Func<T, T, T> sub;
        private static Func<T, T, T> mul;
        private static Func<T, T, T> div;
        private static Func<T, T, bool> equ;
        private static Func<T, T, bool> neq;
        private static Func<T, T, bool> grt;
        private static Func<T, T, bool> lst;
        public T Value { get; set; }

        public GenericNum(T value)
        {
            if (equ == null)
            {
                var p1 = Expression.Parameter(typeof(T));
                var p2 = Expression.Parameter(typeof(T));
                var dt = Expression.Parameter(typeof(double));
                add = (Func<T, T, T>)Expression.Lambda(Expression.Add(p1, p2), p1, p2).Compile();
                sub = (Func<T, T, T>)Expression.Lambda(Expression.Subtract(p1, p2), p1, p2).Compile();
                mul = (Func<T, T, T>)Expression.Lambda(Expression.Multiply(p1, p2), p1, p2).Compile();
                div = (Func<T, T, T>)Expression.Lambda(Expression.Divide(p1, p2), p1, p2).Compile();
                equ = (Func<T, T, bool>)Expression.Lambda(Expression.Equal(p1, p2), p1, p2).Compile();
                neq = (Func<T, T, bool>)Expression.Lambda(Expression.NotEqual(p1, p2), p1, p2).Compile();
                grt = (Func<T, T, bool>)Expression.Lambda(Expression.GreaterThan(p1, p2), p1, p2).Compile();
                lst = (Func<T, T, bool>)Expression.Lambda(Expression.LessThan(p1, p2), p1, p2).Compile();
            }
            Value = value;
        }

        public int CompareTo(T other)
        {
            if (lst(Value, other)) return -1;
            else if (grt(Value, other)) return 1;
            return 0;
        }

        public int CompareTo(object obj)
        {
            if (obj is GenericNum<T> t)
            {
                var other = t.Value;
                if (lst(Value, other)) return -1;
                else if (grt(Value, other)) return 1;
            }
            return 0;
        }

        public override bool Equals(object obj)
        {
            if (obj is GenericNum<T> t) return equ(this, t);
            return false;
        }

        public override int GetHashCode() => Value.GetHashCode();

        public bool Equals(T other) => equ(Value, other);

        public static T operator +(GenericNum<T> left, GenericNum<T> right) => add(left.Value, right.Value);
        public static T operator -(GenericNum<T> left, GenericNum<T> right) => sub(left.Value, right.Value);
        public static T operator *(GenericNum<T> left, GenericNum<T> right) => mul(left.Value, right.Value);
        public static T operator /(GenericNum<T> left, GenericNum<T> right) => div(left.Value, right.Value);
        public static bool operator >(GenericNum<T> left, GenericNum<T> right) => grt(left.Value, right.Value);
        public static bool operator <(GenericNum<T> left, GenericNum<T> right) => lst(left.Value, right.Value);
        public static bool operator ==(GenericNum<T> left, GenericNum<T> right) => equ(left.Value, right.Value);
        public static bool operator !=(GenericNum<T> left, GenericNum<T> right) => neq(left.Value, right.Value);

        public static implicit operator T(GenericNum<T> value) => value.Value;
        public static implicit operator GenericNum<T>(T value) => new GenericNum<T>(value);
    }

    class Program
    {
        static void Main(string[] args)
        {
            var r = new Random();
            var x = new GenericNum<int>(r.Next()); //create
            var y = new GenericNum<int>(r.Next()); //create
            var p = x + y; //add
            Console.WriteLine(p);
            return;
        }
    }
}

dotnet --info:

.NET Core SDK (reflecting any global.json):
 Version:   2.2.300
 Commit:    73efd5bd87

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.18362
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\2.2.300\

Host (useful for support):
  Version: 3.0.0-preview6-27804-01
  Commit:  fdf81c6faf

.NET Core SDKs installed:
  2.1.700 [C:\Program Files\dotnet\sdk]
  2.2.300 [C:\Program Files\dotnet\sdk]
  3.0.100-preview6-012264 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.All 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.0.0-preview6.19307.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.0.0-preview6-27804-01 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.0.0-preview6-27804-01 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

To install additional .NET Core runtimes or SDKs:
  https://aka.ms/dotnet-download
MichalStrehovsky commented 5 years ago

Thanks for reporting this!

This is an access violation around this stack

    reproNative.exe!00007ff7c77cb878()  Unknown
>   reproNative.exe!S_P_CoreLib_System_Delegate__CreateObjectArrayDelegate() Line 758   Unknown
    reproNative.exe!S_P_CoreLib_Internal_Runtime_Augments_DynamicDelegateAugments__CreateObjectArrayDelegate() Line 31  Unknown
    reproNative.exe!System_Linq_Expressions_System_Dynamic_Utils_DelegateHelpers__CreateObjectArrayDelegate() Line 20   Unknown
    reproNative.exe!System_Linq_Expressions_System_Linq_Expressions_Interpreter_LightLambda__MakeDelegate() Line 366    Unknown
    reproNative.exe!System_Linq_Expressions_System_Linq_Expressions_Interpreter_LightDelegateCreator__CreateDelegate_0() Line 33    Unknown
    reproNative.exe!System_Linq_Expressions_System_Linq_Expressions_Interpreter_LightDelegateCreator__CreateDelegate() Line 28  Unknown
    reproNative.exe!System_Linq_Expressions_System_Linq_Expressions_LambdaExpression__Compile_0() Line 131  Unknown
    reproNative.exe!System_Linq_Expressions_System_Linq_Expressions_LambdaExpression__Compile() Line 112    Unknown
    reproNative.exe!repro_TypeTest_GenericNum_1<Int32>___ctor() Line 33 Unknown
    reproNative.exe!repro_TypeTest_Program__Main() Line 94  Unknown
    reproNative.exe!repro__Module___MainMethodWrapper() Unknown

We're failing to construct a Func<int, int, int> and Func<int, int, bool> delegate, but instead of throwing an exception, the reflection stack ends up using an incomplete delegate type (the one that was used in a cast in your repro) that is not suitable to be allocated.

You can work around by adding

new Func<int, int, int>((int x, int y) => x).ToString();
new Func<int, int, bool>((int x, int y) => false).ToString();

anywhere in your app.

As for the CoreRT fix, I think we'll want to filter the incomplete types in ExecutionEnvironmentImplementation.TryGetConstructedGenericTypeForComponents, but I need to check what .NET Native does here (it's possible it promotes incomplete generic types to complete generic types).