summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBreeze2026-04-16 15:56:19 +0000
committerBreeze2026-04-16 16:16:01 +0000
commitd005c885dbe439745abe7f64710cb93f5da569ce (patch)
tree227943bf9731e9d1dab4420677cf05ce181eb461
parent5b078674a5929a5c90043c66721d45e1d8eae73d (diff)
downloadrpcd-d005c885dbe439745abe7f64710cb93f5da569ce.tar.gz
session: clamp uloop timeout to avoid int overflow
rpc_touch_session() computes `ses->timeout * 1000` as int*int, which overflows INT_MAX once ses->timeout exceeds 2147483 seconds (~24.85 days). The wrapped-around negative value passed to uloop_timeout_set() causes libubox to fire the session timeout callback on the very next uloop iteration, destroying the just-created session before the caller can use it. In practice this makes `ubus call session login` with e.g. `timeout:2592000` (30 days) return a valid-looking session id whose reported `expires` is a large negative number, and any subsequent session.set / session.get call on that SID returns "Not found". LuCI's ucode dispatcher passes the value of `luci.sauth.sessiontime` straight through as the login timeout, so users who follow common advice to bump sessiontime to 30 days get silently locked out of LuCI (uhttpd logs `accepted login`, response is 403 with no Set-Cookie) while SSH keeps working with the same credentials. At least one affected user resorted to factory-resetting multiple APs before recovering, see https://forum.openwrt.org/t/241892 . The same overflow lurks in rpc_session_from_blob() when thawing a persisted session, where `blobmsg_get_u64(EXPIRES) * 1000` is passed to uloop_timeout_set()'s int `msecs` parameter. Introduce a small helper that converts a seconds value to milliseconds with clamping to INT_MAX (and negative-input guard), and use it at both call sites. The cap still allows uloop timeouts of ~24.85 days, which is longer than any realistic administrative session. Signed-off-by: Breeze <chanlikessummer@gmail.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
-rw-r--r--session.c28
1 files changed, 26 insertions, 2 deletions
diff --git a/session.c b/session.c
index 195d07d..6ac226a 100644
--- a/session.c
+++ b/session.c
@@ -248,11 +248,34 @@ rpc_session_dump(struct rpc_session *ses, struct ubus_context *ctx,
ubus_send_reply(ctx, req, buf.head);
}
+/*
+ * Convert a session timeout in seconds to a millisecond value suitable
+ * for uloop_timeout_set(), clamping to INT_MAX to avoid overflowing the
+ * int argument. Without this, any timeout exceeding ~2147483 seconds
+ * (~24.85 days) would wrap around to a negative value and cause libubox
+ * to fire the timeout callback on the next uloop iteration, destroying
+ * the session immediately after creation.
+ */
+static int
+rpc_session_timeout_ms(int64_t seconds)
+{
+ int64_t msecs;
+
+ if (seconds < 0)
+ seconds = 0;
+
+ msecs = seconds * 1000;
+ if (msecs > INT_MAX)
+ msecs = INT_MAX;
+
+ return (int)msecs;
+}
+
static void
rpc_touch_session(struct rpc_session *ses)
{
if (ses->timeout > 0)
- uloop_timeout_set(&ses->t, ses->timeout * 1000);
+ uloop_timeout_set(&ses->t, rpc_session_timeout_ms(ses->timeout));
}
static void
@@ -1315,7 +1338,8 @@ rpc_session_from_blob(struct uci_context *uci, struct blob_attr *attr)
avl_insert(&sessions, &ses->avl);
- uloop_timeout_set(&ses->t, blobmsg_get_u64(tb[RPC_DUMP_EXPIRES]) * 1000);
+ uloop_timeout_set(&ses->t,
+ rpc_session_timeout_ms(blobmsg_get_u64(tb[RPC_DUMP_EXPIRES])));
return true;
}