From ca9757b9c6f34c8a436896af87fea32b96caf306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Andr=C3=A9=20Vadla=20Ravn=C3=A5s?= Date: Fri, 10 May 2024 23:21:33 +0200 Subject: [PATCH] gumjs: Always expose thread's system error to NativeCallback Also when Interceptor isn't involved. --- bindings/gumjs/gumquickcore.c | 57 ++++++++++++++++++++++++++++++++--- bindings/gumjs/gumv8core.cpp | 50 +++++++++++++++++++++++++----- bindings/gumjs/gumv8core.h | 1 - tests/gumjs/script.c | 36 ++++++++++++++++++++++ 4 files changed, 130 insertions(+), 14 deletions(-) diff --git a/bindings/gumjs/gumquickcore.c b/bindings/gumjs/gumquickcore.c index aedd317aa..47f97cf74 100644 --- a/bindings/gumjs/gumquickcore.c +++ b/bindings/gumjs/gumquickcore.c @@ -156,6 +156,7 @@ struct _GumQuickCallbackContext { JSValue wrapper; GumQuickCpuContext * cpu_context; + gint * system_error; GumAddress return_address; GumAddress raw_return_address; int initial_property_count; @@ -312,9 +313,11 @@ static void gum_quick_native_callback_invoke (ffi_cif * cif, GUMJS_DECLARE_FINALIZER (gumjs_callback_context_finalize) GUMJS_DECLARE_GETTER (gumjs_callback_context_get_return_address) GUMJS_DECLARE_GETTER (gumjs_callback_context_get_cpu_context) +GUMJS_DECLARE_GETTER (gumjs_callback_context_get_system_error) +GUMJS_DECLARE_SETTER (gumjs_callback_context_set_system_error) static JSValue gum_quick_callback_context_new (GumQuickCore * core, - GumCpuContext * cpu_context, GumAddress raw_return_address, - GumQuickCallbackContext ** context); + GumCpuContext * cpu_context, gint * system_error, + GumAddress raw_return_address, GumQuickCallbackContext ** context); static gboolean gum_quick_callback_context_get (JSContext * ctx, JSValueConst val, GumQuickCore * core, GumQuickCallbackContext ** ic); @@ -567,6 +570,9 @@ static const JSCFunctionListEntry gumjs_callback_context_entries[] = JS_CGETSET_DEF ("returnAddress", gumjs_callback_context_get_return_address, NULL), JS_CGETSET_DEF ("context", gumjs_callback_context_get_cpu_context, NULL), + JS_CGETSET_DEF (GUMJS_SYSTEM_ERROR_FIELD, + gumjs_callback_context_get_system_error, + gumjs_callback_context_set_system_error), }; static const JSClassDef gumjs_cpu_context_def = @@ -4522,7 +4528,7 @@ gum_quick_native_callback_invoke (ffi_cif * cif, #endif this_obj = gum_quick_callback_context_new (core, &cpu_context, - return_address, &jcc); + &saved_system_error, return_address, &jcc); } argc = cif->nargs; @@ -4544,6 +4550,7 @@ gum_quick_native_callback_invoke (ffi_cif * cif, if (jcc != NULL) { + jcc->system_error = NULL; JS_FreeValue (ctx, jcc->cpu_context->wrapper); jcc->cpu_context = NULL; JS_FreeValue (ctx, jcc->wrapper); @@ -4577,6 +4584,7 @@ GUMJS_DEFINE_FINALIZER (gumjs_callback_context_finalize) static JSValue gum_quick_callback_context_new (GumQuickCore * core, GumCpuContext * cpu_context, + gint * system_error, GumAddress raw_return_address, GumQuickCallbackContext ** context) { @@ -4589,6 +4597,7 @@ gum_quick_callback_context_new (GumQuickCore * core, jcc = g_slice_new (GumQuickCallbackContext); jcc->wrapper = wrapper; jcc->cpu_context = NULL; + jcc->system_error = system_error; jcc->return_address = 0; jcc->raw_return_address = raw_return_address; jcc->initial_property_count = JS_GetOwnPropertyCountUnchecked (wrapper); @@ -4647,14 +4656,52 @@ GUMJS_DEFINE_GETTER (gumjs_callback_context_get_cpu_context) return JS_DupValue (ctx, self->cpu_context->wrapper); } +GUMJS_DEFINE_GETTER (gumjs_callback_context_get_system_error) +{ + GumQuickCallbackContext * self; + + if (!gum_quick_callback_context_get (ctx, this_val, core, &self)) + return JS_EXCEPTION; + + return JS_NewInt32 (ctx, *self->system_error); +} + +GUMJS_DEFINE_SETTER (gumjs_callback_context_set_system_error) +{ + GumQuickCallbackContext * self; + gint value; + + if (!gum_quick_callback_context_get (ctx, this_val, core, &self)) + return JS_EXCEPTION; + + if (!_gum_quick_int_get (ctx, val, &value)) + return JS_EXCEPTION; + + *self->system_error = value; + + return JS_UNDEFINED; +} + static gboolean gum_quick_callback_context_get (JSContext * ctx, JSValueConst val, GumQuickCore * core, GumQuickCallbackContext ** cc) { - return _gum_quick_unwrap (ctx, val, core->callback_context_class, core, - (gpointer *) cc); + GumQuickCallbackContext * c; + + if (!_gum_quick_unwrap (ctx, val, core->callback_context_class, core, + (gpointer *) &c)) + return FALSE; + + if (c->cpu_context == NULL) + { + _gum_quick_throw_literal (ctx, "invalid operation"); + return FALSE; + } + + *cc = c; + return TRUE; } GUMJS_DEFINE_FINALIZER (gumjs_cpu_context_finalize) diff --git a/bindings/gumjs/gumv8core.cpp b/bindings/gumjs/gumv8core.cpp index b4a900c1c..c905a1917 100644 --- a/bindings/gumjs/gumv8core.cpp +++ b/bindings/gumjs/gumv8core.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2022 Ole André Vadla Ravnås + * Copyright (C) 2010-2024 Ole André Vadla Ravnås * Copyright (C) 2015 Asger Hautop Drewsen * Copyright (C) 2015 Marc Hartmayer * Copyright (C) 2020-2022 Francesco Tamagni @@ -143,6 +143,7 @@ struct GumV8CallbackContext { Global * wrapper; Global * cpu_context; + gint * system_error; GumAddress return_address; GumAddress raw_return_address; }; @@ -308,11 +309,13 @@ static void gum_v8_native_callback_invoke (ffi_cif * cif, void * return_value, void ** args, void * user_data); static GumV8CallbackContext * gum_v8_callback_context_new_persistent ( - GumV8Core * core, GumCpuContext * cpu_context, + GumV8Core * core, GumCpuContext * cpu_context, gint * system_error, GumAddress raw_return_address); static void gum_v8_callback_context_free (GumV8CallbackContext * self); GUMJS_DECLARE_GETTER (gumjs_callback_context_get_return_address) GUMJS_DECLARE_GETTER (gumjs_callback_context_get_cpu_context) +GUMJS_DECLARE_GETTER (gumjs_callback_context_get_system_error) +GUMJS_DECLARE_SETTER (gumjs_callback_context_set_system_error) GUMJS_DECLARE_CONSTRUCTOR (gumjs_cpu_context_construct) GUMJS_DECLARE_GETTER (gumjs_cpu_context_get_gpr) @@ -482,8 +485,21 @@ static const GumV8Function gumjs_native_function_functions[] = static const GumV8Property gumjs_callback_context_values[] = { - { "returnAddress", gumjs_callback_context_get_return_address, NULL }, - { "context", gumjs_callback_context_get_cpu_context, NULL }, + { + "returnAddress", + gumjs_callback_context_get_return_address, + NULL + }, + { + "context", + gumjs_callback_context_get_cpu_context, + NULL + }, + { + GUMJS_SYSTEM_ERROR_FIELD, + gumjs_callback_context_get_system_error, + gumjs_callback_context_set_system_error + }, { NULL, NULL, NULL } }; @@ -3530,7 +3546,7 @@ gum_v8_native_callback_invoke (ffi_cif * cif, #endif jcc = gum_v8_callback_context_new_persistent (self->core, &cpu_context, - return_address); + &error_scope.saved_error, return_address); recv = Local::New (isolate, *jcc->wrapper); } @@ -3566,6 +3582,7 @@ gum_v8_native_callback_invoke (ffi_cif * cif, static GumV8CallbackContext * gum_v8_callback_context_new_persistent (GumV8Core * core, GumCpuContext * cpu_context, + gint * system_error, GumAddress raw_return_address) { auto isolate = core->isolate; @@ -3576,12 +3593,13 @@ gum_v8_callback_context_new_persistent (GumV8Core * core, *core->callback_context_value); auto wrapper = callback_context_value->Clone (); wrapper->SetAlignedPointerInInternalField (0, jcc); - jcc->wrapper = new Global (isolate, wrapper); - jcc->return_address = 0; - jcc->raw_return_address = raw_return_address; + jcc->wrapper = new Global (isolate, wrapper); jcc->cpu_context = new Global (isolate, _gum_v8_cpu_context_new_immutable (cpu_context, core)); + jcc->system_error = system_error; + jcc->return_address = 0; + jcc->raw_return_address = raw_return_address; return jcc; } @@ -3639,6 +3657,22 @@ GUMJS_DEFINE_CLASS_GETTER (gumjs_callback_context_get_cpu_context, info.GetReturnValue ().Set (Local::New (isolate, *context)); } +GUMJS_DEFINE_CLASS_GETTER (gumjs_callback_context_get_system_error, + GumV8CallbackContext) +{ + info.GetReturnValue ().Set (*self->system_error); +} + +GUMJS_DEFINE_CLASS_SETTER (gumjs_callback_context_set_system_error, + GumV8CallbackContext) +{ + gint system_error; + if (!_gum_v8_int_get (value, &system_error, core)) + return; + + *self->system_error = system_error; +} + GUMJS_DEFINE_CONSTRUCTOR (gumjs_cpu_context_construct) { GumCpuContext * cpu_context = NULL; diff --git a/bindings/gumjs/gumv8core.h b/bindings/gumjs/gumv8core.h index c5853e369..f368452f6 100644 --- a/bindings/gumjs/gumv8core.h +++ b/bindings/gumjs/gumv8core.h @@ -172,7 +172,6 @@ class GumV8SystemErrorPreservationScope gum_thread_set_system_error (saved_error); } -private: gint saved_error; }; diff --git a/tests/gumjs/script.c b/tests/gumjs/script.c index 550b797fd..d0a13ce16 100644 --- a/tests/gumjs/script.c +++ b/tests/gumjs/script.c @@ -322,6 +322,7 @@ TESTLIST_BEGIN (script) TESTGROUP_BEGIN ("NativeCallback") TESTENTRY (native_callback_can_be_invoked) + TESTENTRY (native_callback_should_provide_access_to_system_error) TESTENTRY (native_callback_is_a_native_pointer) TESTENTRY (native_callback_memory_should_be_eagerly_reclaimed) TESTENTRY (native_callback_should_be_kept_alive_during_calls) @@ -2173,6 +2174,41 @@ TESTCASE (native_callback_can_be_invoked) g_assert_cmpstr (str, ==, "BADGER"); } +TESTCASE (native_callback_should_provide_access_to_system_error) +{ + void (* callback) (void); + +#ifdef HAVE_WINDOWS + COMPILE_AND_LOAD_SCRIPT ( + "const cb = new NativeCallback(function () {" + " send(this.lastError);" + " this.lastError = this.lastError + 37;" + " return 0;" + "}, 'void', []);" + GUM_PTR_CONST ".writePointer(cb);", &callback); + EXPECT_NO_MESSAGES (); + + SetLastError (1300); + callback (); + g_assert_cmpuint (GetLastError (), ==, 1337); +#else + COMPILE_AND_LOAD_SCRIPT ( + "const cb = new NativeCallback(function () {" + " send(this.errno);" + " this.errno = this.errno + 37;" + " return 0;" + "}, 'void', []);" + GUM_PTR_CONST ".writePointer(cb);", &callback); + EXPECT_NO_MESSAGES (); + + errno = 1300; + callback (); + g_assert_cmpuint (errno, ==, 1337); +#endif + + EXPECT_SEND_MESSAGE_WITH ("1300"); +} + TESTCASE (native_callback_is_a_native_pointer) { COMPILE_AND_LOAD_SCRIPT (