Update: A fix has be implimented and is being backported to .NET 8.0.9. dotnet/runtime#106819
his is a simple project to reproduce a crash on .NET 8 running in AMD64 container on Docker using Rosseta 2 on Apple Silicon.
The crash is caused by a new runtime feature Write xor Execute that is enabled by default in .NET 7 and subsequently .NET 8.
To workaround this issue, set the environment variable DOTNET_EnableWriteXorExecute
to 0
before running the application. This can be done using the Dockerfile like used in this example or numerous other ways.
To reproduce this issue a Mac with Apple Silicon is required and Docker Desktop for Mac with "Use Rosetta for x86_64/amd64 emulation on Apple Silicon" enabled.
- Clone this repository
- Run
docker build -t net8-amd64-crash .
- Run
docker run net8-amd64-crash
Note: Warning about "requested image's platform (linux/amd64) [...]" is expected. - The program will either report a "Fatal error. System.AccessViolationException" or segfault in which case there will be no "Exiting" message and will have an exit code of 139.
The symptoms of this issue are widespread and exibit themselves in many different ways. Below are some examples that have been resolved by disabling W^E.
When running dotnet restore
the process will hang and never complete.
Application will throw "Fatal error. System.AccessViolationException".
Application will segfault and exit with code 139.
Application outputs to console and crashes with exit code 133.
assertion failed [block != nullptr]: BasicBlock requested for unrecognized address
(BuilderBase.h:550 block_for_offset)
There are two allocator features in the coreclr runtime that check g_isWXorXEnabled
to enable specific functionality based on the DOTNET_EnableWriteXorExecute
environment variable.
Most intrestingly is ExecutableAllocator::IsDoubleMappingEnabled
has a check #if defined(HOST_OSX) && defined(HOST_ARM64)
which forces the double mapping to be disabled otherwise falling back to g_isWXorXEnabled
. Double mapping is not supported in Apple Silicon using Rosetta, which was intended to be fixed. This is not completely the case though as the emulation check occurs in the underlying doublemapping.cpp
not in the allocator that has the IsDoubleMappingEnabled
check. This gap results in double mapping being implied it is enabled resulting in inappropriate memory calls in the allocator as well as double mapping methods without the IsProcessTranslated
guard clause. By disabling W^E the IsDoubleMappingEnabled
method always returns false, preventing any double mapping from occurring.
The other enabled check ExecutableAllocator::IsWXORXEnabled
is less intresting in that on Apple Silicon W^E is a requirment and thus is always enabled. Thereby having this specific funcitonality enabled in a virtualized environment would be supported and only enhance security at the cost of some performance.