Skip to content

JIT bug / GC relocating pinned arrays #9980

@rgr21

Description

@rgr21

The code below I believe should be stable and run forever, but in practice crashes with AccessViolationException very quickly when built with VS15.5 and running with anything up to and including 4.7.2 preview.
VS 15.4 and earlier generate different IL which doesn't trigger the crash.

I believe that dotnet/roslyn#20548 was the change in Roslyn that introduced the different IL. If my understanding is correct, the new IL is still "correct", but sadly doesn't work in the real world.

Believe the bug is outstanding in dotnet core 2.0.6 and 2.1.0-preview1. (I had hopes that https://github.com/dotnet/coreclr/pull/15706/files might resolve, but that is in 2.1.0-preview).

The test case below is obviously artificial, but switch out the NativeMethod(double*) for any PInvoke call to a maths library, and it becomes a bit of a disaster for me.

<useLegacyJit enabled="1" /> appears to mitigate the issue.

Old/good IL:

.method private hidebysig static float64 
          UnsafeMethod_UsingFixed(int32 n,
                                  float64[] a) cil managed
  {
    // Code size       36 (0x24)
    .maxstack  2
    .locals init ([0] float64& pinned pa,
             [1] float64[] V_1)​

New/bad IL:

.method private hidebysig static float64 
          UnsafeMethod_UsingFixed(int32 n,
                                  float64[] a) cil managed
  {
    // Code size       35 (0x23)
    .maxstack  2
    .locals init ([0] float64* pa,
             [1] float64[] pinned V_1)​

Minimal repro case

using System.Collections.Generic;
using System.Threading.Tasks;

namespace RyuJitFixedBug
{
    class Program
    {
        static void Main(string[] args)
        {
            Task.Run(() => WasteMemory());

            while (true)
            {
                var n = 8000;
                var a = new double[n];
                UnsafeMethod_UsingFixed(n, a);
            }
        }

        private static void WasteMemory()
        {
            int allocated = 0;
            var list = new List<byte[]>();
            while (true)
            {
                var b = new byte[80000];
                allocated += b.Length;
                list.Add(b);
                if (allocated >= 100_000_000)
                {
                    allocated = 0;
                    list.Clear();
                }
            }
        }

        private static unsafe double UnsafeMethod_UsingFixed(int n, double[] a)
        {
            double info;
            fixed (double* pa = a) // Fix the array - shouldn't be able to relocate now
            {
                info = NativeMethod(n, pa);
            }
            return info;
        }

        private static unsafe double NativeMethod(long n, double* a) // Pretend this is actually a call to a native library, MKL say. This is the real usecase.
        {
            var s = 0d;
            for (long i = 0; i < n; ++i)
            {
                s += a[i]; // Will segv if array has relocated (Or might just give random numbers. Depends what has been done with the memory we were pointing at?).
            }
            return s;
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIbug

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions