From: Felix Fietkau Date: Fri, 13 Jun 2025 10:19:38 +0000 (+0200) Subject: ucode: add patches to improve exception handling for ubus/uloop X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=185b48e3309a0592d8d54f2d5ddfa781b12a311b;p=thirdparty%2Fopenwrt.git ucode: add patches to improve exception handling for ubus/uloop Add API to allow setting an exception handler for user provided callbacks Signed-off-by: Felix Fietkau --- diff --git a/package/utils/ucode/patches/100-vm-export-function-for-converting-exception-to-ucode.patch b/package/utils/ucode/patches/100-vm-export-function-for-converting-exception-to-ucode.patch new file mode 100644 index 00000000000..e849510f58c --- /dev/null +++ b/package/utils/ucode/patches/100-vm-export-function-for-converting-exception-to-ucode.patch @@ -0,0 +1,47 @@ +From: Felix Fietkau +Date: Wed, 11 Jun 2025 17:35:00 +0200 +Subject: [PATCH] vm: export function for converting exception to ucode + value + +This can be used to simplify implementing module execption handlers in +ucode. + +Signed-off-by: Felix Fietkau +--- + +--- a/include/ucode/vm.h ++++ b/include/ucode/vm.h +@@ -152,6 +152,7 @@ uc_exception_type_t uc_vm_call(uc_vm_t * + + void __attribute__((format(printf, 3, 0))) + uc_vm_raise_exception(uc_vm_t *vm, uc_exception_type_t type, const char *fmt, ...); ++uc_value_t *uc_vm_exception_value(uc_vm_t *vm); + + uc_vm_status_t uc_vm_execute(uc_vm_t *vm, uc_program_t *fn, uc_value_t **retval); + uc_value_t *uc_vm_invoke(uc_vm_t *vm, const char *fname, size_t nargs, ...); +--- a/vm.c ++++ b/vm.c +@@ -814,9 +814,12 @@ uc_vm_exception_tostring(uc_vm_t *vm, si + return message ? ucv_get(message) : ucv_string_new("Exception"); + } + +-static uc_value_t * +-uc_vm_exception_new(uc_vm_t *vm, uc_exception_type_t type, const char *message, uc_value_t *stacktrace) ++uc_value_t * ++uc_vm_exception_value(uc_vm_t *vm) + { ++ uc_exception_type_t type = vm->exception.type; ++ const char *message = vm->exception.message; ++ uc_value_t *stacktrace = vm->exception.stacktrace; + uc_value_t *exception_prototype = uc_vm_registry_get(vm, "vm.exception.proto"); + uc_value_t *exo; + +@@ -881,7 +884,7 @@ uc_vm_handle_exception(uc_vm_t *vm) + ucv_put(uc_vm_stack_pop(vm)); + + /* prepare exception object and expose it to user handler code */ +- exo = uc_vm_exception_new(vm, vm->exception.type, vm->exception.message, vm->exception.stacktrace); ++ exo = uc_vm_exception_value(vm); + + uc_vm_stack_push(vm, exo); + diff --git a/package/utils/ucode/patches/101-uloop-add-exception_handler_set-function.patch b/package/utils/ucode/patches/101-uloop-add-exception_handler_set-function.patch new file mode 100644 index 00000000000..9ff2bd4b9db --- /dev/null +++ b/package/utils/ucode/patches/101-uloop-add-exception_handler_set-function.patch @@ -0,0 +1,139 @@ +From: Felix Fietkau +Date: Wed, 11 Jun 2025 18:31:39 +0200 +Subject: [PATCH] uloop: add exception_handler_set function + +This allows calling the provided handler on exceptions, avoiding the +existing behavior of calling uloop_end(). + +Signed-off-by: Felix Fietkau +--- + +--- a/lib/uloop.c ++++ b/lib/uloop.c +@@ -114,27 +114,48 @@ uc_uloop_cb_free(uc_uloop_cb_t *cb) + } + + static bool ++uc_uloop_vm_call(uc_vm_t *vm, bool mcall, size_t nargs) ++{ ++ uc_value_t *exh, *val; ++ ++ if (uc_vm_call(vm, mcall, nargs) == EXCEPTION_NONE) ++ return true; ++ ++ exh = uc_vm_registry_get(vm, "uloop.ex_handler"); ++ if (!ucv_is_callable(exh)) ++ goto error; ++ ++ val = uc_vm_exception_value(vm); ++ uc_vm_stack_push(vm, ucv_get(exh)); ++ uc_vm_stack_push(vm, ucv_get(val)); ++ ++ if (uc_vm_call(vm, false, 1) != EXCEPTION_NONE) ++ goto error; ++ ++ ucv_put(uc_vm_stack_pop(vm)); ++ ++ return false; ++ ++error: ++ uloop_end(); ++ return false; ++} ++ ++static void + uc_uloop_cb_invoke(uc_uloop_cb_t *cb, uc_value_t *arg) + { + uc_vm_t *vm = cb->vm; + uc_value_t *func = ucv_resource_value_get(cb->obj, 0); + + if (!ucv_is_callable(func)) +- return false; ++ return; + + uc_vm_stack_push(vm, ucv_get(cb->obj)); + uc_vm_stack_push(vm, ucv_get(func)); + uc_vm_stack_push(vm, ucv_get(arg)); + +- if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) { +- uloop_end(); +- +- return false; +- } +- +- ucv_put(uc_vm_stack_pop(vm)); +- +- return true; ++ if (uc_uloop_vm_call(vm, true, 1)) ++ ucv_put(uc_vm_stack_pop(vm)); + } + + /** +@@ -1550,11 +1571,8 @@ uc_uloop_task_output_cb(struct uloop_fd + uc_vm_stack_push(vm, ucv_get(obj)); + uc_vm_stack_push(vm, ucv_get(task->input_cb)); + +- if (uc_vm_call(vm, true, 0) != EXCEPTION_NONE) { +- uloop_end(); +- ++ if (!uc_uloop_vm_call(vm, true, 0)) + return; +- } + + msg = uc_vm_stack_pop(vm); + uc_uloop_pipe_send_common(vm, msg, task->input_fd); +@@ -1572,14 +1590,10 @@ uc_uloop_task_output_cb(struct uloop_fd + uc_vm_stack_push(vm, ucv_get(task->output_cb)); + uc_vm_stack_push(vm, msg); + +- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE) { +- ucv_put(uc_vm_stack_pop(vm)); +- } +- else { +- uloop_end(); +- ++ if (!uc_uloop_vm_call(vm, true, 1)) + return; +- } ++ ++ ucv_put(uc_vm_stack_pop(vm)); + } + else { + ucv_put(msg); +@@ -1703,7 +1717,7 @@ uc_uloop_task(uc_vm_t *vm, size_t nargs) + uc_vm_stack_push(vm, func); + uc_vm_stack_push(vm, ucv_get(p)); + +- if (uc_vm_call(vm, false, 1) == EXCEPTION_NONE) { ++ if (uc_uloop_vm_call(vm, false, 1)) { + res = uc_vm_stack_pop(vm); + uc_uloop_pipe_send_common(vm, res, tpipe->output); + ucv_put(res); +@@ -2167,6 +2181,19 @@ uc_uloop_signal(uc_vm_t *vm, size_t narg + } + #endif + ++static uc_value_t * ++uc_uloop_exception_handler_set(uc_vm_t *vm, size_t nargs) ++{ ++ uc_value_t *arg = uc_fn_arg(0); ++ ++ if (arg && !ucv_is_callable(arg)) ++ return NULL; ++ ++ uc_vm_registry_set(vm, "uloop.ex_handler", ucv_get(arg)); ++ ++ return ucv_boolean_new(true); ++} ++ + + static const uc_function_list_t timer_fns[] = { + { "set", uc_uloop_timer_set }, +@@ -2233,6 +2260,7 @@ static const uc_function_list_t global_f + #ifdef HAVE_ULOOP_SIGNAL + { "signal", uc_uloop_signal }, + #endif ++ { "exception_handler_set", uc_uloop_exception_handler_set }, + }; + + diff --git a/package/utils/ucode/patches/102-ubus-add-exception_handler_set-function.patch b/package/utils/ucode/patches/102-ubus-add-exception_handler_set-function.patch new file mode 100644 index 00000000000..34907824953 --- /dev/null +++ b/package/utils/ucode/patches/102-ubus-add-exception_handler_set-function.patch @@ -0,0 +1,217 @@ +From: Felix Fietkau +Date: Wed, 11 Jun 2025 18:40:10 +0200 +Subject: [PATCH] ubus: add exception_handler_set function + +This allows calling the provided handler on exceptions, avoiding the +existing behavior of calling uloop_end(). + +Signed-off-by: Felix Fietkau +--- + +--- a/lib/ubus.c ++++ b/lib/ubus.c +@@ -573,6 +573,40 @@ uc_ubus_call_cb(struct ubus_request *req + } + + static void ++uc_ubus_vm_handle_exception(uc_vm_t *vm) ++{ ++ uc_value_t *exh, *val; ++ ++ exh = uc_vm_registry_get(vm, "ubus.ex_handler"); ++ if (!ucv_is_callable(exh)) ++ goto error; ++ ++ val = uc_vm_exception_value(vm); ++ uc_vm_stack_push(vm, ucv_get(exh)); ++ uc_vm_stack_push(vm, ucv_get(val)); ++ ++ if (uc_vm_call(vm, false, 1) != EXCEPTION_NONE) ++ goto error; ++ ++ ucv_put(uc_vm_stack_pop(vm)); ++ return; ++ ++error: ++ uloop_end(); ++} ++ ++static bool ++uc_ubus_vm_call(uc_vm_t *vm, bool mcall, size_t nargs) ++{ ++ if (uc_vm_call(vm, mcall, nargs) == EXCEPTION_NONE) ++ return true; ++ ++ uc_ubus_vm_handle_exception(vm); ++ ++ return false; ++} ++ ++static void + uc_ubus_call_user_cb(uc_ubus_deferred_t *defer, int ret, uc_value_t *reply) + { + uc_value_t *this = ucv_get(defer->res); +@@ -587,7 +621,7 @@ uc_ubus_call_user_cb(uc_ubus_deferred_t + uc_vm_stack_push(vm, ucv_int64_new(ret)); + uc_vm_stack_push(vm, ucv_get(reply)); + +- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE) ++ if (uc_ubus_vm_call(vm, true, 2)) + ucv_put(uc_vm_stack_pop(vm)); + } + +@@ -623,10 +657,8 @@ uc_ubus_call_data_user_cb(struct ubus_re + uc_vm_stack_push(vm, ucv_get(func)); + uc_vm_stack_push(vm, ucv_get(reply)); + +- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE) ++ if (uc_ubus_vm_call(vm, true, 1)) + ucv_put(uc_vm_stack_pop(vm)); +- else +- uloop_end(); + } + } + +@@ -645,10 +677,8 @@ uc_ubus_call_fd_cb(struct ubus_request * + uc_vm_stack_push(vm, ucv_get(func)); + uc_vm_stack_push(vm, ucv_int64_new(fd)); + +- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE) ++ if (uc_ubus_vm_call(vm, true, 1)) + ucv_put(uc_vm_stack_pop(vm)); +- else +- uloop_end(); + } + } + +@@ -927,10 +957,8 @@ uc_ubus_defer_common(uc_vm_t *vm, uc_ubu + uc_vm_stack_push(vm, ucv_get(replycb)); + uc_vm_stack_push(vm, ucv_int64_new(rv)); + +- if (uc_vm_call(vm, false, 1) == EXCEPTION_NONE) ++ if (uc_ubus_vm_call(vm, false, 1)) + ucv_put(uc_vm_stack_pop(vm)); +- else +- uloop_end(); + + ucv_put(res->res); + } +@@ -1199,10 +1227,8 @@ uc_ubus_object_notify_data_cb(struct ubu + uc_vm_stack_push(vm, ucv_int64_new(type)); + uc_vm_stack_push(vm, blob_array_to_ucv(vm, blob_data(msg), blob_len(msg), true)); + +- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE) ++ if (uc_ubus_vm_call(vm, true, 2)) + ucv_put(uc_vm_stack_pop(vm)); +- else +- uloop_end(); + } + } + +@@ -1222,10 +1248,8 @@ uc_ubus_object_notify_status_cb(struct u + uc_vm_stack_push(vm, ucv_int64_new(idx)); + uc_vm_stack_push(vm, ucv_int64_new(ret)); + +- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE) ++ if (uc_ubus_vm_call(vm, true, 2)) + ucv_put(uc_vm_stack_pop(vm)); +- else +- uloop_end(); + } + } + +@@ -1245,10 +1269,8 @@ uc_ubus_object_notify_complete_cb(struct + uc_vm_stack_push(vm, ucv_int64_new(idx)); + uc_vm_stack_push(vm, ucv_int64_new(ret)); + +- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE) ++ if (uc_ubus_vm_call(vm, true, 2)) + ucv_put(uc_vm_stack_pop(vm)); +- else +- uloop_end(); + } + + notifyctx->complete = true; +@@ -1579,7 +1601,7 @@ uc_ubus_handle_reply_common(struct ubus_ + /* treat other exceptions as fatal and halt uloop */ + default: + uc_ubus_request_finish_common(callctx, UBUS_STATUS_UNKNOWN_ERROR); +- uloop_end(); ++ uc_ubus_vm_handle_exception(vm); + break; + } + +@@ -1638,10 +1660,8 @@ uc_ubus_object_subscribe_cb(struct ubus_ + uc_vm_stack_push(uuobj->vm, ucv_get(uuobj->res)); + uc_vm_stack_push(uuobj->vm, ucv_get(func)); + +- if (uc_vm_call(uuobj->vm, true, 0) == EXCEPTION_NONE) ++ if (uc_ubus_vm_call(uuobj->vm, true, 0)) + ucv_put(uc_vm_stack_pop(uuobj->vm)); +- else +- uloop_end(); + } + + static bool +@@ -1917,10 +1937,8 @@ uc_ubus_listener_cb(struct ubus_context + uc_vm_stack_push(vm, ucv_string_new(type)); + uc_vm_stack_push(vm, blob_array_to_ucv(vm, blob_data(msg), blob_len(msg), true)); + +- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE) ++ if (uc_ubus_vm_call(vm, true, 2)) + ucv_put(uc_vm_stack_pop(vm)); +- else +- uloop_end(); + } + + static uc_value_t * +@@ -2040,10 +2058,8 @@ uc_ubus_subscriber_remove_cb(struct ubus + uc_vm_stack_push(vm, ucv_get(func)); + uc_vm_stack_push(vm, ucv_uint64_new(id)); + +- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE) ++ if (uc_ubus_vm_call(vm, true, 1)) + ucv_put(uc_vm_stack_pop(vm)); +- else +- uloop_end(); + } + + static uc_value_t * +@@ -2334,10 +2350,8 @@ uc_ubus_channel_disconnect_cb(struct ubu + uc_vm_stack_push(c->vm, ucv_get(c->res)); + uc_vm_stack_push(c->vm, ucv_get(func)); + +- if (uc_vm_call(c->vm, true, 0) == EXCEPTION_NONE) ++ if (uc_ubus_vm_call(c->vm, true, 0)) + ucv_put(uc_vm_stack_pop(c->vm)); +- else +- uloop_end(); + } + + blob_buf_free(&c->buf); +@@ -2438,10 +2452,25 @@ uc_ubus_channel_connect(uc_vm_t *vm, siz + } + + ++static uc_value_t * ++uc_ubus_exception_handler_set(uc_vm_t *vm, size_t nargs) ++{ ++ uc_value_t *arg = uc_fn_arg(0); ++ ++ if (arg && !ucv_is_callable(arg)) ++ return NULL; ++ ++ uc_vm_registry_set(vm, "ubus.ex_handler", ucv_get(arg)); ++ ++ return ucv_boolean_new(true); ++} ++ ++ + static const uc_function_list_t global_fns[] = { + { "error", uc_ubus_error }, + { "connect", uc_ubus_connect }, + { "open_channel", uc_ubus_channel_connect }, ++ { "exception_handler_set", uc_ubus_exception_handler_set }, + }; + + static const uc_function_list_t conn_fns[] = {