4

I have experienced Access Violation Exceptions and Fatal Execution Engine Error (it seems to be the same error) in managed code in C#. I have narrowed it down to the following snippet. Am I missing something? I thought it should not be possible to these kind of exceptions in managed code. I have seen it both in .net 4.7 and 4.5 and on multiple different computers? Is this a known issue in .Net?

    public static class FatalExecutionEngineBugExposer
{

    public static void Main(string[] args)
    {

        for (int i = 0; i < 500000; i++)
        {
            try
            {
                var testProblem = new TestProblem();
                testProblem.AddTrianglesExperiment();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.GetBaseException());
            }
        }
    }

    public class TestProblem
    {
        public struct Point
        {
            public double X, Y, Z;
        }

        public void AddTrianglesExperiment()
        {
            var points = new Point[28800];

            Parallel.ForEach(Partitioner.Create(0, points.Length),
                range =>
                {
                    // Create cache
                    var cache = new CubeRefCache();
                    cache.Entries = new CubeRefCacheEntry[20000];

                    // Add triangles
                    for (int i = range.Item1; i < range.Item2; i++)
                    {
                        ProcessTriangle(ref points[i].X, ref points[i].Y, ref points[i].Z, cache);  
                    }
                });
        }

        void ProcessTriangle(ref double pt1, ref double pt2, ref double pt3, CubeRefCache cache)
        {
            cache.Add(0, 0);
        }

        public struct CubeRefCacheEntry
        {
            public int Index;
            public int Item;
        }

        class CubeRefCache
        {
            internal CubeRefCacheEntry[] Entries;

            internal void Add(int index, int item)
            {
                Entries[0].Index = index;
                Entries[0].Item = item;
            }
        }
    }
}

This snippet "typically" fails within a minute.

UPDATE: It should probably be run in 64-bit. If compiled with 'Any CPU', the issue takes a lot longer to reproduce.

UPDATE2: usings I have:

usings

jptrxff
  • 69
  • 3
  • Your ProcessTriangle accesses the same entries every time is this intentional. please add code for Partitioner – Seabizkit Oct 30 '18 at 08:21
  • what is the reason for pass them as ref.... they are value types... – Seabizkit Oct 30 '18 at 08:29
  • @Seabizkit _"Your ProcessTriangle accesses the same entries..."_ - Are you talking about `cache`? it doesnt, he passes it in from a local variable inside the range processing scope –  Oct 30 '18 at 08:34
  • 1
    "Partitioner" is part of .NET framework, no need to add code for that. – Lasse V. Karlsen Oct 30 '18 at 08:34
  • 1
    If this code fails with a fatal execution engine exception then that, to me, looks like a bug somewhere in the .NET runtime. The code is contrived, sure, like the cache never adding anything, just storing everything in the first element, but if it reproduces a crash, that's good enough. I have, however, been running this code in LINQPad for at least 5 minutes now and it hasn't crashed yet, so I guess if it reliably crashes within a minute there has to be something else going on as well. – Lasse V. Karlsen Oct 30 '18 at 08:37
  • The code might seem contrived at the moment -> this is due to cutting out every piece of code that I could and still reproduce the issue. The 'ref' are necessary to reproduce the problem. In the original code it was not doubles but complex structs. – jptrxff Oct 30 '18 at 09:06
  • I am only using standard .net libraries: – jptrxff Oct 30 '18 at 09:07
  • 1
    It doesn't matter that this is a contrived example. There might, however, be parts of it that are unnecessary, and red herrings, when it comes to actually uncover what the real problem is (in the runtime) but none of that matters. If it is a bug in the runtime, and it looks to me as it is (assuming it *does* crash, as I said I couldn't reproduce it), then it ought to be fixed or at least be put on the bug list for prioritization. **Please don't complain that all the code that would make this code useful or elegant has been stripped out, that's what [mcve] means!** – Lasse V. Karlsen Oct 30 '18 at 09:58
  • I had to run in `x64` mode to reproduce the issue. Simply using `Any CPU` has not caused me to encounter the issue (yet). – MathiasStokholm Oct 30 '18 at 11:38
  • 1
    Can repro in 1m40s on x64. In fact, if I comment out the `public int Item;` and `Entries[0].Item = item;` it crashes immediately or within a few seconds. – CodeCaster Oct 30 '18 at 12:02
  • I have reported it here: https://github.com/dotnet/corefx/issues/33157 – jptrxff Oct 31 '18 at 08:56
  • It was apparently a bug in the clr, and is has been fixed in .net 4.8 – jptrxff Nov 01 '18 at 08:51
  • It is also said to be fixed in .NET Core 3.0, but it should also be fixed in 2.1, otherwise they cannot stamp "LTS" on that release if they don't bring this kind of bugfix to it (is my opinion). – Lasse V. Karlsen Nov 01 '18 at 09:24

1 Answers1

3

The original Github issue (https://github.com/dotnet/corefx/issues/33157) has been closed as a duplicate of https://github.com/dotnet/coreclr/issues/20690. Apparently, this is a known issue that has been fixed in .NET 4.8.

MathiasStokholm
  • 121
  • 2
  • 3
  • Damn, they've known about this horrible bug since March 11th and didn't fix it. Concretely this means you cannot write a method with more than 3 parameters if you expect it to run in 64-bit mode. 4 if it is static. The OP can work around it by omitting *ref* and passing the *cache* variable as the 1st parameter. Passing a Point instead is a workaround too. – Hans Passant Nov 01 '18 at 16:40