From 107a7eddbb9106443e2ad21cb7eed1661684d2e1 Mon Sep 17 00:00:00 2001 From: Simon Zuckerbraun Date: Thu, 11 Jul 2024 17:59:09 -0500 Subject: [PATCH 01/10] Fix race in recv wait on application thread --- bindings/gumjs/gumquickcore.c | 32 +++++++++++++++-- bindings/gumjs/gumv8core.cpp | 31 ++++++++++++++-- tests/gumjs/script.c | 67 +++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 5 deletions(-) diff --git a/bindings/gumjs/gumquickcore.c b/bindings/gumjs/gumquickcore.c index 17f044b44..d7877ab2c 100644 --- a/bindings/gumjs/gumquickcore.c +++ b/bindings/gumjs/gumquickcore.c @@ -162,6 +162,15 @@ struct _GumQuickCallbackContext int initial_property_count; }; +struct _GumQuickThreadData +{ + guint event_count_last_seen; +}; + +typedef struct _GumQuickThreadData GumQuickThreadData; + +static GPrivate gum_quick_thread_data; + static gboolean gum_quick_core_handle_crashed_js (GumExceptionDetails * details, gpointer user_data); @@ -383,6 +392,8 @@ static JSValue gum_quick_value_from_ffi (JSContext * ctx, static void gum_quick_core_setup_atoms (GumQuickCore * self); static void gum_quick_core_teardown_atoms (GumQuickCore * self); +static GumQuickThreadData * get_gum_quick_thread_data (); + static const JSCFunctionListEntry gumjs_root_entries[] = { JS_CFUNC_DEF ("_setTimeout", 0, gumjs_set_timeout), @@ -2713,7 +2724,7 @@ GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) GumQuickScope scope = GUM_QUICK_SCOPE_INIT (self); GMainContext * context; gboolean called_from_js_thread; - guint start_count; + GumQuickThreadData * thread_data = get_gum_quick_thread_data(); gboolean event_source_available; _gum_quick_scope_perform_pending_io (self->current_scope); @@ -2725,8 +2736,7 @@ GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) g_mutex_lock (&self->event_mutex); - start_count = self->event_count; - while (self->event_count == start_count && self->event_source_available) + while (self->event_count == thread_data->event_count_last_seen && self->event_source_available) { if (called_from_js_thread) { @@ -2740,6 +2750,8 @@ GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) } } + thread_data->event_count_last_seen = self->event_count; + event_source_available = self->event_source_available; g_mutex_unlock (&self->event_mutex); @@ -5835,3 +5847,17 @@ gum_quick_core_teardown_atoms (GumQuickCore * self) #undef GUM_TEARDOWN_ATOM } + +static GumQuickThreadData * get_gum_quick_thread_data () +{ + GumQuickThreadData * data = g_private_get (&gum_quick_thread_data); + + if (data == NULL) + { + data = g_new0 (GumQuickThreadData, 1); + data->event_count_last_seen = 0; + g_private_set (&gum_quick_thread_data, (gpointer) data); + } + + return data; +} \ No newline at end of file diff --git a/bindings/gumjs/gumv8core.cpp b/bindings/gumjs/gumv8core.cpp index 805cbe8bd..038152310 100644 --- a/bindings/gumjs/gumv8core.cpp +++ b/bindings/gumjs/gumv8core.cpp @@ -179,6 +179,15 @@ struct GumV8SourceMap GumV8Core * core; }; +struct _GumV8ThreadData +{ + guint event_count_last_seen; +}; + +typedef struct _GumV8ThreadData GumV8ThreadData; + +static GPrivate gum_v8_thread_data; + static gboolean gum_v8_core_handle_crashed_js (GumExceptionDetails * details, gpointer user_data); @@ -384,6 +393,8 @@ static gboolean gum_v8_value_to_ffi_type (GumV8Core * core, static gboolean gum_v8_value_from_ffi_type (GumV8Core * core, Local * svalue, const GumFFIValue * value, const ffi_type * type); +static GumV8ThreadData * get_gum_v8_thread_data(); + static const GumV8Function gumjs_global_functions[] = { { "_setTimeout", gumjs_set_timeout, }, @@ -1601,6 +1612,7 @@ GUMJS_DEFINE_FUNCTION (gumjs_set_incoming_message_callback) GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) { + GumV8ThreadData * thread_data = get_gum_v8_thread_data (); gboolean event_source_available; core->current_scope->PerformPendingIO (); @@ -1613,8 +1625,7 @@ GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) g_mutex_lock (&core->event_mutex); - auto start_count = core->event_count; - while (core->event_count == start_count && core->event_source_available) + while (core->event_count == thread_data->event_count_last_seen && core->event_source_available) { if (called_from_js_thread) { @@ -1628,6 +1639,8 @@ GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) } } + thread_data->event_count_last_seen = core->event_count; + event_source_available = core->event_source_available; g_mutex_unlock (&core->event_mutex); @@ -4583,3 +4596,17 @@ gum_v8_value_from_ffi_type (GumV8Core * core, return TRUE; } + +static GumV8ThreadData * get_gum_v8_thread_data () +{ + GumV8ThreadData * data = (GumV8ThreadData *) g_private_get (&gum_v8_thread_data); + + if (data == NULL) + { + data = g_new0 (GumV8ThreadData, 1); + data->event_count_last_seen = 0; + g_private_set(&gum_v8_thread_data, (gpointer) data); + } + + return data; +} \ No newline at end of file diff --git a/tests/gumjs/script.c b/tests/gumjs/script.c index ded50975f..889afccf3 100644 --- a/tests/gumjs/script.c +++ b/tests/gumjs/script.c @@ -24,6 +24,7 @@ TESTLIST_BEGIN (script) TESTENTRY (recv_may_specify_desired_message_type) TESTENTRY (recv_can_be_waited_for_from_an_application_thread) TESTENTRY (recv_can_be_waited_for_from_two_application_threads) + TESTENTRY (recv_wait_in_an_application_thread_should_not_deadlock) TESTENTRY (recv_can_be_waited_for_from_our_js_thread) TESTENTRY (recv_wait_in_an_application_thread_should_throw_on_unload) TESTENTRY (recv_wait_in_our_js_thread_should_throw_on_unload) @@ -6125,6 +6126,72 @@ TESTCASE (message_can_be_received) EXPECT_SEND_MESSAGE_WITH ("\"pong\""); } +TESTCASE (recv_wait_in_an_application_thread_should_not_deadlock) +{ + GThread * worker_thread; + GumInvokeTargetContext ctx; + + if (!g_test_slow ()) + { + g_print (" "); + return; + } + + COMPILE_AND_LOAD_SCRIPT( + "Interceptor.replace(" GUM_PTR_CONST ", new NativeCallback(function (arg) {" + " let timeToRecv;" + " let shouldExit = false;" + " while (true) {" + " recv(message => {" + " if (message.type == 'stop') {" + " shouldExit = true;" + " return;" + " }" + " else if (message.type != 'waituntil') {" + " throw new Error('Received unexpected message: ' + message.type); }" + " timeToRecv = message.time;" + " }).wait();" + " if (shouldExit) {" + " return 0;" + " }" + " while (Date.now() < timeToRecv) {};" + " recv(message => {" + " if (message.type != 'ping') {" + " throw new Error('Received unexpected message: ' + message.type); }" + " }).wait();" + " send('pong');" + " }" + "}, 'int', ['int']));", target_function_int); + + ctx.script = fixture->script; + ctx.repeat_duration = 0; + ctx.started = 0; + ctx.finished = 0; + worker_thread = g_thread_new ("script-test-worker-thread", + invoke_target_function_int_worker, &ctx); + while (ctx.started == 0) + g_usleep(G_USEC_PER_SEC / 200); + + for (int i = 0; i < 100; i++) + { + gint64 timeNow = g_get_real_time (); + gint64 timeToScheduleRecv = timeNow - (timeNow % (20 * 1000)) + 50 * 1000; + GString * msg = g_string_new (NULL); + g_string_printf (msg, "{\"type\":\"waituntil\", \"time\": %lld}", timeToScheduleRecv / 1000); + POST_MESSAGE (msg->str); + g_string_free (msg, true); + gint64 timeToPost = timeToScheduleRecv + i; + while (g_get_real_time () < timeToPost) {} + POST_MESSAGE ("{\"type\":\"ping\"}"); + EXPECT_SEND_MESSAGE_WITH ("\"pong\""); + } + + POST_MESSAGE ("{\"type\":\"stop\"}" ); + + g_thread_join (worker_thread); + g_assert_cmpint (ctx.finished, == , 1); +} + TESTCASE (message_can_be_received_with_data) { const guint8 data_to_send[2] = { 0x13, 0x37 }; From 804919ab0c38260adac51ad956d8343951d3f6d7 Mon Sep 17 00:00:00 2001 From: Simon Zuckerbraun Date: Fri, 12 Jul 2024 00:50:37 -0500 Subject: [PATCH 02/10] Style corrections --- bindings/gumjs/gumquickcore.c | 9 ++++----- bindings/gumjs/gumv8core.cpp | 10 ++++------ 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/bindings/gumjs/gumquickcore.c b/bindings/gumjs/gumquickcore.c index d7877ab2c..452ed9546 100644 --- a/bindings/gumjs/gumquickcore.c +++ b/bindings/gumjs/gumquickcore.c @@ -45,6 +45,7 @@ typedef guint8 GumQuickCodeTraps; typedef guint8 GumQuickReturnValueShape; typedef struct _GumQuickFFIFunction GumQuickFFIFunction; typedef struct _GumQuickCallbackContext GumQuickCallbackContext; +typedef struct _GumQuickThreadData GumQuickThreadData; struct _GumQuickFlushCallback { @@ -167,10 +168,6 @@ struct _GumQuickThreadData guint event_count_last_seen; }; -typedef struct _GumQuickThreadData GumQuickThreadData; - -static GPrivate gum_quick_thread_data; - static gboolean gum_quick_core_handle_crashed_js (GumExceptionDetails * details, gpointer user_data); @@ -1381,6 +1378,8 @@ static const JSCFunctionListEntry gumjs_worker_entries[] = JS_CFUNC_DEF ("post", 0, gumjs_worker_post), }; +static GPrivate gum_quick_thread_data; + void _gum_quick_core_init (GumQuickCore * self, GumQuickScript * script, @@ -5860,4 +5859,4 @@ static GumQuickThreadData * get_gum_quick_thread_data () } return data; -} \ No newline at end of file +} diff --git a/bindings/gumjs/gumv8core.cpp b/bindings/gumjs/gumv8core.cpp index 038152310..39c5c4d0e 100644 --- a/bindings/gumjs/gumv8core.cpp +++ b/bindings/gumjs/gumv8core.cpp @@ -179,15 +179,11 @@ struct GumV8SourceMap GumV8Core * core; }; -struct _GumV8ThreadData +struct GumV8ThreadData { guint event_count_last_seen; }; -typedef struct _GumV8ThreadData GumV8ThreadData; - -static GPrivate gum_v8_thread_data; - static gboolean gum_v8_core_handle_crashed_js (GumExceptionDetails * details, gpointer user_data); @@ -539,6 +535,8 @@ static const GumV8Function gumjs_source_map_functions[] = { NULL, NULL } }; +static GPrivate gum_v8_thread_data; + void _gum_v8_core_init (GumV8Core * self, GumV8Script * script, @@ -4609,4 +4607,4 @@ static GumV8ThreadData * get_gum_v8_thread_data () } return data; -} \ No newline at end of file +} From aa95c69fe60f014e9b7f29d2f4f47bb012e8ce2f Mon Sep 17 00:00:00 2001 From: Simon Zuckerbraun Date: Fri, 12 Jul 2024 01:21:27 -0500 Subject: [PATCH 03/10] Style corrections --- bindings/gumjs/gumquickcore.c | 5 +++-- bindings/gumjs/gumv8core.cpp | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/bindings/gumjs/gumquickcore.c b/bindings/gumjs/gumquickcore.c index 452ed9546..32969e773 100644 --- a/bindings/gumjs/gumquickcore.c +++ b/bindings/gumjs/gumquickcore.c @@ -389,7 +389,7 @@ static JSValue gum_quick_value_from_ffi (JSContext * ctx, static void gum_quick_core_setup_atoms (GumQuickCore * self); static void gum_quick_core_teardown_atoms (GumQuickCore * self); -static GumQuickThreadData * get_gum_quick_thread_data (); +static GumQuickThreadData * get_gum_quick_thread_data (void); static const JSCFunctionListEntry gumjs_root_entries[] = { @@ -5847,7 +5847,8 @@ gum_quick_core_teardown_atoms (GumQuickCore * self) #undef GUM_TEARDOWN_ATOM } -static GumQuickThreadData * get_gum_quick_thread_data () +static GumQuickThreadData * +get_gum_quick_thread_data (void) { GumQuickThreadData * data = g_private_get (&gum_quick_thread_data); diff --git a/bindings/gumjs/gumv8core.cpp b/bindings/gumjs/gumv8core.cpp index 39c5c4d0e..912990955 100644 --- a/bindings/gumjs/gumv8core.cpp +++ b/bindings/gumjs/gumv8core.cpp @@ -1610,7 +1610,6 @@ GUMJS_DEFINE_FUNCTION (gumjs_set_incoming_message_callback) GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) { - GumV8ThreadData * thread_data = get_gum_v8_thread_data (); gboolean event_source_available; core->current_scope->PerformPendingIO (); @@ -1618,6 +1617,7 @@ GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) { ScriptUnlocker unlocker (core); + auto thread_data = get_gum_v8_thread_data (); auto context = gum_script_scheduler_get_js_context (core->scheduler); gboolean called_from_js_thread = g_main_context_is_owner (context); @@ -4595,7 +4595,8 @@ gum_v8_value_from_ffi_type (GumV8Core * core, return TRUE; } -static GumV8ThreadData * get_gum_v8_thread_data () +static GumV8ThreadData * +get_gum_v8_thread_data () { GumV8ThreadData * data = (GumV8ThreadData *) g_private_get (&gum_v8_thread_data); From 4963dfb50e47161a587f7f05fa61af0e54dc8a01 Mon Sep 17 00:00:00 2001 From: Simon Zuckerbraun Date: Fri, 12 Jul 2024 01:28:25 -0500 Subject: [PATCH 04/10] Style corrections --- bindings/gumjs/gumquickcore.c | 2 +- bindings/gumjs/gumv8core.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/gumjs/gumquickcore.c b/bindings/gumjs/gumquickcore.c index 32969e773..866e7b6be 100644 --- a/bindings/gumjs/gumquickcore.c +++ b/bindings/gumjs/gumquickcore.c @@ -2723,7 +2723,7 @@ GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) GumQuickScope scope = GUM_QUICK_SCOPE_INIT (self); GMainContext * context; gboolean called_from_js_thread; - GumQuickThreadData * thread_data = get_gum_quick_thread_data(); + GumQuickThreadData * thread_data = get_gum_quick_thread_data (); gboolean event_source_available; _gum_quick_scope_perform_pending_io (self->current_scope); diff --git a/bindings/gumjs/gumv8core.cpp b/bindings/gumjs/gumv8core.cpp index 912990955..433fd0a09 100644 --- a/bindings/gumjs/gumv8core.cpp +++ b/bindings/gumjs/gumv8core.cpp @@ -389,7 +389,7 @@ static gboolean gum_v8_value_to_ffi_type (GumV8Core * core, static gboolean gum_v8_value_from_ffi_type (GumV8Core * core, Local * svalue, const GumFFIValue * value, const ffi_type * type); -static GumV8ThreadData * get_gum_v8_thread_data(); +static GumV8ThreadData * get_gum_v8_thread_data (); static const GumV8Function gumjs_global_functions[] = { From f8286c3d0ddba9a590c80d97b2a68be974133238 Mon Sep 17 00:00:00 2001 From: Simon Zuckerbraun Date: Fri, 12 Jul 2024 02:12:22 -0500 Subject: [PATCH 05/10] Style corrections --- tests/gumjs/script.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/gumjs/script.c b/tests/gumjs/script.c index 889afccf3..8345ea97f 100644 --- a/tests/gumjs/script.c +++ b/tests/gumjs/script.c @@ -6137,7 +6137,7 @@ TESTCASE (recv_wait_in_an_application_thread_should_not_deadlock) return; } - COMPILE_AND_LOAD_SCRIPT( + COMPILE_AND_LOAD_SCRIPT ( "Interceptor.replace(" GUM_PTR_CONST ", new NativeCallback(function (arg) {" " let timeToRecv;" " let shouldExit = false;" @@ -6170,7 +6170,7 @@ TESTCASE (recv_wait_in_an_application_thread_should_not_deadlock) worker_thread = g_thread_new ("script-test-worker-thread", invoke_target_function_int_worker, &ctx); while (ctx.started == 0) - g_usleep(G_USEC_PER_SEC / 200); + g_usleep (G_USEC_PER_SEC / 200); for (int i = 0; i < 100; i++) { @@ -6181,7 +6181,7 @@ TESTCASE (recv_wait_in_an_application_thread_should_not_deadlock) POST_MESSAGE (msg->str); g_string_free (msg, true); gint64 timeToPost = timeToScheduleRecv + i; - while (g_get_real_time () < timeToPost) {} + while (g_get_real_time () < timeToPost); POST_MESSAGE ("{\"type\":\"ping\"}"); EXPECT_SEND_MESSAGE_WITH ("\"pong\""); } From af1f9ce3b1d9b2a575bbbe0fc191e226475dd1f5 Mon Sep 17 00:00:00 2001 From: Simon Zuckerbraun Date: Fri, 12 Jul 2024 02:15:58 -0500 Subject: [PATCH 06/10] Style correction --- bindings/gumjs/gumv8core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/gumjs/gumv8core.cpp b/bindings/gumjs/gumv8core.cpp index 433fd0a09..b6f8ebfbf 100644 --- a/bindings/gumjs/gumv8core.cpp +++ b/bindings/gumjs/gumv8core.cpp @@ -4604,7 +4604,7 @@ get_gum_v8_thread_data () { data = g_new0 (GumV8ThreadData, 1); data->event_count_last_seen = 0; - g_private_set(&gum_v8_thread_data, (gpointer) data); + g_private_set (&gum_v8_thread_data, (gpointer) data); } return data; From 5a9e7b85353df5f5b2e572b2a6fef8f98734855f Mon Sep 17 00:00:00 2001 From: Simon Zuckerbraun Date: Fri, 12 Jul 2024 02:41:19 -0500 Subject: [PATCH 07/10] Style corrections --- bindings/gumjs/gumquickcore.c | 3 ++- bindings/gumjs/gumv8core.cpp | 6 ++++-- tests/gumjs/script.c | 14 +++++++++----- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/bindings/gumjs/gumquickcore.c b/bindings/gumjs/gumquickcore.c index 866e7b6be..d0f7b9beb 100644 --- a/bindings/gumjs/gumquickcore.c +++ b/bindings/gumjs/gumquickcore.c @@ -2735,7 +2735,8 @@ GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) g_mutex_lock (&self->event_mutex); - while (self->event_count == thread_data->event_count_last_seen && self->event_source_available) + while (self->event_count == thread_data->event_count_last_seen + && self->event_source_available) { if (called_from_js_thread) { diff --git a/bindings/gumjs/gumv8core.cpp b/bindings/gumjs/gumv8core.cpp index b6f8ebfbf..69c528e0e 100644 --- a/bindings/gumjs/gumv8core.cpp +++ b/bindings/gumjs/gumv8core.cpp @@ -1623,7 +1623,8 @@ GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) g_mutex_lock (&core->event_mutex); - while (core->event_count == thread_data->event_count_last_seen && core->event_source_available) + while (core->event_count == thread_data->event_count_last_seen + && core->event_source_available) { if (called_from_js_thread) { @@ -4598,7 +4599,8 @@ gum_v8_value_from_ffi_type (GumV8Core * core, static GumV8ThreadData * get_gum_v8_thread_data () { - GumV8ThreadData * data = (GumV8ThreadData *) g_private_get (&gum_v8_thread_data); + GumV8ThreadData * data = + (GumV8ThreadData *) g_private_get (&gum_v8_thread_data); if (data == NULL) { diff --git a/tests/gumjs/script.c b/tests/gumjs/script.c index 8345ea97f..eab6877cb 100644 --- a/tests/gumjs/script.c +++ b/tests/gumjs/script.c @@ -6138,7 +6138,7 @@ TESTCASE (recv_wait_in_an_application_thread_should_not_deadlock) } COMPILE_AND_LOAD_SCRIPT ( - "Interceptor.replace(" GUM_PTR_CONST ", new NativeCallback(function (arg) {" + "Interceptor.replace(" GUM_PTR_CONST ", new NativeCallback((arg) => {" " let timeToRecv;" " let shouldExit = false;" " while (true) {" @@ -6148,7 +6148,8 @@ TESTCASE (recv_wait_in_an_application_thread_should_not_deadlock) " return;" " }" " else if (message.type != 'waituntil') {" - " throw new Error('Received unexpected message: ' + message.type); }" + " throw new Error(" + " 'Received unexpected message: ' + message.type); }" " timeToRecv = message.time;" " }).wait();" " if (shouldExit) {" @@ -6157,7 +6158,8 @@ TESTCASE (recv_wait_in_an_application_thread_should_not_deadlock) " while (Date.now() < timeToRecv) {};" " recv(message => {" " if (message.type != 'ping') {" - " throw new Error('Received unexpected message: ' + message.type); }" + " throw new Error(" + " 'Received unexpected message: ' + message.type); }" " }).wait();" " send('pong');" " }" @@ -6175,9 +6177,11 @@ TESTCASE (recv_wait_in_an_application_thread_should_not_deadlock) for (int i = 0; i < 100; i++) { gint64 timeNow = g_get_real_time (); - gint64 timeToScheduleRecv = timeNow - (timeNow % (20 * 1000)) + 50 * 1000; + gint64 timeToScheduleRecv = + timeNow - (timeNow % (20 * 1000)) + 50 * 1000; GString * msg = g_string_new (NULL); - g_string_printf (msg, "{\"type\":\"waituntil\", \"time\": %lld}", timeToScheduleRecv / 1000); + g_string_printf (msg, "{\"type\":\"waituntil\", \"time\": %lld}", + timeToScheduleRecv / 1000); POST_MESSAGE (msg->str); g_string_free (msg, true); gint64 timeToPost = timeToScheduleRecv + i; From e7348526aa463b3aa0822fd1ffabcad1a8660abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Andr=C3=A9=20Vadla=20Ravn=C3=A5s?= Date: Mon, 15 Jul 2024 23:46:57 +0200 Subject: [PATCH 08/10] Try a simpler approach --- bindings/gumjs/gumquickcore.c | 36 ++++++----------------------------- bindings/gumjs/gumv8core.cpp | 35 +++++----------------------------- 2 files changed, 11 insertions(+), 60 deletions(-) diff --git a/bindings/gumjs/gumquickcore.c b/bindings/gumjs/gumquickcore.c index d0f7b9beb..183b76ae6 100644 --- a/bindings/gumjs/gumquickcore.c +++ b/bindings/gumjs/gumquickcore.c @@ -45,7 +45,6 @@ typedef guint8 GumQuickCodeTraps; typedef guint8 GumQuickReturnValueShape; typedef struct _GumQuickFFIFunction GumQuickFFIFunction; typedef struct _GumQuickCallbackContext GumQuickCallbackContext; -typedef struct _GumQuickThreadData GumQuickThreadData; struct _GumQuickFlushCallback { @@ -163,11 +162,6 @@ struct _GumQuickCallbackContext int initial_property_count; }; -struct _GumQuickThreadData -{ - guint event_count_last_seen; -}; - static gboolean gum_quick_core_handle_crashed_js (GumExceptionDetails * details, gpointer user_data); @@ -389,8 +383,6 @@ static JSValue gum_quick_value_from_ffi (JSContext * ctx, static void gum_quick_core_setup_atoms (GumQuickCore * self); static void gum_quick_core_teardown_atoms (GumQuickCore * self); -static GumQuickThreadData * get_gum_quick_thread_data (void); - static const JSCFunctionListEntry gumjs_root_entries[] = { JS_CFUNC_DEF ("_setTimeout", 0, gumjs_set_timeout), @@ -1378,8 +1370,6 @@ static const JSCFunctionListEntry gumjs_worker_entries[] = JS_CFUNC_DEF ("post", 0, gumjs_worker_post), }; -static GPrivate gum_quick_thread_data; - void _gum_quick_core_init (GumQuickCore * self, GumQuickScript * script, @@ -2720,12 +2710,16 @@ GUMJS_DEFINE_FUNCTION (gumjs_set_incoming_message_callback) GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) { GumQuickCore * self = core; + guint start_count; GumQuickScope scope = GUM_QUICK_SCOPE_INIT (self); GMainContext * context; gboolean called_from_js_thread; - GumQuickThreadData * thread_data = get_gum_quick_thread_data (); gboolean event_source_available; + g_mutex_lock (&self->event_mutex); + start_count = self->event_count; + g_mutex_unlock (&self->event_mutex); + _gum_quick_scope_perform_pending_io (self->current_scope); _gum_quick_scope_suspend (&scope); @@ -2735,8 +2729,7 @@ GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) g_mutex_lock (&self->event_mutex); - while (self->event_count == thread_data->event_count_last_seen - && self->event_source_available) + while (self->event_count == start_count && self->event_source_available) { if (called_from_js_thread) { @@ -2750,8 +2743,6 @@ GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) } } - thread_data->event_count_last_seen = self->event_count; - event_source_available = self->event_source_available; g_mutex_unlock (&self->event_mutex); @@ -5847,18 +5838,3 @@ gum_quick_core_teardown_atoms (GumQuickCore * self) #undef GUM_TEARDOWN_ATOM } - -static GumQuickThreadData * -get_gum_quick_thread_data (void) -{ - GumQuickThreadData * data = g_private_get (&gum_quick_thread_data); - - if (data == NULL) - { - data = g_new0 (GumQuickThreadData, 1); - data->event_count_last_seen = 0; - g_private_set (&gum_quick_thread_data, (gpointer) data); - } - - return data; -} diff --git a/bindings/gumjs/gumv8core.cpp b/bindings/gumjs/gumv8core.cpp index 69c528e0e..648c8defb 100644 --- a/bindings/gumjs/gumv8core.cpp +++ b/bindings/gumjs/gumv8core.cpp @@ -179,11 +179,6 @@ struct GumV8SourceMap GumV8Core * core; }; -struct GumV8ThreadData -{ - guint event_count_last_seen; -}; - static gboolean gum_v8_core_handle_crashed_js (GumExceptionDetails * details, gpointer user_data); @@ -389,8 +384,6 @@ static gboolean gum_v8_value_to_ffi_type (GumV8Core * core, static gboolean gum_v8_value_from_ffi_type (GumV8Core * core, Local * svalue, const GumFFIValue * value, const ffi_type * type); -static GumV8ThreadData * get_gum_v8_thread_data (); - static const GumV8Function gumjs_global_functions[] = { { "_setTimeout", gumjs_set_timeout, }, @@ -535,8 +528,6 @@ static const GumV8Function gumjs_source_map_functions[] = { NULL, NULL } }; -static GPrivate gum_v8_thread_data; - void _gum_v8_core_init (GumV8Core * self, GumV8Script * script, @@ -1610,6 +1601,10 @@ GUMJS_DEFINE_FUNCTION (gumjs_set_incoming_message_callback) GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) { + g_mutex_lock (&core->event_mutex); + auto start_count = core->event_count; + g_mutex_unlock (&core->event_mutex); + gboolean event_source_available; core->current_scope->PerformPendingIO (); @@ -1617,14 +1612,12 @@ GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) { ScriptUnlocker unlocker (core); - auto thread_data = get_gum_v8_thread_data (); auto context = gum_script_scheduler_get_js_context (core->scheduler); gboolean called_from_js_thread = g_main_context_is_owner (context); g_mutex_lock (&core->event_mutex); - while (core->event_count == thread_data->event_count_last_seen - && core->event_source_available) + while (core->event_count == start_count && core->event_source_available) { if (called_from_js_thread) { @@ -1638,8 +1631,6 @@ GUMJS_DEFINE_FUNCTION (gumjs_wait_for_event) } } - thread_data->event_count_last_seen = core->event_count; - event_source_available = core->event_source_available; g_mutex_unlock (&core->event_mutex); @@ -4595,19 +4586,3 @@ gum_v8_value_from_ffi_type (GumV8Core * core, return TRUE; } - -static GumV8ThreadData * -get_gum_v8_thread_data () -{ - GumV8ThreadData * data = - (GumV8ThreadData *) g_private_get (&gum_v8_thread_data); - - if (data == NULL) - { - data = g_new0 (GumV8ThreadData, 1); - data->event_count_last_seen = 0; - g_private_set (&gum_v8_thread_data, (gpointer) data); - } - - return data; -} From 58cddeaf2fe6cbb404ec851a116c3f96e23ddab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Andr=C3=A9=20Vadla=20Ravn=C3=A5s?= Date: Mon, 15 Jul 2024 23:58:18 +0200 Subject: [PATCH 09/10] Fix some stylistic inconsistences --- tests/gumjs/script.c | 76 +++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/tests/gumjs/script.c b/tests/gumjs/script.c index eab6877cb..7a6a314df 100644 --- a/tests/gumjs/script.c +++ b/tests/gumjs/script.c @@ -6130,6 +6130,7 @@ TESTCASE (recv_wait_in_an_application_thread_should_not_deadlock) { GThread * worker_thread; GumInvokeTargetContext ctx; + guint i; if (!g_test_slow ()) { @@ -6138,31 +6139,27 @@ TESTCASE (recv_wait_in_an_application_thread_should_not_deadlock) } COMPILE_AND_LOAD_SCRIPT ( - "Interceptor.replace(" GUM_PTR_CONST ", new NativeCallback((arg) => {" - " let timeToRecv;" - " let shouldExit = false;" - " while (true) {" - " recv(message => {" - " if (message.type == 'stop') {" - " shouldExit = true;" - " return;" - " }" - " else if (message.type != 'waituntil') {" - " throw new Error(" - " 'Received unexpected message: ' + message.type); }" + "Interceptor.replace(" GUM_PTR_CONST ", new NativeCallback(arg => {" + " let timeToRecv;" + " let shouldExit = false;" + " while (true) {" + " recv(message => {" + " if (message.type === 'stop')" + " shouldExit = true;" + " else if (message.type === 'wait-until')" " timeToRecv = message.time;" - " }).wait();" - " if (shouldExit) {" - " return 0;" - " }" - " while (Date.now() < timeToRecv) {};" - " recv(message => {" - " if (message.type != 'ping') {" - " throw new Error(" - " 'Received unexpected message: ' + message.type); }" - " }).wait();" - " send('pong');" - " }" + " else" + " throw new Error(`unexpected message: ${message.type}`);" + " }).wait();" + " if (shouldExit)" + " return 0;" + " while (Date.now() < timeToRecv) {}" + " recv(message => {" + " if (message.type !== 'ping')" + " throw new Error(`unexpected message: ${message.type}`);" + " }).wait();" + " send('pong');" + " }" "}, 'int', ['int']));", target_function_int); ctx.script = fixture->script; @@ -6174,26 +6171,31 @@ TESTCASE (recv_wait_in_an_application_thread_should_not_deadlock) while (ctx.started == 0) g_usleep (G_USEC_PER_SEC / 200); - for (int i = 0; i < 100; i++) + for (i = 0; i != 100; i++) { - gint64 timeNow = g_get_real_time (); - gint64 timeToScheduleRecv = - timeNow - (timeNow % (20 * 1000)) + 50 * 1000; - GString * msg = g_string_new (NULL); - g_string_printf (msg, "{\"type\":\"waituntil\", \"time\": %lld}", - timeToScheduleRecv / 1000); - POST_MESSAGE (msg->str); - g_string_free (msg, true); - gint64 timeToPost = timeToScheduleRecv + i; - while (g_get_real_time () < timeToPost); + gint64 time_now, time_to_schedule_recv, time_to_post; + gchar * msg; + + time_now = g_get_real_time (); + time_to_schedule_recv = time_now - (time_now % (20 * 1000)) + (50 * 1000); + time_to_post = time_to_schedule_recv + i; + + msg = g_strdup_printf ( + "{\"type\":\"wait-until\",\"time\":%" G_GINT64_FORMAT "}", + time_to_schedule_recv / 1000); + POST_MESSAGE (msg); + g_free (msg); + + while (g_get_real_time () < time_to_post) + ; POST_MESSAGE ("{\"type\":\"ping\"}"); EXPECT_SEND_MESSAGE_WITH ("\"pong\""); } - POST_MESSAGE ("{\"type\":\"stop\"}" ); + POST_MESSAGE ("{\"type\":\"stop\"}"); g_thread_join (worker_thread); - g_assert_cmpint (ctx.finished, == , 1); + g_assert_cmpint (ctx.finished, ==, 1); } TESTCASE (message_can_be_received_with_data) From 10fdb3314e132eb822b619ef6cd56a75e073e1a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Andr=C3=A9=20Vadla=20Ravn=C3=A5s?= Date: Mon, 15 Jul 2024 23:59:20 +0200 Subject: [PATCH 10/10] Add Simon Zuckerbraun to copyright headers --- bindings/gumjs/gumquickcore.c | 1 + bindings/gumjs/gumv8core.cpp | 1 + tests/gumjs/script.c | 1 + 3 files changed, 3 insertions(+) diff --git a/bindings/gumjs/gumquickcore.c b/bindings/gumjs/gumquickcore.c index 183b76ae6..0b255daca 100644 --- a/bindings/gumjs/gumquickcore.c +++ b/bindings/gumjs/gumquickcore.c @@ -3,6 +3,7 @@ * Copyright (C) 2020-2022 Francesco Tamagni * Copyright (C) 2020 Marcus Mengs * Copyright (C) 2021 Abdelrahman Eid + * Copyright (C) 2024 Simon Zuckerbraun * * Licence: wxWindows Library Licence, Version 3.1 */ diff --git a/bindings/gumjs/gumv8core.cpp b/bindings/gumjs/gumv8core.cpp index 648c8defb..825560401 100644 --- a/bindings/gumjs/gumv8core.cpp +++ b/bindings/gumjs/gumv8core.cpp @@ -5,6 +5,7 @@ * Copyright (C) 2020-2022 Francesco Tamagni * Copyright (C) 2020 Marcus Mengs * Copyright (C) 2021 Abdelrahman Eid + * Copyright (C) 2024 Simon Zuckerbraun * * Licence: wxWindows Library Licence, Version 3.1 */ diff --git a/tests/gumjs/script.c b/tests/gumjs/script.c index 7a6a314df..ea14bdb7d 100644 --- a/tests/gumjs/script.c +++ b/tests/gumjs/script.c @@ -7,6 +7,7 @@ * Copyright (C) 2023 Grant Douglas * Copyright (C) 2024 Hillel Pinto * Copyright (C) 2024 Håvard Sørbø + * Copyright (C) 2024 Simon Zuckerbraun * * Licence: wxWindows Library Licence, Version 3.1 */