Skip to content

Unexpected IndexOutOfRangeException #119654

@dfederm

Description

@dfederm

Description

With .NET 10 I'm seeing an unexpected IndexOutOfRangeException. It doesn't repro in .NET 9 or with JIT optimizations disabled, so that hints to it being a JIT codegen issue.

Reproduction Steps

Unfortunately this isn't a "minimal" repro, but it doesn't require much to get set up.

TrackerExecutor.csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.BuildXL.Processes" Version="0.1.0-20241105.3" />
  </ItemGroup>
</Project>

Program.cs

// Copyright (C) Microsoft Corporation. All Rights Reserved.

using System;
using System.IO;
using System.Threading.Tasks;
using BuildXL.Processes;
using BuildXL.Utilities.Core;

namespace DBS.TrackerExecutor;

public static class Program
{
    public static async Task Main()
    {
        ////System.Diagnostics.Debugger.Launch();

        string tempDir = Path.Combine(Directory.GetCurrentDirectory(), ".tmp");
        if (Directory.Exists(tempDir))
        {
            Directory.Delete(tempDir, recursive: true);
        }

        await Parallel.ForAsync(0, 100, async (i, ct) =>
        {
            string workingDirectory = Path.Combine(tempDir, Guid.NewGuid().ToString());
            await RunInternalAsync(workingDirectory);
        });
    }

    private static async Task RunInternalAsync(string workingDirectory)
    {
        Directory.CreateDirectory(workingDirectory);

        var eventListener = new DetoursEventListener();
        eventListener.SetMessageHandlingFlags(MessageHandlingFlags.DebugMessageNotify | MessageHandlingFlags.FileAccessNotify | MessageHandlingFlags.ProcessDataNotify | MessageHandlingFlags.ProcessDetoursStatusNotify);

        var info = new SandboxedProcessInfo(
            fileStorage: null,
            fileName: @"C:\Program Files\dotnet\dotnet.exe",
            disableConHostSharing: false,
            detoursEventListener: eventListener,
            createJobObjectForCurrentProcess: true)
        {
            PipSemiStableHash = 0,
            PipDescription = "Process Invocation",
            Arguments = @"new console",
            WorkingDirectory = workingDirectory,
            EnvironmentVariables = BuildParameters.GetFactory().PopulateFromEnvironment(),

            // Don't buffer any output.
            MaxLengthInMemory = 0,
        };

        info.FileAccessManifest.FailUnexpectedFileAccesses = false;
        info.FileAccessManifest.MonitorChildProcesses = true;
        info.FileAccessManifest.IgnoreReparsePoints = true;
        info.FileAccessManifest.UseExtraThreadToDrainNtClose = false;
        info.FileAccessManifest.UseLargeNtClosePreallocatedList = true;
        info.FileAccessManifest.LogProcessData = true;
        info.FileAccessManifest.ReportProcessArgs = true;
        info.FileAccessManifest.NormalizeReadTimestamps = false;

        SandboxedProcessResult sandboxProcessResult;
        using SandboxedProcess process = await SandboxedProcess.StartAsync(info);
        sandboxProcessResult = await process.GetResultAsync();
    }

    private sealed class DetoursEventListener : IDetoursEventListener
    {
        public override void HandleFileAccess(FileAccessData fileAccessData)
        {
        }

        public override void HandleDebugMessage(DebugData debugData)
        {
        }

        public override void HandleProcessData(ProcessData processData)
        {
        }

        public override void HandleProcessDetouringStatus(ProcessDetouringStatusData data)
        {
        }
    }
}

NuGet.config:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<configuration>
  <packageSources>
    <clear />
    <add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
  </packageSources>
</configuration>

Run dotnet run and you should see something like (among ignorable irrelevant console spew):

OnUnhandledException called recursively
   at System.Environment.FailFast(System.Runtime.CompilerServices.StackCrawlMarkHandle, System.String, System.Runtime.CompilerServices.ObjectHandleOnStack, System.String)
   at System.Environment.FailFast(System.Threading.StackCrawlMark ByRef, System.String, System.Exception, System.String)
   at System.Environment.FailFast(System.String)
   at System.AppContext.OnUnhandledException(System.Object)
   at System.Runtime.EH.DispatchEx(System.Runtime.StackFrameIterator ByRef, ExInfo ByRef)
   at System.Runtime.EH.RhThrowEx(System.Object, ExInfo ByRef)
   at Internal.Runtime.CompilerHelpers.ThrowHelpers.ThrowIndexOutOfRangeException()
   at BuildXL.Native.Streams.OverlappedPool.ReserveOverlappedWithTarget(BuildXL.Native.Streams.IIOCompletionTarget)
   at BuildXL.Processes.Internal.AsyncPipeReader.BuildXL.Native.Streams.IIOCompletionTarget.OnCompletion(BuildXL.Native.Streams.FileAsyncIOResult)
   at System.Threading.QueueUserWorkItemCallbackDefaultContext.Execute()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart()
   at System.Threading.Thread.StartCallback()

Expected behavior

Not a crash

Actual behavior

Unhandled exception.

Regression?

Yes, this worked in .NET 9

Known Workarounds

No response

Configuration

No response

Other information

From looking at the disassembly, I believe it's a bounds check that's causing the exception, specifically this one below (despite what the debugger says):

Image

Metadata

Metadata

Assignees

Labels

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

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions