(Case 1264311): Disposing ReadCommand.Bufffer after AsyncReadManager.Read causes an AccessViolation

Edit: Updated the original post after more investigation to keep it concise. The test project is attached and reproduces the problem. Updated the thread title for the same reason.

Hi!

UnsafeUtility.Free in this example (based on the example in the documentation) does cause an Access Violation. How do I correctly free the memory of the (void*) Buffer of each ReadCommand after the read has completed?

[Test, Performance, Version("1")]
public unsafe void CrashIfRunMultipleTimes()
{
// Arrange
var p1    = Path.Combine(Application.dataPath, "StreamingAssets", "empty.txt");
var p2    = Path.Combine(Application.dataPath, "StreamingAssets", "nonempty.txt");
var p3    = Path.Combine(Application.dataPath, "StreamingAssets", "nonempty_more.txt");
var paths = new List<FixedString512> {p1, p2, p3};
var fs1   = new FileInfo(p1).Length;
var fs2   = new FileInfo(p2).Length;
var fs3   = new FileInfo(p3).Length;
var commands = new List<ReadCommand> {
                                       new ReadCommand {
                                                        Buffer =
                                                         UnsafeUtility.Malloc(fs1, 16, Allocator.Persistent),
                                                        Offset = 0,
                                                        Size   = fs1
                                                       },
                                       new ReadCommand {
                                                        Buffer =
                                                         UnsafeUtility.Malloc(fs2, 16, Allocator.Persistent),
                                                        Offset = 0,
                                                        Size   = fs2
                                                       },
                                       new ReadCommand {
                                                        Buffer =
                                                         UnsafeUtility.Malloc(fs3, 16, Allocator.Persistent),
                                                        Offset = 0,
                                                        Size   = fs3
                                                       }
                                      };
var naPaths       = new NativeArray<FixedString512>(paths.Count, Allocator.Persistent);
var naCommands    = new NativeArray<ReadCommand>(paths.Count, Allocator.Persistent);
var naReadHandles = new NativeArray<ReadHandle>(paths.Count, Allocator.Persistent);
naPaths.CopyFrom(paths.ToArray());
naCommands.CopyFrom(commands.ToArray());
var job = new ReadAsyncFileJob {Paths = naPaths, ReadCommands = naCommands, ReadHandles = naReadHandles};

// Act
var handle = job.Schedule(naPaths.Length, 64);
handle.Complete();

// Assert
// should not crash, but does when run multiple times

// Free memory
for (var i = 0; i < naCommands.Length; i++)
  UnsafeUtility.Free(naCommands[0].Buffer, Allocator.Persistent);

naPaths.Dispose();
naCommands.Dispose();
naReadHandles.Dispose();
}
(245c.52f4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
Unity!YGNodeFree+0x20:
00007ff7`61d4a8d0 488b8940040000  mov     rcx,qword ptr [rcx+440h] ds:808a8984`8d8d93cd=????????????????
0:091> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************


KEY_VALUES_STRING: 1

    Key  : AV.Fault
    Value: Read

    Key  : Analysis.CPU.mSec
    Value: 10562

    Key  : Analysis.DebugAnalysisProvider.CPP
    Value: Create: 8007007e on DRAGONMASTER

    Key  : Analysis.DebugData
    Value: CreateObject

    Key  : Analysis.DebugModel
    Value: CreateObject

    Key  : Analysis.Elapsed.mSec
    Value: 18528

    Key  : Analysis.Memory.CommitPeak.Mb
    Value: 565

    Key  : Analysis.System
    Value: CreateObject

    Key  : Timeline.OS.Boot.DeltaSec
    Value: 300484

    Key  : Timeline.Process.Start.DeltaSec
    Value: 807

    Key  : WER.OS.Branch
    Value: rs4_release

    Key  : WER.OS.Timestamp
    Value: 2018-04-10T18:04:00Z

    Key  : WER.OS.Version
    Value: 10.0.17134.1

    Key  : WER.Process.Version
    Value: 2020.1.0.59919


ADDITIONAL_XML: 1

OS_BUILD_LAYERS: 1

NTGLOBALFLAG:  0

PROCESS_BAM_CURRENT_THROTTLED: 0

PROCESS_BAM_PREVIOUS_THROTTLED: 0

APPLICATION_VERIFIER_FLAGS:  0

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 00007ff761d4a8d0 (Unity!YGNodeFree+0x0000000000000020)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 0000000000000000
   Parameter[1]: ffffffffffffffff
Attempt to read from address ffffffffffffffff

FAULTING_THREAD:  000052f4

PROCESS_NAME:  Unity.exe

READ_ADDRESS:  ffffffffffffffff

ERROR_CODE: (NTSTATUS) 0xc0000005 - Die Anweisung in 0x%p verwies auf Arbeitsspeicher bei 0x%p. Der Vorgang %s konnte im Arbeitsspeicher nicht durchgef hrt werden.

EXCEPTION_CODE_STR:  c0000005

EXCEPTION_PARAMETER1:  0000000000000000

EXCEPTION_PARAMETER2:  ffffffffffffffff

STACK_TEXT:
00000074`bb36f3f0 00007ff7`61f82af6     : 0000021f`9a124ae0 0000021f`9a124ae0 00000000`00000017 00007ff7`62f6d379 : Unity!YGNodeFree+0x20
00000074`bb36f420 00000221`1cc97f9e     : 00007ff7`6696bd38 00000220`b84776d0 0000021f`311eb0b0 00000074`bb36f530 : Unity!Native_CUSTOM_YGNodeFreeInternal+0x56
00000074`bb36f460 00000221`1cc97e2b     : 00007fff`3112cd78 00007fff`3112cd70 00000220`c9c3a3a0 00000221`1cc97853 : 0x00000221`1cc97f9e
00000074`bb36f4f0 00000221`1cc97bbb     : 00000000`ffffffff 00000000`00000000 00000000`00000000 00000000`00000001 : 0x00000221`1cc97e2b
00000074`bb36f550 00000220`b847783c     : 00000220`4c7e2d20 00000220`cc2b8780 00000000`00000000 00000000`00000000 : 0x00000221`1cc97bbb
00000074`bb36f5c0 00007fff`30da4239     : 00000220`cc2b8780 00000074`bb36f6a0 00000220`00000000 0000021f`50038038 : 0x00000220`b847783c
00000074`bb36f670 00007fff`30f9de32     : 00000000`00000000 00000220`cbc77b40 00000000`00000000 00000220`cbc77c00 : mono_2_0_bdwgc!mono_gc_run_finalize+0x389
00000074`bb36f730 00007fff`30da3139     : 00000000`00000000 00000220`4c7ead10 00000000`00000000 00000000`00000000 : mono_2_0_bdwgc!GC_invoke_finalizers+0xaa
00000074`bb36f760 00007fff`30d66368     : 00000220`4c4988f0 00000000`00000000 00000000`00000000 00000220`4c7e2d20 : mono_2_0_bdwgc!finalizer_thread+0x1a9
00000074`bb36f820 00007fff`30d660f6     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : mono_2_0_bdwgc!start_wrapper_internal+0x248
00000074`bb36f8f0 00007fff`a56b4034     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : mono_2_0_bdwgc!start_wrapper+0x46
00000074`bb36f920 00007fff`a5823691     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
00000074`bb36f950 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21


SYMBOL_NAME:  Unity!YGNodeFree+20

MODULE_NAME: Unity

IMAGE_NAME:  Unity.exe

STACK_COMMAND:  ~91s ; .cxr ; kb

FAILURE_BUCKET_ID:  INVALID_POINTER_READ_c0000005_Unity.exe!YGNodeFree

OS_VERSION:  10.0.17134.1

BUILDLAB_STR:  rs4_release

OSPLATFORM_TYPE:  x64

OSNAME:  Windows 10

IMAGE_VERSION:  2020.1.0.59919

FAILURE_ID_HASH:  {90a83abd-89db-bfa7-c5d2-da7b8e3cb8eb}

Followup:     MachineOwner
---------

Edit:

  • ReadHandle.Dispose is missing in the examples - as they only dispose NativeArray but not the handles themself, but adding it does not fix it.

Reproduce
Open the attached project and run the single included Performance test “CrashTest” (multiple times).

6106626–664704–AsyncReadManager.Crash.zip (31.9 KB)

And I am clearly blind already.

UnsafeUtility.Free(cmds[0].Buffer, Allocator.Persistent);

when it clearly should be

UnsafeUtility.Free(cmds[i].Buffer, Allocator.Persistent);

The error did exactly tell me what was wrong and I did try all combinations when it was in plain sight…