Messages in this thread Patch in this message |  | | From | Pasha Tatashin <> | | Subject | [PATCH v1 3/3] liveupdate: pin sessions and handle inactive ones during serialization | | Date | Wed, 6 May 2026 00:32:04 -0400 |
| |
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
|  |