summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMeng2026-03-16 08:58:36 +0000
committerMeng2026-03-16 09:34:41 +0000
commite655a0d69492a91d9dada74f0832e028e6b5367b (patch)
treea9ee5e8e7ee46167ea083bb7c6104096631abdbc
parent69b62b1990bc0257bd01342ea1710375fa0cce6d (diff)
downloadrpcd-e655a0d69492a91d9dada74f0832e028e6b5367b.tar.gz
exec: defer async reply teardown to avoid use-after-free
Async exec replies currently tear down their context directly from ustream callbacks. When the callback chain keeps running after the reply path frees the context, rpcd can hit a use-after-free and crash with SIGSEGV. Defer reply completion through a 0 ms uloop timeout and guard against duplicate scheduling so cleanup runs after the callbacks unwind in both exec paths. Signed-off-by: Meng <x.meng@genexis.eu>
-rw-r--r--exec.c36
-rw-r--r--file.c39
-rw-r--r--include/rpcd/exec.h3
3 files changed, 68 insertions, 10 deletions
diff --git a/exec.c b/exec.c
index 3cd7384..31dfc68 100644
--- a/exec.c
+++ b/exec.c
@@ -122,6 +122,9 @@ rpc_exec_reply(struct rpc_exec_context *c, int rv)
{
uloop_timeout_cancel(&c->timeout);
uloop_process_delete(&c->process);
+ uloop_timeout_cancel(&c->deferred_reply);
+
+ c->deferred_reply_pending = false;
if (rv == UBUS_STATUS_OK)
{
@@ -156,13 +159,34 @@ rpc_exec_reply(struct rpc_exec_context *c, int rv)
}
static void
+rpc_exec_deferred_reply_cb(struct uloop_timeout *t)
+{
+ struct rpc_exec_context *c =
+ container_of(t, struct rpc_exec_context, deferred_reply);
+
+ c->deferred_reply_pending = false;
+ rpc_exec_reply(c, c->deferred_status);
+}
+
+static void
+rpc_exec_schedule_reply(struct rpc_exec_context *c, int rv)
+{
+ if (c->deferred_reply_pending)
+ return;
+
+ c->deferred_status = rv;
+ c->deferred_reply_pending = true;
+ uloop_timeout_set(&c->deferred_reply, 0);
+}
+
+static void
rpc_exec_timeout_cb(struct uloop_timeout *t)
{
struct rpc_exec_context *c =
container_of(t, struct rpc_exec_context, timeout);
kill(c->process.pid, SIGKILL);
- rpc_exec_reply(c, UBUS_STATUS_TIMEOUT);
+ rpc_exec_schedule_reply(c, UBUS_STATUS_TIMEOUT);
}
static void
@@ -222,7 +246,7 @@ rpc_exec_opipe_read_cb(struct ustream *s, int bytes)
}
else if (ustream_read_buf_full(s))
{
- rpc_exec_reply(c, UBUS_STATUS_NOT_SUPPORTED);
+ rpc_exec_schedule_reply(c, UBUS_STATUS_NOT_SUPPORTED);
}
}
@@ -252,7 +276,7 @@ rpc_exec_epipe_read_cb(struct ustream *s, int bytes)
}
else if (ustream_read_buf_full(s))
{
- rpc_exec_reply(c, UBUS_STATUS_NOT_SUPPORTED);
+ rpc_exec_schedule_reply(c, UBUS_STATUS_NOT_SUPPORTED);
}
}
@@ -263,7 +287,7 @@ rpc_exec_opipe_state_cb(struct ustream *s)
container_of(s, struct rpc_exec_context, opipe.stream);
if (c->opipe.stream.eof && c->epipe.stream.eof)
- rpc_exec_reply(c, UBUS_STATUS_OK);
+ rpc_exec_schedule_reply(c, UBUS_STATUS_OK);
}
static void
@@ -273,7 +297,7 @@ rpc_exec_epipe_state_cb(struct ustream *s)
container_of(s, struct rpc_exec_context, epipe.stream);
if (c->opipe.stream.eof && c->epipe.stream.eof)
- rpc_exec_reply(c, UBUS_STATUS_OK);
+ rpc_exec_schedule_reply(c, UBUS_STATUS_OK);
}
int
@@ -349,6 +373,8 @@ rpc_exec(const char **args, rpc_exec_write_cb_t in,
c->process.cb = rpc_exec_process_cb;
uloop_process_add(&c->process);
+ c->deferred_reply.cb = rpc_exec_deferred_reply_cb;
+
c->timeout.cb = rpc_exec_timeout_cb;
uloop_timeout_set(&c->timeout, rpc_exec_timeout);
diff --git a/file.c b/file.c
index 6912490..b3bf31f 100644
--- a/file.c
+++ b/file.c
@@ -70,7 +70,10 @@ struct rpc_file_exec_context {
struct uloop_process process;
struct ustream_fd opipe;
struct ustream_fd epipe;
+ struct uloop_timeout deferred_reply;
int stat;
+ int deferred_status;
+ bool deferred_reply_pending;
};
@@ -788,6 +791,9 @@ rpc_file_exec_reply(struct rpc_file_exec_context *c, int rv)
{
uloop_timeout_cancel(&c->timeout);
uloop_process_delete(&c->process);
+ uloop_timeout_cancel(&c->deferred_reply);
+
+ c->deferred_reply_pending = false;
if (rv == UBUS_STATUS_OK)
{
@@ -814,13 +820,34 @@ rpc_file_exec_reply(struct rpc_file_exec_context *c, int rv)
}
static void
+rpc_file_exec_deferred_reply_cb(struct uloop_timeout *t)
+{
+ struct rpc_file_exec_context *c =
+ container_of(t, struct rpc_file_exec_context, deferred_reply);
+
+ c->deferred_reply_pending = false;
+ rpc_file_exec_reply(c, c->deferred_status);
+}
+
+static void
+rpc_file_exec_schedule_reply(struct rpc_file_exec_context *c, int rv)
+{
+ if (c->deferred_reply_pending)
+ return;
+
+ c->deferred_status = rv;
+ c->deferred_reply_pending = true;
+ uloop_timeout_set(&c->deferred_reply, 0);
+}
+
+static void
rpc_file_exec_timeout_cb(struct uloop_timeout *t)
{
struct rpc_file_exec_context *c =
container_of(t, struct rpc_file_exec_context, timeout);
kill(c->process.pid, SIGKILL);
- rpc_file_exec_reply(c, UBUS_STATUS_TIMEOUT);
+ rpc_file_exec_schedule_reply(c, UBUS_STATUS_TIMEOUT);
}
static void
@@ -842,7 +869,7 @@ rpc_file_exec_opipe_read_cb(struct ustream *s, int bytes)
container_of(s, struct rpc_file_exec_context, opipe.stream);
if (ustream_read_buf_full(s))
- rpc_file_exec_reply(c, UBUS_STATUS_NOT_SUPPORTED);
+ rpc_file_exec_schedule_reply(c, UBUS_STATUS_NOT_SUPPORTED);
}
static void
@@ -852,7 +879,7 @@ rpc_file_exec_epipe_read_cb(struct ustream *s, int bytes)
container_of(s, struct rpc_file_exec_context, epipe.stream);
if (ustream_read_buf_full(s))
- rpc_file_exec_reply(c, UBUS_STATUS_NOT_SUPPORTED);
+ rpc_file_exec_schedule_reply(c, UBUS_STATUS_NOT_SUPPORTED);
}
static void
@@ -862,7 +889,7 @@ rpc_file_exec_opipe_state_cb(struct ustream *s)
container_of(s, struct rpc_file_exec_context, opipe.stream);
if (c->opipe.stream.eof && c->epipe.stream.eof)
- rpc_file_exec_reply(c, UBUS_STATUS_OK);
+ rpc_file_exec_schedule_reply(c, UBUS_STATUS_OK);
}
static void
@@ -872,7 +899,7 @@ rpc_file_exec_epipe_state_cb(struct ustream *s)
container_of(s, struct rpc_file_exec_context, epipe.stream);
if (c->opipe.stream.eof && c->epipe.stream.eof)
- rpc_file_exec_reply(c, UBUS_STATUS_OK);
+ rpc_file_exec_schedule_reply(c, UBUS_STATUS_OK);
}
static void
@@ -1034,6 +1061,8 @@ rpc_file_exec_run(const char *cmd, const struct blob_attr *sid,
c->process.cb = rpc_file_exec_process_cb;
uloop_process_add(&c->process);
+ c->deferred_reply.cb = rpc_file_exec_deferred_reply_cb;
+
c->timeout.cb = rpc_file_exec_timeout_cb;
uloop_timeout_set(&c->timeout, *ops->exec_timeout);
diff --git a/include/rpcd/exec.h b/include/rpcd/exec.h
index 928f5ed..6c28864 100644
--- a/include/rpcd/exec.h
+++ b/include/rpcd/exec.h
@@ -61,6 +61,7 @@ struct rpc_exec_context {
struct ubus_request_data request;
struct uloop_timeout timeout;
struct uloop_process process;
+ struct uloop_timeout deferred_reply;
struct ustream_fd ipipe;
struct ustream_fd opipe;
struct ustream_fd epipe;
@@ -69,6 +70,8 @@ struct rpc_exec_context {
int errlen;
char *err;
int stat;
+ int deferred_status;
+ bool deferred_reply_pending;
void *priv;
bool blob_array;
void *blob_cookie;