lkml.org 
[lkml]   [2026]   [May]   [6]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH v1 3/3] liveupdate: pin sessions and handle inactive ones during serialization
Date
During the reboot() syscall, user processes are still running
concurrently. Even though the system is actively serializing LUO
sessions and will not return to userspace unless liveupdate_reboot()
fails, it is still possible for a user process to close a preserved LUO
session. This creates a race condition where a session could be
destroyed while it is being serialized.

To fix this, we must ensure that we only preserve sessions that are not
closed at the time of serialization. Take a reference to the session's
struct file for all outgoing sessions to pin them during this process.

Handle inactive sessions (where get_file_active() fails) by cleaning
them up and removing them from the outgoing list during the pinning
phase. This ensures serialization can safely proceed with the remaining
active sessions.

Fixes: 0153094d03df ("liveupdate: luo_session: add sessions support")
Reported-by: Oskar Gerlicz Kowalczuk <oskar@gerlicz.space>
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
kernel/liveupdate/luo_internal.h | 1 +
kernel/liveupdate/luo_session.c | 77 +++++++++++++++++++++++++++++---
2 files changed, 73 insertions(+), 5 deletions(-)

diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
index 875844d7a41d..1124bab6e0de 100644
--- a/kernel/liveupdate/luo_internal.h
+++ b/kernel/liveupdate/luo_internal.h
@@ -73,6 +73,7 @@ struct luo_session {
struct luo_session_ser *ser;
struct list_head list;
bool retrieved;
+ struct file *file;
struct luo_file_set file_set;
struct mutex mutex;
};
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index 996adc995514..b344b64bbced 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -177,12 +177,27 @@ static int luo_session_insert(struct luo_session_header *sh,
return 0;
}

+static void __luo_session_remove(struct luo_session_header *sh,
+ struct luo_session *session)
+{
+ lockdep_assert_held_write(&sh->rwsem);
+ if (!list_empty(&session->list)) {
+ list_del_init(&session->list);
+ sh->count--;
+ }
+}
+
+static void luo_session_unpreserve_files(struct luo_session *session)
+{
+ scoped_guard(mutex, &session->mutex)
+ luo_file_unpreserve_files(&session->file_set);
+}
+
static void luo_session_remove(struct luo_session_header *sh,
struct luo_session *session)
{
- guard(rwsem_write)(&sh->rwsem);
- list_del(&session->list);
- sh->count--;
+ scoped_guard(rwsem_write, &sh->rwsem)
+ __luo_session_remove(sh, session);
}

static int luo_session_finish_one(struct luo_session *session)
@@ -217,12 +232,13 @@ static int luo_session_release(struct inode *inodep, struct file *filep)
if (err) {
pr_warn("Unable to finish session [%s] on release\n",
session->name);
+ scoped_guard(mutex, &session->mutex)
+ session->file = NULL;
return err;
}
sh = &luo_session_global.incoming;
} else {
- scoped_guard(mutex, &session->mutex)
- luo_file_unpreserve_files(&session->file_set);
+ luo_session_unpreserve_files(session);
sh = &luo_session_global.outgoing;
}

@@ -380,16 +396,65 @@ static int luo_session_getfile(struct luo_session *session, struct file **filep)
struct file *file;

lockdep_assert_held(&session->mutex);
+
+ /* serialization may evict partially initialized session */
+ if (list_empty(&session->list))
+ return -EBUSY;
+
snprintf(name_buf, sizeof(name_buf), "[luo_session] %s", session->name);
file = anon_inode_getfile(name_buf, &luo_session_fops, session, O_RDWR);
if (IS_ERR(file))
return PTR_ERR(file);

+ session->file = file;
*filep = file;

return 0;
}

+/**
+ * luo_session_get_all_outgoing - Pins all active outgoing sessions.
+ *
+ * This function iterates through all sessions in the outgoing list and
+ * attempts to pin their associated file descriptors. If a session's file
+ * is no longer active (refcount 0), the session is cleaned up and removed.
+ *
+ * Note: Successfully pinned files have their reference count increased.
+ * These references are intentionally leaked in the current kernel upon
+ * successful live update, as the system will transition to a new kernel
+ * image which will reclaim all memory. In case of failure, they are
+ * released in luo_session_put_all_outgoing().
+ */
+static void luo_session_get_all_outgoing(void)
+{
+ struct luo_session_header *sh = &luo_session_global.outgoing;
+ struct luo_session *session, *tmp;
+
+ lockdep_assert_held_write(&sh->rwsem);
+ list_for_each_entry_safe(session, tmp, &sh->list, list) {
+ guard(mutex)(&session->mutex);
+ if (!session->file) {
+ __luo_session_remove(sh, session);
+ } else if (!get_file_active(&session->file)) {
+ luo_session_unpreserve_files(session);
+ __luo_session_remove(sh, session);
+ }
+ }
+}
+
+static void luo_session_put_all_outgoing(void)
+{
+ struct luo_session_header *sh = &luo_session_global.outgoing;
+ struct luo_session *session;
+
+ lockdep_assert_held_write(&sh->rwsem);
+ list_for_each_entry(session, &sh->list, list) {
+ guard(mutex)(&session->mutex);
+ if (session->file)
+ fput(session->file);
+ }
+}
+
int luo_session_create(const char *name, struct file **filep)
{
struct luo_session *session;
@@ -596,6 +661,7 @@ int luo_session_serialize(void)
guard(rwsem_write)(&sh->rwsem);

sh->rebooting = true;
+ luo_session_get_all_outgoing();
list_for_each_entry(session, &sh->list, list) {
err = luo_session_freeze_one(session, &sh->ser[i]);
if (err)
@@ -616,6 +682,7 @@ int luo_session_serialize(void)
luo_session_unfreeze_one(session, &sh->ser[i]);
memset(sh->ser[i].name, 0, sizeof(sh->ser[i].name));
}
+ luo_session_put_all_outgoing();

return err;
}
--
2.54.0.545.g6539524ca2-goog

\
 
 \ /
  Last update: 2026-05-06 06:33    [W:0.091 / U:14.362 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and my Meterkast|Read the blog