Skip to content

Commit 6651400

Browse files
committed
[Mono]: Fix additional stack-walks to be async safe.
Mono's stack-walker can be signal/async safe when needed, making sure it won't allocate additional memory or taking internal runtime locks. It is however up to the caller of the stack walking API's to decide if it should be signal and/or async safe. Currently this is controlled using two different flags, the MONO_UNWIND_SIGNAL_SAFE as well as mono_thread_info_is_async_context. This is problematic since callers wants signal safe stack-walking but since not both are set, it will not behave fully signal safe. dotnet/android#9365 hit a couple of scenarios described here: dotnet/android#9365 (comment) that ends up deadlocking due to the fact that they did stack-walking from within a signal handler and deadlocked or dumping stack on suspended thread holding runtime loader lock, but without making the stack-walk async safe. Fix makes sure that calls to stack-walk API's can be made signal and/or async safe and that identified areas uses the correct set of flags given state of threads when stack-walking.
1 parent 20be601 commit 6651400

File tree

3 files changed

+54
-23
lines changed

3 files changed

+54
-23
lines changed

src/mono/mono/metadata/threads.c

+19-9
Original file line numberDiff line numberDiff line change
@@ -2941,25 +2941,27 @@ collect_frame (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data)
29412941
return FALSE;
29422942
}
29432943

2944-
/* This needs to be async safe */
29452944
static SuspendThreadResult
29462945
get_thread_dump (MonoThreadInfo *info, gpointer ud)
29472946
{
29482947
ThreadDumpUserData *user_data = (ThreadDumpUserData *)ud;
29492948
MonoInternalThread *thread = user_data->thread;
29502949

2951-
#if 0
2952-
/* This no longer works with remote unwinding */
2953-
g_string_append_printf (text, " tid=0x%p this=0x%p ", (gpointer)(gsize)thread->tid, thread);
2954-
mono_thread_internal_describe (thread, text);
2955-
g_string_append (text, "\n");
2956-
#endif
2950+
/* This needs to be async safe */
2951+
gboolean restore_async_context = FALSE;
2952+
if (!mono_thread_info_is_async_context ()) {
2953+
mono_thread_info_set_is_async_context (TRUE);
2954+
restore_async_context = TRUE;
2955+
}
29572956

29582957
if (thread == mono_thread_internal_current ())
29592958
mono_get_eh_callbacks ()->mono_walk_stack_with_ctx (collect_frame, NULL, MONO_UNWIND_SIGNAL_SAFE, ud);
29602959
else
29612960
mono_get_eh_callbacks ()->mono_walk_stack_with_state (collect_frame, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, ud);
29622961

2962+
if (restore_async_context)
2963+
mono_thread_info_set_is_async_context (FALSE);
2964+
29632965
return MonoResumeThread;
29642966
}
29652967

@@ -4107,9 +4109,17 @@ mono_thread_info_get_last_managed (MonoThreadInfo *info)
41074109
* The suspended thread might be holding runtime locks. Make sure we don't try taking
41084110
* any runtime locks while unwinding.
41094111
*/
4110-
mono_thread_info_set_is_async_context (TRUE);
4112+
gboolean restore_async_context = FALSE;
4113+
if (!mono_thread_info_is_async_context ()) {
4114+
mono_thread_info_set_is_async_context (TRUE);
4115+
restore_async_context = TRUE;
4116+
}
4117+
41114118
mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &ji);
4112-
mono_thread_info_set_is_async_context (FALSE);
4119+
4120+
if (restore_async_context)
4121+
mono_thread_info_set_is_async_context (FALSE);
4122+
41134123
return ji;
41144124
}
41154125

src/mono/mono/mini/mini-exceptions.c

+28-12
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ static MonoFtnPtrEHCallback ftnptr_eh_callback;
126126
*/
127127
int mono_llvmonly_do_unwind_flag;
128128

129-
static void mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data, gboolean crash_context);
129+
static void mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data);
130130
static void mono_raise_exception_with_ctx (MonoException *exc, MonoContext *ctx);
131131
static void mono_runtime_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data);
132132
static gboolean mono_current_thread_has_handle_block_guard (void);
@@ -1224,7 +1224,7 @@ mono_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnw
12241224
start_ctx = &extra_ctx;
12251225
}
12261226

1227-
mono_walk_stack_full (func, start_ctx, thread->jit_data, mono_get_lmf (), unwind_options, user_data, FALSE);
1227+
mono_walk_stack_full (func, start_ctx, thread->jit_data, mono_get_lmf (), unwind_options, user_data);
12281228
}
12291229

12301230
/**
@@ -1258,7 +1258,7 @@ mono_walk_stack_with_state (MonoJitStackWalk func, MonoThreadUnwindState *state,
12581258
&state->ctx,
12591259
(MonoJitTlsData *)state->unwind_data [MONO_UNWIND_DATA_JIT_TLS],
12601260
(MonoLMF *)state->unwind_data [MONO_UNWIND_DATA_LMF],
1261-
unwind_options, user_data, FALSE);
1261+
unwind_options, user_data);
12621262
}
12631263

12641264
void
@@ -1278,14 +1278,13 @@ mono_walk_stack (MonoJitStackWalk func, MonoUnwindOptions options, void *user_da
12781278
* \param thread the thread whose stack to walk, can be NULL to use the current thread
12791279
* \param lmf the LMF of \p thread, can be NULL to use the LMF of the current thread
12801280
* \param user_data data passed to the callback
1281-
* \param crash_context tells us that we're in a context where it's not safe to lock or allocate
12821281
* This function walks the stack of a thread, starting from the state
12831282
* represented by \p start_ctx. For each frame the callback
12841283
* function is called with the relevant info. The walk ends when no more
12851284
* managed stack frames are found or when the callback returns a TRUE value.
12861285
*/
12871286
static void
1288-
mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data, gboolean crash_context)
1287+
mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data)
12891288
{
12901289
gint il_offset;
12911290
MonoContext ctx, new_ctx;
@@ -1372,15 +1371,15 @@ mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoJitTlsD
13721371
MonoDebugSourceLocation *source = NULL;
13731372

13741373
// Don't do this when we can be in a signal handler
1375-
if (!crash_context)
1374+
if (!async)
13761375
source = mono_debug_lookup_source_location (jinfo_get_method (frame.ji), frame.native_offset, NULL);
13771376
if (source) {
13781377
il_offset = source->il_offset;
13791378
} else {
13801379
MonoSeqPointInfo *seq_points = NULL;
13811380

13821381
// It's more reliable to look into the global cache if possible
1383-
if (crash_context)
1382+
if (async)
13841383
seq_points = (MonoSeqPointInfo *) frame.ji->seq_points;
13851384
else
13861385
seq_points = mono_get_seq_points (jinfo_get_method (frame.ji));
@@ -2976,7 +2975,17 @@ mono_handle_native_crash (const char *signal, MonoContext *mctx, MONO_SIG_HANDLE
29762975
g_async_safe_printf ("\tManaged Stacktrace:\n");
29772976
g_async_safe_printf ("=================================================================\n");
29782977

2979-
mono_walk_stack_full (print_stack_frame_signal_safe, mctx, jit_tls, mono_get_lmf (), MONO_UNWIND_LOOKUP_IL_OFFSET, NULL, TRUE);
2978+
gboolean restore_async_context = FALSE;
2979+
if (!mono_thread_info_is_async_context ()) {
2980+
mono_thread_info_set_is_async_context (TRUE);
2981+
restore_async_context = TRUE;
2982+
}
2983+
2984+
mono_walk_stack_full (print_stack_frame_signal_safe, mctx, jit_tls, mono_get_lmf (), MONO_UNWIND_LOOKUP_IL_OFFSET, NULL);
2985+
2986+
if (restore_async_context)
2987+
mono_thread_info_set_is_async_context (FALSE);
2988+
29802989
g_async_safe_printf ("=================================================================\n");
29812990
}
29822991

@@ -3146,15 +3155,22 @@ mono_install_handler_block_guard (MonoThreadUnwindState *ctx)
31463155
MonoJitTlsData *jit_tls = (MonoJitTlsData *)ctx->unwind_data [MONO_UNWIND_DATA_JIT_TLS];
31473156

31483157
/* Guard against a null MonoJitTlsData. This can happens if the thread receives the
3149-
* interrupt signal before the JIT has time to initialize its TLS data for the given thread.
3158+
* interrupt signal before the JIT has time to initialize its TLS data for the given thread.
31503159
*/
31513160
if (!jit_tls || jit_tls->handler_block)
31523161
return FALSE;
31533162

31543163
/* Do an async safe stack walk */
3155-
mono_thread_info_set_is_async_context (TRUE);
3156-
mono_walk_stack_with_state (find_last_handler_block, ctx, MONO_UNWIND_NONE, &data);
3157-
mono_thread_info_set_is_async_context (FALSE);
3164+
gboolean restore_async_context = FALSE;
3165+
if (!mono_thread_info_is_async_context ()) {
3166+
mono_thread_info_set_is_async_context (TRUE);
3167+
restore_async_context = TRUE;
3168+
}
3169+
3170+
mono_walk_stack_with_state (find_last_handler_block, ctx, MONO_UNWIND_SIGNAL_SAFE, &data);
3171+
3172+
if (restore_async_context)
3173+
mono_thread_info_set_is_async_context (FALSE);
31583174

31593175
if (!data.ji)
31603176
return FALSE;

src/mono/mono/mini/mini-posix.c

+7-2
Original file line numberDiff line numberDiff line change
@@ -291,11 +291,16 @@ MONO_SIG_HANDLER_FUNC (static, profiler_signal_handler)
291291

292292
int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
293293

294-
mono_thread_info_set_is_async_context (TRUE);
294+
gboolean restore_async_context = FALSE;
295+
if (!mono_thread_info_is_async_context ()) {
296+
mono_thread_info_set_is_async_context (TRUE);
297+
restore_async_context = TRUE;
298+
}
295299

296300
MONO_PROFILER_RAISE (sample_hit, ((const mono_byte*)mono_arch_ip_from_context (ctx), ctx));
297301

298-
mono_thread_info_set_is_async_context (FALSE);
302+
if (restore_async_context)
303+
mono_thread_info_set_is_async_context (FALSE);
299304

300305
mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
301306

0 commit comments

Comments
 (0)