From 4439cb6068bc22243395d1372a7708e065a698e2 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 7 Mar 2025 16:17:27 +0100 Subject: [PATCH] ucode: add pending ubus patches Signed-off-by: Felix Fietkau --- .../100-ubus-add-channel-defer-method.patch | 155 ++++++++++++++++ ...rn-argument-to-call-and-channel-requ.patch | 88 +++++++++ ...e-data-callback-for-ubus-defer-calls.patch | 171 ++++++++++++++++++ ...ding-multiple-reply-messages-on-a-re.patch | 110 +++++++++++ ...al-use-after-free-on-channel-disconn.patch | 28 +++ ...en-implied-await-when-calling-defer-.patch | 105 +++++++++++ 6 files changed, 657 insertions(+) create mode 100644 package/utils/ucode/patches/100-ubus-add-channel-defer-method.patch create mode 100644 package/utils/ucode/patches/101-ubus-add-no_return-argument-to-call-and-channel-requ.patch create mode 100644 package/utils/ucode/patches/102-ubus-add-separate-data-callback-for-ubus-defer-calls.patch create mode 100644 package/utils/ucode/patches/103-ubus-support-sending-multiple-reply-messages-on-a-re.patch create mode 100644 package/utils/ucode/patches/104-ubus-fix-potential-use-after-free-on-channel-disconn.patch create mode 100644 package/utils/ucode/patches/105-ubus-remove-broken-implied-await-when-calling-defer-.patch diff --git a/package/utils/ucode/patches/100-ubus-add-channel-defer-method.patch b/package/utils/ucode/patches/100-ubus-add-channel-defer-method.patch new file mode 100644 index 0000000000..7c5aa81ad7 --- /dev/null +++ b/package/utils/ucode/patches/100-ubus-add-channel-defer-method.patch @@ -0,0 +1,155 @@ +From: Felix Fietkau +Date: Tue, 4 Mar 2025 12:01:09 +0100 +Subject: [PATCH] ubus: add channel defer() method + +Works in the same way as regular defer calls, except that it operates on +channels. + +Signed-off-by: Felix Fietkau +--- + +--- a/lib/ubus.c ++++ b/lib/ubus.c +@@ -878,44 +878,30 @@ uc_ubus_chan_request(uc_vm_t *vm, size_t + ok_return(res.res); + } + +-static uc_value_t * +-uc_ubus_defer(uc_vm_t *vm, size_t nargs) ++static int ++uc_ubus_defer_common(uc_vm_t *vm, uc_ubus_connection_t *c, uc_ubus_call_res_t *res, ++ uint32_t id, uc_value_t *funname, uc_value_t *funargs, ++ uc_value_t *fd, uc_value_t *fdcb, uc_value_t *replycb) + { +- uc_value_t *objname, *funname, *funargs, *replycb, *fd, *fdcb, *conn, *res = NULL; + uc_ubus_deferred_t *defer; +- uc_ubus_connection_t *c; + enum ubus_msg_status rv; + uc_callframe_t *frame; +- uint32_t id; ++ uc_value_t *conn; + int fd_val = -1; + +- conn_get(vm, &c); +- +- args_get_named(vm, nargs, +- "object", UC_STRING, REQUIRED, &objname, +- "method", UC_STRING, REQUIRED, &funname, +- "data", UC_OBJECT, OPTIONAL, &funargs, +- "cb", UC_CLOSURE, OPTIONAL, &replycb, +- "fd", 0, NAMED, &fd, +- "fd_cb", UC_CLOSURE, NAMED, &fdcb); +- + blob_buf_init(&c->buf, 0); + + if (funargs) + ucv_object_to_blob(funargs, &c->buf); +- + if (fd) { + fd_val = get_fd(vm, fd); +- if (fd_val < 0) +- err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid file descriptor argument"); ++ if (fd_val < 0) { ++ rv = UBUS_STATUS_INVALID_ARGUMENT; ++ set_error(rv, "Invalid file descriptor argument"); ++ return rv; ++ } + } + +- rv = ubus_lookup_id(&c->ctx, ucv_string_get(objname), &id); +- +- if (rv != UBUS_STATUS_OK) +- err_return(rv, "Failed to resolve object name '%s'", +- ucv_string_get(objname)); +- + defer = xalloc(sizeof(*defer)); + + rv = ubus_invoke_async_fd(&c->ctx, id, ucv_string_get(funname), +@@ -935,11 +921,11 @@ uc_ubus_defer(uc_vm_t *vm, size_t nargs) + defer->timeout.cb = uc_ubus_call_timeout_cb; + uloop_timeout_set(&defer->timeout, c->timeout * 1000); + +- res = uc_resource_new(defer_type, defer); ++ res->res = uc_resource_new(defer_type, defer); + frame = uc_vector_last(&vm->callframes); + conn = frame ? frame->ctx : NULL; + +- defer->registry_index = request_reg_add(vm, ucv_get(res), ucv_get(replycb), ucv_get(fdcb), ucv_get(conn), ucv_get(fd)); ++ defer->registry_index = request_reg_add(vm, ucv_get(res->res), ucv_get(replycb), ucv_get(fdcb), ucv_get(conn), ucv_get(fd)); + + if (!uc_ubus_have_uloop()) { + have_own_uloop = true; +@@ -958,11 +944,64 @@ uc_ubus_defer(uc_vm_t *vm, size_t nargs) + free(defer); + } + ++ return rv; ++} ++ ++static uc_value_t * ++uc_ubus_defer(uc_vm_t *vm, size_t nargs) ++{ ++ uc_value_t *objname, *funname, *funargs, *replycb, *fd, *fdcb = NULL; ++ uc_ubus_call_res_t res = { 0 }; ++ uc_ubus_connection_t *c; ++ uint32_t id; ++ int rv; ++ ++ conn_get(vm, &c); ++ ++ rv = ubus_lookup_id(&c->ctx, ucv_string_get(objname), &id); ++ if (rv != UBUS_STATUS_OK) ++ err_return(rv, "Failed to resolve object name '%s'", ++ ucv_string_get(objname)); ++ ++ args_get_named(vm, nargs, ++ "object", UC_STRING, REQUIRED, &objname, ++ "method", UC_STRING, REQUIRED, &funname, ++ "data", UC_OBJECT, OPTIONAL, &funargs, ++ "cb", UC_CLOSURE, OPTIONAL, &replycb, ++ "fd", 0, NAMED, &fd, ++ "fd_cb", UC_CLOSURE, NAMED, &fdcb); ++ ++ rv = uc_ubus_defer_common(vm, c, &res, id, funname, funargs, fd, fdcb, replycb); + if (rv != UBUS_STATUS_OK) + err_return(rv, "Failed to invoke function '%s' on object '%s'", + ucv_string_get(funname), ucv_string_get(objname)); + +- ok_return(res); ++ ok_return(res.res); ++} ++ ++static uc_value_t * ++uc_ubus_chan_defer(uc_vm_t *vm, size_t nargs) ++{ ++ uc_value_t *funname, *funargs, *replycb, *fd, *fdcb = NULL; ++ uc_ubus_call_res_t res = { 0 }; ++ uc_ubus_connection_t *c; ++ int rv; ++ ++ conn_get(vm, &c); ++ ++ args_get_named(vm, nargs, ++ "method", UC_STRING, REQUIRED, &funname, ++ "data", UC_OBJECT, OPTIONAL, &funargs, ++ "cb", UC_CLOSURE, OPTIONAL, &replycb, ++ "fd", 0, NAMED, &fd, ++ "fd_cb", UC_CLOSURE, NAMED, &fdcb); ++ ++ rv = uc_ubus_defer_common(vm, c, &res, 0, funname, funargs, fd, fdcb, replycb); ++ if (rv != UBUS_STATUS_OK) ++ err_return(rv, "Failed to invoke function '%s' on channel", ++ ucv_string_get(funname)); ++ ++ ok_return(res.res); + } + + +@@ -2384,6 +2423,7 @@ static const uc_function_list_t conn_fns + + static const uc_function_list_t chan_fns[] = { + { "request", uc_ubus_chan_request }, ++ { "defer", uc_ubus_chan_defer }, + { "error", uc_ubus_error }, + { "disconnect", uc_ubus_disconnect }, + }; diff --git a/package/utils/ucode/patches/101-ubus-add-no_return-argument-to-call-and-channel-requ.patch b/package/utils/ucode/patches/101-ubus-add-no_return-argument-to-call-and-channel-requ.patch new file mode 100644 index 0000000000..0f653d24e6 --- /dev/null +++ b/package/utils/ucode/patches/101-ubus-add-no_return-argument-to-call-and-channel-requ.patch @@ -0,0 +1,88 @@ +From: Felix Fietkau +Date: Tue, 4 Mar 2025 13:17:49 +0100 +Subject: [PATCH] ubus: add no_return argument to call and channel request + +Allows issuing a call without waiting for any result. This is useful +for implementing non-blocking notification calls without the overhead +of extra request tracking. + +Signed-off-by: Felix Fietkau +--- + +--- a/lib/ubus.c ++++ b/lib/ubus.c +@@ -764,7 +764,8 @@ get_fd(uc_vm_t *vm, uc_value_t *val) + static int + uc_ubus_call_common(uc_vm_t *vm, uc_ubus_connection_t *c, uc_ubus_call_res_t *res, + uint32_t id, uc_value_t *funname, uc_value_t *funargs, +- uc_value_t *fd, uc_value_t *fdcb, uc_value_t *mret) ++ uc_value_t *fd, uc_value_t *fdcb, uc_value_t *noret, ++ uc_value_t *mret) + { + uc_ubus_deferred_t defer = {}; + enum ubus_msg_status rv; +@@ -796,8 +797,12 @@ uc_ubus_call_common(uc_vm_t *vm, uc_ubus + defer.registry_index = request_reg_add(vm, NULL, NULL, ucv_get(fdcb), NULL, NULL); + } + +- if (rv == UBUS_STATUS_OK) +- rv = ubus_complete_request(&c->ctx, &defer.request, c->timeout * 1000); ++ if (rv == UBUS_STATUS_OK) { ++ if (ucv_is_truish(noret)) ++ ubus_abort_request(&c->ctx, &defer.request); ++ else ++ rv = ubus_complete_request(&c->ctx, &defer.request, c->timeout * 1000); ++ } + + if (defer.request.fd_cb) + request_reg_clear(vm, defer.registry_index); +@@ -808,7 +813,7 @@ uc_ubus_call_common(uc_vm_t *vm, uc_ubus + static uc_value_t * + uc_ubus_call(uc_vm_t *vm, size_t nargs) + { +- uc_value_t *obj, *funname, *funargs, *fd, *fdcb, *mret = NULL; ++ uc_value_t *obj, *funname, *funargs, *fd, *fdcb, *noret, *mret = NULL; + uc_ubus_call_res_t res = { 0 }; + uc_ubus_connection_t *c; + enum ubus_msg_status rv; +@@ -819,6 +824,7 @@ uc_ubus_call(uc_vm_t *vm, size_t nargs) + "method", UC_STRING, REQUIRED, &funname, + "data", UC_OBJECT, OPTIONAL, &funargs, + "multiple_return", UC_BOOLEAN, OPTIONAL, &mret, ++ "no_return", UC_BOOLEAN, NAMED, &noret, + "fd", 0, NAMED, &fd, + "fd_cb", UC_CLOSURE, NAMED, &fdcb); + +@@ -839,7 +845,7 @@ uc_ubus_call(uc_vm_t *vm, size_t nargs) + "Argument object is not string or integer"); + } + +- rv = uc_ubus_call_common(vm, c, &res, id, funname, funargs, fd, fdcb, mret); ++ rv = uc_ubus_call_common(vm, c, &res, id, funname, funargs, fd, fdcb, noret, mret); + + if (rv != UBUS_STATUS_OK) { + if (ucv_type(obj) == UC_STRING) +@@ -856,7 +862,7 @@ uc_ubus_call(uc_vm_t *vm, size_t nargs) + static uc_value_t * + uc_ubus_chan_request(uc_vm_t *vm, size_t nargs) + { +- uc_value_t *funname, *funargs, *fd, *fdcb, *mret = NULL; ++ uc_value_t *funname, *funargs, *fd, *fdcb, *noret, *mret = NULL; + uc_ubus_call_res_t res = { 0 }; + uc_ubus_connection_t *c; + enum ubus_msg_status rv; +@@ -865,12 +871,13 @@ uc_ubus_chan_request(uc_vm_t *vm, size_t + "method", UC_STRING, REQUIRED, &funname, + "data", UC_OBJECT, OPTIONAL, &funargs, + "multiple_return", UC_BOOLEAN, OPTIONAL, &mret, ++ "no_return", UC_BOOLEAN, NAMED, &noret, + "fd", 0, NAMED, &fd, + "fd_cb", UC_CLOSURE, NAMED, &fdcb); + + conn_get(vm, &c); + +- rv = uc_ubus_call_common(vm, c, &res, 0, funname, funargs, fd, fdcb, mret); ++ rv = uc_ubus_call_common(vm, c, &res, 0, funname, funargs, fd, fdcb, noret, mret); + if (rv != UBUS_STATUS_OK) + err_return(rv, "Failed to send request '%s' on channel", + ucv_string_get(funname)); diff --git a/package/utils/ucode/patches/102-ubus-add-separate-data-callback-for-ubus-defer-calls.patch b/package/utils/ucode/patches/102-ubus-add-separate-data-callback-for-ubus-defer-calls.patch new file mode 100644 index 0000000000..a4e25141c5 --- /dev/null +++ b/package/utils/ucode/patches/102-ubus-add-separate-data-callback-for-ubus-defer-calls.patch @@ -0,0 +1,171 @@ +From: Felix Fietkau +Date: Tue, 4 Mar 2025 14:32:25 +0100 +Subject: [PATCH] ubus: add separate data callback for ubus defer calls + +This allows receiving one call per data message, which makes it possible to +properly deal with multiple response messages. + +Signed-off-by: Felix Fietkau +--- + +--- a/lib/ubus.c ++++ b/lib/ubus.c +@@ -306,14 +306,14 @@ _uc_reg_clear(uc_vm_t *vm, const char *k + _uc_reg_clear(vm, "ubus.connections", idx, 4) + + +-#define request_reg_add(vm, request, cb, fdcb, conn, fd) \ +- _uc_reg_add(vm, "ubus.requests", 5, request, cb, fdcb, conn, fd) ++#define request_reg_add(vm, request, cb, datacb, fdcb, conn, fd) \ ++ _uc_reg_add(vm, "ubus.requests", 6, request, cb, datacb, fdcb, conn, fd) + +-#define request_reg_get(vm, idx, request, cb, fdcb) \ +- _uc_reg_get(vm, "ubus.requests", idx, 3, request, cb, fdcb) ++#define request_reg_get(vm, idx, request, cb, datacb, fdcb) \ ++ _uc_reg_get(vm, "ubus.requests", idx, 4, request, cb, datacb, fdcb) + + #define request_reg_clear(vm, idx) \ +- _uc_reg_clear(vm, "ubus.requests", idx, 5) ++ _uc_reg_clear(vm, "ubus.requests", idx, 6) + + + #define object_reg_add(vm, obj, msg, cb) \ +@@ -636,7 +636,7 @@ uc_ubus_call_user_cb(uc_ubus_deferred_t + { + uc_value_t *this, *func; + +- request_reg_get(defer->vm, defer->registry_index, &this, &func, NULL); ++ request_reg_get(defer->vm, defer->registry_index, &this, &func, NULL, NULL); + + if (ucv_is_callable(func)) { + uc_vm_stack_push(defer->vm, ucv_get(this)); +@@ -666,6 +666,26 @@ uc_ubus_call_data_cb(struct ubus_request + } + + static void ++uc_ubus_call_data_user_cb(struct ubus_request *req, int type, struct blob_attr *msg) ++{ ++ uc_ubus_deferred_t *defer = container_of(req, uc_ubus_deferred_t, request); ++ uc_value_t *this, *func, *reply; ++ ++ request_reg_get(defer->vm, defer->registry_index, &this, NULL, &func, NULL); ++ ++ if (ucv_is_callable(func)) { ++ reply = blob_array_to_ucv(defer->vm, blob_data(msg), blob_len(msg), true); ++ ++ uc_vm_stack_push(defer->vm, ucv_get(this)); ++ uc_vm_stack_push(defer->vm, ucv_get(func)); ++ uc_vm_stack_push(defer->vm, ucv_get(reply)); ++ ++ if (uc_vm_call(defer->vm, true, 1) == EXCEPTION_NONE) ++ ucv_put(uc_vm_stack_pop(defer->vm)); ++ } ++} ++ ++static void + uc_ubus_call_fd_cb(struct ubus_request *req, int fd) + { + uc_ubus_deferred_t *defer = container_of(req, uc_ubus_deferred_t, request); +@@ -674,7 +694,7 @@ uc_ubus_call_fd_cb(struct ubus_request * + if (defer->complete) + return; + +- request_reg_get(defer->vm, defer->registry_index, &this, NULL, &func); ++ request_reg_get(defer->vm, defer->registry_index, &this, NULL, NULL, &func); + if (ucv_is_callable(func)) { + uc_vm_stack_push(defer->vm, ucv_get(this)); + uc_vm_stack_push(defer->vm, ucv_get(func)); +@@ -794,7 +814,7 @@ uc_ubus_call_common(uc_vm_t *vm, uc_ubus + defer.request.priv = res; + if (ucv_is_callable(fdcb)) { + defer.request.fd_cb = uc_ubus_call_fd_cb; +- defer.registry_index = request_reg_add(vm, NULL, NULL, ucv_get(fdcb), NULL, NULL); ++ defer.registry_index = request_reg_add(vm, NULL, NULL, NULL, ucv_get(fdcb), NULL, NULL); + } + + if (rv == UBUS_STATUS_OK) { +@@ -888,7 +908,8 @@ uc_ubus_chan_request(uc_vm_t *vm, size_t + static int + uc_ubus_defer_common(uc_vm_t *vm, uc_ubus_connection_t *c, uc_ubus_call_res_t *res, + uint32_t id, uc_value_t *funname, uc_value_t *funargs, +- uc_value_t *fd, uc_value_t *fdcb, uc_value_t *replycb) ++ uc_value_t *fd, uc_value_t *fdcb, uc_value_t *replycb, ++ uc_value_t *datacb) + { + uc_ubus_deferred_t *defer; + enum ubus_msg_status rv; +@@ -918,7 +939,10 @@ uc_ubus_defer_common(uc_vm_t *vm, uc_ubu + defer->vm = vm; + defer->ctx = &c->ctx; + +- defer->request.data_cb = uc_ubus_call_data_cb; ++ if (ucv_is_callable(datacb)) ++ defer->request.data_cb = uc_ubus_call_data_user_cb; ++ else ++ defer->request.data_cb = uc_ubus_call_data_cb; + if (ucv_is_callable(fdcb)) + defer->request.fd_cb = uc_ubus_call_fd_cb; + defer->request.complete_cb = uc_ubus_call_done_cb; +@@ -932,7 +956,8 @@ uc_ubus_defer_common(uc_vm_t *vm, uc_ubu + frame = uc_vector_last(&vm->callframes); + conn = frame ? frame->ctx : NULL; + +- defer->registry_index = request_reg_add(vm, ucv_get(res->res), ucv_get(replycb), ucv_get(fdcb), ucv_get(conn), ucv_get(fd)); ++ defer->registry_index = request_reg_add(vm, ucv_get(res->res), ucv_get(replycb), ucv_get(datacb), ++ ucv_get(fdcb), ucv_get(conn), ucv_get(fd)); + + if (!uc_ubus_have_uloop()) { + have_own_uloop = true; +@@ -957,7 +982,7 @@ uc_ubus_defer_common(uc_vm_t *vm, uc_ubu + static uc_value_t * + uc_ubus_defer(uc_vm_t *vm, size_t nargs) + { +- uc_value_t *objname, *funname, *funargs, *replycb, *fd, *fdcb = NULL; ++ uc_value_t *objname, *funname, *funargs, *replycb, *datacb, *fd, *fdcb = NULL; + uc_ubus_call_res_t res = { 0 }; + uc_ubus_connection_t *c; + uint32_t id; +@@ -975,10 +1000,11 @@ uc_ubus_defer(uc_vm_t *vm, size_t nargs) + "method", UC_STRING, REQUIRED, &funname, + "data", UC_OBJECT, OPTIONAL, &funargs, + "cb", UC_CLOSURE, OPTIONAL, &replycb, ++ "data_cb", UC_CLOSURE, OPTIONAL, &datacb, + "fd", 0, NAMED, &fd, + "fd_cb", UC_CLOSURE, NAMED, &fdcb); + +- rv = uc_ubus_defer_common(vm, c, &res, id, funname, funargs, fd, fdcb, replycb); ++ rv = uc_ubus_defer_common(vm, c, &res, id, funname, funargs, fd, fdcb, replycb, datacb); + if (rv != UBUS_STATUS_OK) + err_return(rv, "Failed to invoke function '%s' on object '%s'", + ucv_string_get(funname), ucv_string_get(objname)); +@@ -989,7 +1015,7 @@ uc_ubus_defer(uc_vm_t *vm, size_t nargs) + static uc_value_t * + uc_ubus_chan_defer(uc_vm_t *vm, size_t nargs) + { +- uc_value_t *funname, *funargs, *replycb, *fd, *fdcb = NULL; ++ uc_value_t *funname, *funargs, *replycb, *datacb, *fd, *fdcb = NULL; + uc_ubus_call_res_t res = { 0 }; + uc_ubus_connection_t *c; + int rv; +@@ -1000,10 +1026,11 @@ uc_ubus_chan_defer(uc_vm_t *vm, size_t n + "method", UC_STRING, REQUIRED, &funname, + "data", UC_OBJECT, OPTIONAL, &funargs, + "cb", UC_CLOSURE, OPTIONAL, &replycb, ++ "data_cb", UC_CLOSURE, OPTIONAL, &datacb, + "fd", 0, NAMED, &fd, + "fd_cb", UC_CLOSURE, NAMED, &fdcb); + +- rv = uc_ubus_defer_common(vm, c, &res, 0, funname, funargs, fd, fdcb, replycb); ++ rv = uc_ubus_defer_common(vm, c, &res, 0, funname, funargs, fd, fdcb, replycb, datacb); + if (rv != UBUS_STATUS_OK) + err_return(rv, "Failed to invoke function '%s' on channel", + ucv_string_get(funname)); +@@ -1520,7 +1547,7 @@ uc_ubus_handle_reply_common(struct ubus_ + + /* Add wrapped request context into registry to prevent GC'ing + * until reply or timeout occurred */ +- callctx->registry_index = request_reg_add(vm, ucv_get(reqobj), NULL, NULL, NULL, NULL); ++ callctx->registry_index = request_reg_add(vm, ucv_get(reqobj), NULL, NULL, NULL, NULL, NULL); + } + + /* Otherwise, when the function returned an object, treat it as diff --git a/package/utils/ucode/patches/103-ubus-support-sending-multiple-reply-messages-on-a-re.patch b/package/utils/ucode/patches/103-ubus-support-sending-multiple-reply-messages-on-a-re.patch new file mode 100644 index 0000000000..283c6ae948 --- /dev/null +++ b/package/utils/ucode/patches/103-ubus-support-sending-multiple-reply-messages-on-a-re.patch @@ -0,0 +1,110 @@ +From: Felix Fietkau +Date: Tue, 4 Mar 2025 14:47:46 +0100 +Subject: [PATCH] ubus: support sending multiple reply messages on a + request + +When passing true in the return code argument, assume that more reply calls +are coming and only send a reply data message without the status code. + +Signed-off-by: Felix Fietkau +--- + +--- a/lib/ubus.c ++++ b/lib/ubus.c +@@ -1058,16 +1058,21 @@ uc_ubus_request_finish_common(uc_ubus_re + } + + static void +-uc_ubus_request_finish(uc_ubus_request_t *callctx, int code, uc_value_t *reply) ++uc_ubus_request_send_reply(uc_ubus_request_t *callctx, uc_value_t *reply) + { +- if (callctx->replied) ++ if (!reply) + return; + +- if (reply) { +- blob_buf_init(&buf, 0); +- ucv_object_to_blob(reply, &buf); +- ubus_send_reply(callctx->ctx, &callctx->req, buf.head); +- } ++ blob_buf_init(&buf, 0); ++ ucv_object_to_blob(reply, &buf); ++ ubus_send_reply(callctx->ctx, &callctx->req, buf.head); ++} ++ ++static void ++uc_ubus_request_finish(uc_ubus_request_t *callctx, int code) ++{ ++ if (callctx->replied) ++ return; + + uc_ubus_request_finish_common(callctx, code); + request_reg_clear(callctx->vm, callctx->registry_index); +@@ -1078,7 +1083,7 @@ uc_ubus_request_timeout(struct uloop_tim + { + uc_ubus_request_t *callctx = container_of(timeout, uc_ubus_request_t, timeout); + +- uc_ubus_request_finish(callctx, UBUS_STATUS_TIMEOUT, NULL); ++ uc_ubus_request_finish(callctx, UBUS_STATUS_TIMEOUT); + } + + static uc_value_t * +@@ -1087,25 +1092,36 @@ uc_ubus_request_reply(uc_vm_t *vm, size_ + uc_ubus_request_t **callctx = uc_fn_this("ubus.request"); + int64_t code = UBUS_STATUS_OK; + uc_value_t *reply, *rcode; ++ bool more = false; + + if (!callctx || !*callctx) + err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid call context"); + + args_get(vm, nargs, +- "reply", UC_OBJECT, true, &reply, +- "rcode", UC_INTEGER, true, &rcode); ++ "reply", UC_OBJECT, OPTIONAL, &reply, ++ "rcode", 0, OPTIONAL, &rcode); + + if ((*callctx)->replied) + err_return(UBUS_STATUS_INVALID_ARGUMENT, "Reply has already been sent"); + +- if (rcode) { ++ switch (ucv_type(rcode)) { ++ case UC_INTEGER: + code = ucv_int64_get(rcode); + + if (errno == ERANGE || code < 0 || code > __UBUS_STATUS_LAST) + code = UBUS_STATUS_UNKNOWN_ERROR; ++ break; ++ case UC_BOOLEAN: ++ case UC_NULL: ++ more = ucv_is_truish(rcode); ++ break; ++ default: ++ err_return(UBUS_STATUS_INVALID_ARGUMENT, "Argument is not integer or bool"); + } + +- uc_ubus_request_finish(*callctx, code, reply); ++ uc_ubus_request_send_reply(*callctx, reply); ++ if (!more) ++ uc_ubus_request_finish(*callctx, code); + + ok_return(ucv_boolean_new(true)); + } +@@ -1171,7 +1187,7 @@ uc_ubus_request_error(uc_vm_t *vm, size_ + if (errno == ERANGE || code < 0 || code > __UBUS_STATUS_LAST) + code = UBUS_STATUS_UNKNOWN_ERROR; + +- uc_ubus_request_finish(*callctx, code, NULL); ++ uc_ubus_request_finish(*callctx, code); + + ok_return(ucv_boolean_new(true)); + } +@@ -2538,7 +2554,7 @@ static void free_object(void *ud) { + static void free_request(void *ud) { + uc_ubus_request_t *callctx = ud; + +- uc_ubus_request_finish(callctx, UBUS_STATUS_TIMEOUT, NULL); ++ uc_ubus_request_finish(callctx, UBUS_STATUS_TIMEOUT); + uloop_timeout_cancel(&callctx->timeout); + free(callctx); + } diff --git a/package/utils/ucode/patches/104-ubus-fix-potential-use-after-free-on-channel-disconn.patch b/package/utils/ucode/patches/104-ubus-fix-potential-use-after-free-on-channel-disconn.patch new file mode 100644 index 0000000000..7e30d17d1b --- /dev/null +++ b/package/utils/ucode/patches/104-ubus-fix-potential-use-after-free-on-channel-disconn.patch @@ -0,0 +1,28 @@ +From: Felix Fietkau +Date: Tue, 4 Mar 2025 20:55:43 +0100 +Subject: [PATCH] ubus: fix potential use-after-free on channel disconnect + +Ensure that free function only gets called called after shutdown, +because it potentially invalidates the uc_ubus_connection_t +pointer. + +Signed-off-by: Felix Fietkau +--- + +--- a/lib/ubus.c ++++ b/lib/ubus.c +@@ -2358,12 +2358,12 @@ uc_ubus_channel_disconnect_cb(struct ubu + } + + blob_buf_free(&c->buf); +- if (c->registry_index >= 0) +- connection_reg_clear(c->vm, c->registry_index); + if (c->ctx.sock.fd >= 0) { + ubus_shutdown(&c->ctx); + c->ctx.sock.fd = -1; + } ++ if (c->registry_index >= 0) ++ connection_reg_clear(c->vm, c->registry_index); + } + + static uc_value_t * diff --git a/package/utils/ucode/patches/105-ubus-remove-broken-implied-await-when-calling-defer-.patch b/package/utils/ucode/patches/105-ubus-remove-broken-implied-await-when-calling-defer-.patch new file mode 100644 index 0000000000..d81952eec0 --- /dev/null +++ b/package/utils/ucode/patches/105-ubus-remove-broken-implied-await-when-calling-defer-.patch @@ -0,0 +1,105 @@ +From: Felix Fietkau +Date: Fri, 7 Mar 2025 14:33:10 +0100 +Subject: [PATCH] ubus: remove broken implied await when calling defer() + outside of uloop.run() + +Calling ubus.defer() outside of uloop.run() was apparently broken since commit +1cb04f9b76e2 ("ubus: add object publishing, notify and subscribe support") from +March 2022. + +It was supposed to block until the request completes, however it blocked forever +due to a counter imbalance introduced by the above commit. + +These days this 'feature' is of questionable value, since req.await() exists, +and there is a legitimate use for issuing deferred requests from outside of +uloop.run() and then calling uloop.run() or req.await() afterwards. + +Since nobody noticed the breakage in all this time, let's just get rid of this +to get rid of some API footguns. + +Signed-off-by: Felix Fietkau +--- + +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -180,7 +180,6 @@ if(UBUS_SUPPORT) + try_compile(HAVE_NEW_UBUS_STATUS_CODES + ${CMAKE_BINARY_DIR} + "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test.c") +- check_symbol_exists(uloop_fd_set_cb "libubox/uloop.h" FD_SET_CB_EXISTS) + check_function_exists(uloop_timeout_remaining64 REMAINING64_FUNCTION_EXISTS) + check_function_exists(ubus_channel_connect HAVE_CHANNEL_SUPPORT) + if(REMAINING64_FUNCTION_EXISTS) +--- a/lib/ubus.c ++++ b/lib/ubus.c +@@ -132,9 +132,6 @@ static uc_resource_type_t *defer_type; + static uc_resource_type_t *conn_type; + static uc_resource_type_t *chan_type; + +-static uint64_t n_cb_active; +-static bool have_own_uloop; +- + static struct blob_buf buf; + + typedef struct { +@@ -649,11 +646,6 @@ uc_ubus_call_user_cb(uc_ubus_deferred_t + } + + request_reg_clear(defer->vm, defer->registry_index); +- +- n_cb_active--; +- +- if (have_own_uloop && n_cb_active == 0) +- uloop_end(); + } + + static void +@@ -735,24 +727,6 @@ uc_ubus_call_timeout_cb(struct uloop_tim + uc_ubus_call_user_cb(defer, UBUS_STATUS_TIMEOUT, NULL); + } + +-static bool +-uc_ubus_have_uloop(void) +-{ +- bool prev = uloop_cancelled; +- bool active; +- +-#ifdef HAVE_ULOOP_FD_SET_CB +- if (uloop_fd_set_cb) +- return true; +-#endif +- +- uloop_cancelled = true; +- active = uloop_cancelling(); +- uloop_cancelled = prev; +- +- return active; +-} +- + static int + get_fd(uc_vm_t *vm, uc_value_t *val) + { +@@ -958,11 +932,6 @@ uc_ubus_defer_common(uc_vm_t *vm, uc_ubu + + defer->registry_index = request_reg_add(vm, ucv_get(res->res), ucv_get(replycb), ucv_get(datacb), + ucv_get(fdcb), ucv_get(conn), ucv_get(fd)); +- +- if (!uc_ubus_have_uloop()) { +- have_own_uloop = true; +- uloop_run(); +- } + } + else { + uc_vm_stack_push(vm, ucv_get(replycb)); +@@ -2303,11 +2272,6 @@ uc_ubus_defer_abort(uc_vm_t *vm, size_t + + request_reg_clear((*d)->vm, (*d)->registry_index); + +- n_cb_active--; +- +- if (have_own_uloop && n_cb_active == 0) +- uloop_end(); +- + (*d)->complete = true; + + ok_return(ucv_boolean_new(true)); -- 2.30.2