Repository navigation
[release/10.0.1xx] [native] Fix race publishing assembly store runtime data - #13261
Closed
filipnavara wants to merge 1 commit into
Closed
filipnavara wants to merge 1 commit into
filipnavara wants to merge 1 commit into
Conversation
filipnavara
marked this pull request as ready for review
October 26, 2026 14:03
filipnavara
requested review from
jonathanpeppers and
simonrozsival
as code owners
October 26, 2026 14:03
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Both loaders still have plain field reads that can race with atomic writes from another in-flight initializer.
1 open issue
What changed in this PR
This release-branch fix addresses assembly-store initialization races in MonoVM and CoreCLR reported in #13194. The separate non-store race in #13065 remains outside scope.
Changes:
- Store descriptor and debug-information pointers before publishing
image_data. - Use release/acquire operations to order publication and readiness checks in both loaders.
| File | Description |
|---|---|
| src/native/mono/monodroid/embedded-assemblies.cc | Adds ordered publication to MonoVM assembly-store initialization. |
| src/native/clr/host/assembly-store.cc | Applies the same publication ordering to CoreCLR. |
Review effort: Balanced
Fixes: dotnet#13194 When two threads load the same assembly from the assembly store for the first time, both could enter the initialization block in `open_from_bundles` (Mono) / `AssemblyStore::open_assembly` (CoreCLR). The fields were written with plain stores, which on weakly ordered CPUs (e.g. arm64) can become visible to other threads in any order. A thread could therefore observe `image_data != nullptr`, skip initialization and dereference a still-NULL `descriptor`, crashing with SIGSEGV at fault address 0x8. Compute `image_data`, `debug_info_data` and the descriptor locally from the assembly store entry and use only those local values after the readiness check, so the loaders never read the shared fields back while another thread may still be writing them. The shared fields are written atomically, with `image_data` published last using release semantics and checked with acquire semantics.
filipnavara
force-pushed
the
dev/fix-assembly-store-race-10
branch
from
October 26, 2026 14:19
379d88d to
6f66634
Compare
2 of 3 tasks
Member
Author
|
Closing in favor of #13264 approach |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Fixes: #13194
Context: #13065
When two threads load the same assembly from the assembly store for the
first time, both can enter the initialization block in
EmbeddedAssemblies::assembly_store_open_from_bundles()(MonoVM) andAssemblyStore::open_assembly()(CoreCLR). That block wroteimage_data,descriptoranddebug_info_datawith plain stores, andother threads checked
image_data != nullptrwith a plain load to decidewhether the entry was ready.
On weakly ordered CPUs such as arm64, those stores can become visible to
another core in any order, and the reader's later loads aren't ordered
after its
image_datacheck. A second thread can therefore seeimage_dataset, skip initialization, and readdescriptor->data_sizewhile
descriptoris still NULL:The issue reports this from the field (about 3,200 users on
.NET for Android 36.1.69, Release, arm64). It was also reproduced
deterministically by stopping a thread under a debugger at the point
where it can see
image_dataset but notdescriptor.Fix: compute
image_data,debug_info_dataand the descriptor(
&store_entry) locally from the assembly store entry, and have theloader use only those local values. It no longer reads them back from
the shared
AssemblyStoreSingleAssemblyRuntimeData, so it can't race withanother thread that is still initializing the same entry. The shared
fields are still filled in:
descriptoranddebug_info_datawithrelaxed atomic stores, then
image_datalast with__ATOMIC_RELEASE.The readiness check reads
image_datawith__ATOMIC_ACQUIRE. In thearm64 release build the check compiles to
ldarand the publish tostlr.To support this, the MonoVM
get_assembly_data()overload that tookthe runtime data struct is removed (the existing pointer-and-size
overload is used instead). The CoreCLR
get_assembly_data()now takesthe image pointer and size directly.
mainisn't affected: the MonoVM host was removed in #12868, and #12972changed CoreCLR to fill in this data while the assembly store is set up,
before any lookups can run.
This doesn't address #13065, which is a separate race in
map_runtime_file()on the individual-assembly (non-store) path.There's no automated test: the race window is a few instructions wide
and depends on timing.