From ff6b949f108f9219838e7522b1152f5cbb54dea1 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 10 Apr 2024 09:43:10 +0300 Subject: [PATCH] [mono][interp] Keep delegate alive during invocation (#100832) When invoking a delegate, we were overwritting the stack slot containing the delegate object reference. In the case of invoking a delegate for a dynamic method, we were running into issues when the delegate object is collected while the method is executed because the method code is also discarded. --- src/mono/mono/mini/interp/interp-internals.h | 1 + src/mono/mono/mini/interp/interp.c | 3 +++ src/mono/mono/mini/interp/transform.c | 21 ++++++++++++++++++++ src/mono/mono/mini/interp/transform.h | 1 + 4 files changed, 26 insertions(+) diff --git a/src/mono/mono/mini/interp/interp-internals.h b/src/mono/mono/mini/interp/interp-internals.h index 4e0be7db04334..79b93dae1fb5f 100644 --- a/src/mono/mono/mini/interp/interp-internals.h +++ b/src/mono/mono/mini/interp/interp-internals.h @@ -153,6 +153,7 @@ struct InterpMethod { unsigned int hasthis; // boolean MonoProfilerCallInstrumentationFlags prof_flags; InterpMethodCodeType code_type; + int ref_slot_offset; // GC visible pointer slot #ifdef ENABLE_EXPERIMENT_TIERED MiniTieredCounter tiered_counter; #endif diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 620dc135d50f2..2942fb23d3f82 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -4047,6 +4047,9 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause } cmethod = del_imethod; if (!is_multicast) { + int ref_slot_offset = frame->imethod->ref_slot_offset; + if (ref_slot_offset >= 0) + LOCAL_VAR (ref_slot_offset, gpointer) = del; if (cmethod->param_count == param_count + 1) { // Target method is static but the delegate has a target object. We handle // this separately from the case below, because, for these calls, the instance diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 0d5ef5380de26..0150c3d32e3e0 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -412,6 +412,17 @@ create_interp_dummy_var (TransformData *td) td->locals [td->dummy_var].flags = INTERP_LOCAL_FLAG_GLOBAL; } +static int alloc_global_var_offset (TransformData *td, int var); + +static void +interp_create_ref_handle_var (TransformData *td) +{ + int var = create_interp_local_explicit (td, m_class_get_byval_arg (mono_defaults.int_class), sizeof (gpointer)); + td->locals [var].flags = INTERP_LOCAL_FLAG_GLOBAL; + alloc_global_var_offset (td, var); + td->ref_handle_var = var; +} + static int get_tos_offset (TransformData *td) { @@ -3798,6 +3809,10 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target td->last_ins->data [0] = get_data_item_index_imethod (td, mono_interp_get_imethod (target_method)); } else { if (is_delegate_invoke) { + // MINT_CALL_DELEGATE will store the delegate object into this slot so it is kept alive + // while the method is invoked + if (td->ref_handle_var == -1) + interp_create_ref_handle_var (td); interp_add_ins (td, MINT_CALL_DELEGATE); interp_ins_set_dreg (td->last_ins, dreg); interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); @@ -11149,6 +11164,7 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG td->n_data_items = 0; td->max_data_items = 0; td->dummy_var = -1; + td->ref_handle_var = -1; td->data_items = NULL; td->data_hash = g_hash_table_new (NULL, NULL); #ifdef ENABLE_EXPERIMENT_TIERED @@ -11287,6 +11303,11 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG mono_interp_register_imethod_data_items (rtm->data_items, td->imethod_items); rtm->patchpoint_data = td->patchpoint_data; + if (td->ref_handle_var != -1) + rtm->ref_slot_offset = td->locals [td->ref_handle_var].offset; + else + rtm->ref_slot_offset = -1; + /* Save debug info */ interp_save_debug_info (rtm, header, td, td->line_numbers); diff --git a/src/mono/mono/mini/interp/transform.h b/src/mono/mono/mini/interp/transform.h index 2bfbe42390510..c9ff2df80a598 100644 --- a/src/mono/mono/mini/interp/transform.h +++ b/src/mono/mono/mini/interp/transform.h @@ -227,6 +227,7 @@ typedef struct gint32 max_stack_size; InterpLocal *locals; int dummy_var; + int ref_handle_var; int *local_ref_count; unsigned int il_locals_offset; unsigned int il_locals_size;