[openwrt/openwrt] ucode: add patches to improve exception handling for ubus/uloop

LEDE Commits lede-commits at lists.infradead.org
Sat Jul 5 05:44:23 PDT 2025


nbd pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/185b48e3309a0592d8d54f2d5ddfa781b12a311b

commit 185b48e3309a0592d8d54f2d5ddfa781b12a311b
Author: Felix Fietkau <nbd at nbd.name>
AuthorDate: Fri Jun 13 12:19:38 2025 +0200

    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 <nbd at nbd.name>
---
 ...unction-for-converting-exception-to-ucode.patch |  47 +++++
 ...-uloop-add-exception_handler_set-function.patch | 139 +++++++++++++
 ...2-ubus-add-exception_handler_set-function.patch | 217 +++++++++++++++++++++
 3 files changed, 403 insertions(+)

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 0000000000..e849510f58
--- /dev/null
+++ b/package/utils/ucode/patches/100-vm-export-function-for-converting-exception-to-ucode.patch
@@ -0,0 +1,47 @@
+From: Felix Fietkau <nbd at nbd.name>
+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 <nbd at nbd.name>
+---
+
+--- 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 0000000000..9ff2bd4b9d
--- /dev/null
+++ b/package/utils/ucode/patches/101-uloop-add-exception_handler_set-function.patch
@@ -0,0 +1,139 @@
+From: Felix Fietkau <nbd at nbd.name>
+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 <nbd at nbd.name>
+---
+
+--- 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 0000000000..3490782495
--- /dev/null
+++ b/package/utils/ucode/patches/102-ubus-add-exception_handler_set-function.patch
@@ -0,0 +1,217 @@
+From: Felix Fietkau <nbd at nbd.name>
+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 <nbd at nbd.name>
+---
+
+--- 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[] = {




More information about the lede-commits mailing list