Skip to content

Interlocked.Read takes readonly ref despite requiring write access which causes crashes #11658

@leander-behr-blag

Description

@leander-behr-blag

Description

Interlocked.Read uses CompareExchange to read a value atomically and with memory ordering guarantees. It takes a readonly ref, which seems to make sense given that it never changes the value.

One could very reasonably use Interlocked.Read to read from a shared memory view with read only access. This causes a crash however because CompareExchange actually does write to the address.

Reproduction Steps

Open a shared memory block with read only access and try to read from it with Interlocked.Read.

I haven't written any C# in a long time so excuse oddities in the example.

using System.IO.MemoryMappedFiles;

Console.WriteLine("Hello, World!");

// Create or open a memory-mapped file
using var memoryMappedFile = MemoryMappedFile.CreateOrOpen("TestFile42", 100, MemoryMappedFileAccess.Read);

// Create a view accessor to access the memory-mapped file
using var accessor = memoryMappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read);


// Function to get a reference to the n-th long in the memory-mapped file
unsafe ref long GetNthLong(MemoryMappedViewAccessor accessor, int n)
{
    if (n < 0 || n * sizeof(long) >= accessor.Capacity)
    {
        throw new ArgumentOutOfRangeException(nameof(n), "Index is out of bounds.");
    }

    byte* pointer = null;
    accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer);
    try
    {
        return ref *(long*)(pointer + n * sizeof(long));
    }
    finally
    {
        accessor.SafeMemoryMappedViewHandle.ReleasePointer();
    }
}

ref readonly long firstLong = ref GetNthLong(accessor, 0); // Get the first long (n = 0)

long readValue = Interlocked.Read(in firstLong); // Atomic read ==> AccessViolationException
//long readValue = firstLong; // Normal read (not atomic)

Console.WriteLine($"first long: {readValue}");

Expected behavior

The function should not advertise itself as non-modifying or should somehow be implemented to actually not require write access.

Actual behavior

The function takes a readonly ref but requires write access to the memory.

Regression?

No response

Known Workarounds

No response

Configuration

I tried this in a .NET 9 console app on Win11 23h2 on x86.

Other information

No response

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions