Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Illegal memory double mapping on Apple Silicon using Rosseta emulation #106819

Closed
Yukon opened this issue Aug 22, 2024 · 7 comments
Closed

Illegal memory double mapping on Apple Silicon using Rosseta emulation #106819

Yukon opened this issue Aug 22, 2024 · 7 comments

Comments

@Yukon
Copy link

Yukon commented Aug 22, 2024

Description

As part of adding W^X (write xor execute) support #54954 memory double mapping was implemented that is unsupported by Apple Silicon, including emulating AMD64 using Rosseta. A patch to fix this #70912 was incomplete and results in memory related crashes running .NET 8 as a result of feature being enabled by default.

Reproduction Steps

Importantly, to reproduce requires a Mac with Apple Silicon and Docker Desktop for Mac with "Use Rosetta for x86_64/amd64 emulation on Apple Silicon" enabled (or any other containerization of Linux AMD64 using Rosetta).

A complete reproduction can be found at https://github.com/Yukon/Dotnet-WriteXorExecuteCrash that includes a Dockerfile to quickly reproduce.

A simple reproduction is inserting into a ConcurrentDictionary<string, bool>.

var random = new Random();
var stringDict  = new ConcurrentDictionary<string, bool>();
while (true)
{
    stringDict.TryAdd(random.Next(0xFFFF).ToString(), true);
}

Expected behavior

Application executes without throwing exceptions or crashing.

Actual behavior

For the complete reproduction provided the application with either segfault with exit code 139 or will throw:

Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at System.Collections.Concurrent.ConcurrentDictionary`2[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TryAddInternal(Tables<System.__Canon,Boolean>, System.__Canon, System.Nullable`1<Int32>, Boolean, Boolean, Boolean, Boolean ByRef)
   at Program+<>c__DisplayClass0_0.<<Main>$>b__0()
   at System.Threading.Tasks.Task`1[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].InnerInvoke()
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(System.Threading.Thread, System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef, System.Threading.Thread)
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart()

Additional symptoms related to this issue and others (#97828 , #100539),

dotnet restore hangs

When running dotnet restore the process will hang and never complete.

assertion failed [block != nullptr]

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)

Regression?

Regressed in .NET 7 when W^X was enabled by default #69672.

Known Workarounds

Disabling W^X stops all double mapping from occurring.

bool ExecutableAllocator::IsDoubleMappingEnabled()
{
LIMITED_METHOD_CONTRACT;
#if defined(HOST_OSX) && defined(HOST_ARM64)
return false;
#else
return g_isWXorXEnabled;
#endif
}

Set the environment variable DOTNET_EnableWriteXorExecute to 0 before running the application.

Configuration

  • .NET 8
  • Apple Silicon (Mac ARM64) emulating Linux AMD64 using Rosseta 2.
  • Docker Engine 27.1.1 with Rosseta emulation enabled.

Other information

Double mapping is not supported in Apple Silicon using Rosetta, which was intended to be fixed in #70912. 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.

@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Aug 22, 2024
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Aug 22, 2024
@filipnavara
Copy link
Member

This was already fixed in .NET 9 by #102509 and fixed in .NET 8 servicing. Am I missing something?

@Yukon
Copy link
Author

Yukon commented Aug 22, 2024

Missed that specific fix in my research, looking at the change log it appears the premises described in other information is still accurate as the IsProcessTranslated was replaced with minipal_detect_rosetta.

The runtime/src/coreclr/utilcode/executableallocator.cpp does not check if rosseta is being used when determining if double mapping is enabled. There appears to be race condition with the check to disable W^X as the repo reliability crashes until explicitly disabling the feature with env flag.

if (IsDoubleMappingEnabled())
{
if (!VMToOSInterface::CreateDoubleMemoryMapper(&m_doubleMemoryMapperHandle, &m_maxExecutableCodeSize))
{
g_isWXorXEnabled = false;
return true;

@vcsjones vcsjones added area-VM-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Aug 22, 2024
Copy link
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

@Yukon
Copy link
Author

Yukon commented Aug 22, 2024

Is the .NET 8 backport in the current LTS or an upcoming one? I could test a build with #102509.

@filipnavara
Copy link
Member

The 8.0 backport was only merged last week, so it’s not part of 8.0.8 release yet.

@mangod9
Copy link
Member

mangod9 commented Aug 22, 2024

correct should be included in 8.0.9. Can you try your scenario on .NET 9 to ensure its the same issue?

@Yukon
Copy link
Author

Yukon commented Aug 22, 2024

Running with the nightly .NET 9 I can confirm the issue has been fixed by #102509. Thank you all for your time and appreciate the backport!

@Yukon Yukon closed this as completed Aug 22, 2024
@dotnet-policy-service dotnet-policy-service bot removed the untriaged New issue has not been triaged by the area owner label Aug 22, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Sep 22, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants