runtime: AccessViolationException when retrieving enum from dependency container
When trying to run our app on .NET 5 preview4 we ran into the following issue. Consider the following simplified code:
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace Progress.WebApp
{
public enum TheEnum
{
HelloWorld = -1,
}
public sealed class Startup
{
static void Main(string[] args)
=> WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build()
.Run();
public void ConfigureServices(IServiceCollection services)
=> services.AddSingleton(typeof(TheEnum), TheEnum.HelloWorld);
public void Configure(IApplicationBuilder app)
=> app.UseMiddleware<Middleware>();
}
public sealed class Middleware
{
readonly RequestDelegate next;
public Middleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context, TheEnum theEnum)
=> await context.Response.WriteAsync(theEnum.ToString());
}
}
Project:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>
Running this code using dotnet run
and navigating to localhost:5000
prints “HelloWorld” the first time as expected, but after hitting F5 a few times the server crashes with the following exception:
Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at DynamicClass.lambda_method1(System.Runtime.CompilerServices.Closure, System.Object, Microsoft.AspNetCore.Http.HttpContext, System.IServiceProvider)
at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions+<>c__DisplayClass4_1.<UseMiddleware>b__2(Microsoft.AspNetCore.Http.HttpContext)
at Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware.Invoke(Microsoft.AspNetCore.Http.HttpContext)
at Microsoft.AspNetCore.Hosting.HostingApplication.ProcessRequestAsync(Context)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol+<ProcessRequest>d__218`1[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[[Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol+<ProcessRequest>d__218`1[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Microsoft.AspNetCore.Server.Kestrel.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]](<ProcessRequest>d__218`1<System.__Canon> ByRef)
at System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder.Start[[Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol+<ProcessRequest>d__218`1[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Microsoft.AspNetCore.Server.Kestrel.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]](<ProcessRequest>d__218`1<System.__Canon> ByRef)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequest[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](Microsoft.AspNetCore.Hosting.Server.IHttpApplication`1<System.__Canon>)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol+<ProcessRequests>d__217`1[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext()
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol+<ProcessRequests>d__217`1[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Microsoft.AspNetCore.Server.Kestrel.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].ExecutionContextCallback(System.Object)
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(System.Threading.Thread, System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol+<ProcessRequests>d__217`1[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Microsoft.AspNetCore.Server.Kestrel.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].MoveNext(System.Threading.Thread)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol+<ProcessRequests>d__217`1[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Microsoft.AspNetCore.Server.Kestrel.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].ExecuteFromThreadPool(System.Threading.Thread)
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
Changing the underlying enum value to 1 (from -1) causes the server to no longer crash, but instead a NullReferenceException is thrown during request processing (despite the first few request still working as expected):
Connection id "0HLVSO0VBBCEI", Request id "0HLVSO0VBBCEI:00000004": An unhandled exception was thrown by the application.
System.NullReferenceException: Object reference not set to an instance of an object.
at lambda_method1(Closure , Object , HttpContext , IServiceProvider )
at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass4_1.<UseMiddleware>b__2(HttpContext context)
at Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Hosting.HostingApplication.ProcessRequestAsync(Context context)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequest[TContext](IHttpApplication`1 application)
Output dotnet --info
.NET SDK (reflecting any global.json):
Version: 5.0.100-preview.4.20258.7
Commit: 65f0fc2cad
Runtime Environment:
OS Name: Windows
OS Version: 10.0.19041
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\5.0.100-preview.4.20258.7\
Host (useful for support):
Version: 5.0.0-preview.4.20251.6
Commit: 47ec733ba7
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 21 (20 by maintainers)
Commits related to this issue
- AccessViolation when using ValueTypes as a Service When using ValueTypes as services in DependencyInjection, we are generating incorrect IL in a few cases: - When the ValueType is a top level servic... — committed to eerhardt/runtime by eerhardt 4 years ago
- AccessViolation when using ValueTypes as a Service (#42152) * AccessViolation when using ValueTypes as a Service When using ValueTypes as services in DependencyInjection, we are generating incorre... — committed to dotnet/runtime by eerhardt 4 years ago
- AccessViolation when using ValueTypes as a Service When using ValueTypes as services in DependencyInjection, we are generating incorrect IL in a few cases: - When the ValueType is a top level servic... — committed to dotnet/runtime by eerhardt 4 years ago
- [release/5.0-rc2] AccessViolation when using ValueTypes as a Service (#42213) * AccessViolation when using ValueTypes as a Service When using ValueTypes as services in DependencyInjection, we are ... — committed to dotnet/runtime by github-actions[bot] 4 years ago
So more things I’ve learned:
https://github.com/dotnet/runtime/blob/b1ca1f4eb040e42045718e265775f79eea982d32/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs#L23-L28
The first 2 times a service gets retrieved, it uses reflection (
RuntimeResolver.Resolve
) to get the service. But once it is called a 2nd time, it calls the base.RealizeService in the background, which generates the IL and sticks it in the cache. So the next time it is retrieved, the generated IL is invoked.The reason the A/V is happening is because
0xFFFFFFFF
is being returned as anobject
fromServiceProvider.GetService
. This “object” is then trying to be cast down/unboxed to aTheEnum
value.0xFFFFFFFF
is-1
, which is theHelloWorld
value. So what is happening is a double unboxing of the enum, causing the runtime to A/V.The reason the double unboxing is occuring is what @ericstj pointed to above - https://github.com/dotnet/extensions/commit/b248ede5665ef0ba59252731a40f697cc4ac40ad that change added an unbox call during VisitConstant. The bug that was fixing was when a constant ValueType was being passed into a constructor of a service. In this scenario, the constant ValueType is the service itself. In this scenario, we shouldn’t be unboxing the value during VisitConstant.
To fix this, I think we can simply set a
bool
duringVisitConstructor
saying that we areinConstructor
. And only when we areinConstructor
duringVisitConstant
do we do the unboxing. When we are visiting a constant at the top-level, skip adding the unboxing code.I’ll prepare a fix for this.
I looked through as well and found that
https://github.com/dotnet/runtime/blob/b1ca1f4eb040e42045718e265775f79eea982d32/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs#L217-L236
will call it as well. Both the Ctor and the IEnumerable code paths will need the value unboxed, from what I can tell. I’ve added a test for IEnumerable as well.