Skip to content

Commit 7ee35b9

Browse files
github-actions[bot]vseanreesermsftlateralusXlambdageek
authored
[release/7.0] [Mono] Race in init_method when using LLVM AOT. (#93006)
Backport of #75088 to release/7.0-staging Fixes #81211 ## Customer Impact Customers targeting Apple platforms using LLVM AOT codegen (the default) in highly concurrent settings (such as firing off multiple simultaneous async HTTP requests) may experience unexpected behavior such as InvalidCastExceptions, NullReferenceExceptions or crashes. ## Testing Manual testing ## Risk Low. This code has been running on .NET 8 `main` for over a year in CI, as well as on some other non-mobile platforms --- * [Mono] Race in init_method when using LLVM AOT. When using LLVM AOT codegen, init_method updates two GOT slots. These slots are initialized as part of init_method, but there is a race between initialization of the two slots. Current implementation can have two threads running init_method for the same method, but as soon as: [got_slots [pindex]] = addr store is visible, it will trigger other threads to return back from init_method, but since that could happen before the corresponding LLVM AOT const slot is set, second thread will return to method calling init_method, load the LLVM aot const, and crash when trying to use it (since its still NULL). This crash is very rare but have been identified on x86/x64 CPU's, when one thread is either preempted between updating regular GOT slot and LLVM GOT slot or store into LLVM GOT slot gets delayed in store buffer. I have also been able to emulate the scenario in debugger, triggering the issue and crashing in the method loading from LLVM aot const slot. Fix change order of updates and make sure the update of LLVM aot const slot happens before memory_barrier, since: got [got_slots [pindex]] = addr; have release semantics in relation to addr and update of LLVM aot const slot. Fix also add acquire/release semantics for ji->type in init_method since it is used to guard if a thread ignores a patch or not and it should not be re-ordered with previous stores, since it can cause similar race conditions with updated slots. * Move register_jump_target_got_slot above mono_memory_barrier. * revert unintentional branding change --------- Co-authored-by: vseanreesermsft <[email protected]> Co-authored-by: lateralusX <[email protected]> Co-authored-by: Aleksey Kliger <[email protected]>
1 parent 83ec4e3 commit 7ee35b9

File tree

1 file changed

+14
-10
lines changed

1 file changed

+14
-10
lines changed

src/mono/mono/mini/aot-runtime.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4633,10 +4633,13 @@ init_method (MonoAotModule *amodule, gpointer info, guint32 method_index, MonoMe
46334633
* been initialized by load_method () for a static cctor before the cctor has
46344634
* finished executing (#23242).
46354635
*/
4636-
if (ji->type == MONO_PATCH_INFO_NONE) {
4637-
} else if (!got [got_slots [pindex]] || ji->type == MONO_PATCH_INFO_SFLDA) {
4636+
MonoJumpInfoType ji_type = ji->type;
4637+
LOAD_ACQUIRE_FENCE;
4638+
4639+
if (ji_type == MONO_PATCH_INFO_NONE) {
4640+
} else if (!got [got_slots [pindex]] || ji_type == MONO_PATCH_INFO_SFLDA) {
46384641
/* In llvm-only made, we might encounter shared methods */
4639-
if (mono_llvm_only && ji->type == MONO_PATCH_INFO_METHOD && mono_method_check_context_used (ji->data.method)) {
4642+
if (mono_llvm_only && ji_type == MONO_PATCH_INFO_METHOD && mono_method_check_context_used (ji->data.method)) {
46404643
g_assert (context);
46414644
ji->data.method = mono_class_inflate_generic_method_checked (ji->data.method, context, error);
46424645
if (!is_ok (error)) {
@@ -4646,10 +4649,10 @@ init_method (MonoAotModule *amodule, gpointer info, guint32 method_index, MonoMe
46464649
}
46474650
}
46484651
/* This cannot be resolved in mono_resolve_patch_target () */
4649-
if (ji->type == MONO_PATCH_INFO_AOT_JIT_INFO) {
4652+
if (ji_type == MONO_PATCH_INFO_AOT_JIT_INFO) {
46504653
// FIXME: Lookup using the index
46514654
jinfo = mono_aot_find_jit_info (amodule->assembly->image, code);
4652-
ji->type = MONO_PATCH_INFO_ABS;
4655+
ji->type = ji_type = MONO_PATCH_INFO_ABS;
46534656
ji->data.target = jinfo;
46544657
}
46554658
addr = mono_resolve_patch_target (method, code, ji, TRUE, error);
@@ -4658,18 +4661,19 @@ init_method (MonoAotModule *amodule, gpointer info, guint32 method_index, MonoMe
46584661
mono_mempool_destroy (mp);
46594662
return FALSE;
46604663
}
4661-
if (ji->type == MONO_PATCH_INFO_METHOD_JUMP)
4664+
if (ji_type == MONO_PATCH_INFO_METHOD_JUMP) {
46624665
addr = mono_create_ftnptr (addr);
4663-
mono_memory_barrier ();
4664-
got [got_slots [pindex]] = addr;
4665-
if (ji->type == MONO_PATCH_INFO_METHOD_JUMP)
46664666
register_jump_target_got_slot (ji->data.method, &(got [got_slots [pindex]]));
4667-
4667+
}
46684668
if (llvm) {
46694669
void (*init_aotconst) (int, gpointer) = (void (*)(int, gpointer))amodule->info.llvm_init_aotconst;
46704670
init_aotconst (got_slots [pindex], addr);
46714671
}
4672+
mono_memory_barrier ();
4673+
got [got_slots [pindex]] = addr;
46724674
}
4675+
4676+
STORE_RELEASE_FENCE;
46734677
ji->type = MONO_PATCH_INFO_NONE;
46744678
}
46754679

0 commit comments

Comments
 (0)