]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
liveupdate: Reference count incoming FLB data
authorDavid Matlack <dmatlack@google.com>
Thu, 23 Apr 2026 17:40:29 +0000 (17:40 +0000)
committerMike Rapoport (Microsoft) <rppt@kernel.org>
Sun, 31 May 2026 23:31:38 +0000 (02:31 +0300)
Increment the incoming FLB refcount in liveupdate_flb_get_incoming() so
that the FLB structure cannot be freed while the caller is actively using
it. Add an additional liveupdate_flb_put_incoming() function so the
caller can explicitly indicate when it is done using the FLB data.

During a Live Update, a subsystem might need to hold onto the incoming
File-Lifecycle-Bound (FLB) data for an extended period, such as during
device enumeration. Incrementing the reference count guarantees that the
data remains valid and accessible until the subsystem releases it,
preventing future use-after-free bugs.

Fixes: cab056f2aae7 ("liveupdate: luo_flb: introduce File-Lifecycle-Bound global state")
Signed-off-by: David Matlack <dmatlack@google.com>
Reviewed-by: Samiullah Khawaja <skhawaja@google.com>
Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Link: https://lore.kernel.org/r/20260423174032.3140399-3-dmatlack@google.com
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
include/linux/liveupdate.h
kernel/liveupdate/luo_flb.c
lib/tests/liveupdate.c

index 8d3bbc35c828b822461cb9246344814ac8830c3c..88722e5caf020be427c70f489cee7eca40544125 100644 (file)
@@ -240,6 +240,8 @@ void liveupdate_unregister_flb(struct liveupdate_file_handler *fh,
                               struct liveupdate_flb *flb);
 
 int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, void **objp);
+void liveupdate_flb_put_incoming(struct liveupdate_flb *flb);
+
 int liveupdate_flb_get_outgoing(struct liveupdate_flb *flb, void **objp);
 
 #else /* CONFIG_LIVEUPDATE */
@@ -280,6 +282,10 @@ static inline int liveupdate_flb_get_incoming(struct liveupdate_flb *flb,
        return -EOPNOTSUPP;
 }
 
+static inline void liveupdate_flb_put_incoming(struct liveupdate_flb *flb)
+{
+}
+
 static inline int liveupdate_flb_get_outgoing(struct liveupdate_flb *flb,
                                              void **objp)
 {
index 59c5f31ab767408cabde721f9470499c4eeb939e..8f5c5dd01cd048b29cd7a8c8f6fdc212db0e1913 100644 (file)
@@ -165,7 +165,7 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb)
        bool found = false;
        int err;
 
-       guard(mutex)(&private->incoming.lock);
+       lockdep_assert_held(&private->incoming.lock);
 
        if (private->incoming.finished)
                return -ENODATA;
@@ -206,12 +206,14 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb)
        return 0;
 }
 
-static void luo_flb_file_finish_one(struct liveupdate_flb *flb)
+void liveupdate_flb_put_incoming(struct liveupdate_flb *flb)
 {
        struct luo_flb_private *private = luo_flb_get_private(flb);
+       struct liveupdate_flb_op_args args = {0};
 
-       if (refcount_dec_and_test(&private->incoming.count)) {
-               struct liveupdate_flb_op_args args = {0};
+       scoped_guard(mutex, &private->incoming.lock) {
+               if (!refcount_dec_and_test(&private->incoming.count))
+                       return;
 
                if (!private->incoming.retrieved) {
                        int err = luo_flb_retrieve_one(flb);
@@ -220,16 +222,14 @@ static void luo_flb_file_finish_one(struct liveupdate_flb *flb)
                                return;
                }
 
-               scoped_guard(mutex, &private->incoming.lock) {
-                       args.flb = flb;
-                       args.obj = private->incoming.obj;
-                       flb->ops->finish(&args);
+               args.flb = flb;
+               args.obj = private->incoming.obj;
+               flb->ops->finish(&args);
 
-                       private->incoming.data = 0;
-                       private->incoming.obj = NULL;
-                       private->incoming.finished = true;
-                       module_put(flb->ops->owner);
-               }
+               private->incoming.data = 0;
+               private->incoming.obj = NULL;
+               private->incoming.finished = true;
+               module_put(flb->ops->owner);
        }
 }
 
@@ -312,7 +312,7 @@ void luo_flb_file_finish(struct liveupdate_file_handler *fh)
 
        guard(rwsem_read)(&luo_register_rwlock);
        list_for_each_entry_reverse(iter, flb_list, list)
-               luo_flb_file_finish_one(iter->flb);
+               liveupdate_flb_put_incoming(iter->flb);
 }
 
 static void luo_flb_unregister_one(struct liveupdate_file_handler *fh,
@@ -509,6 +509,8 @@ int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, void **objp)
        if (!liveupdate_enabled())
                return -EOPNOTSUPP;
 
+       guard(mutex)(&private->incoming.lock);
+
        if (!private->incoming.obj) {
                int err = luo_flb_retrieve_one(flb);
 
@@ -516,7 +518,7 @@ int liveupdate_flb_get_incoming(struct liveupdate_flb *flb, void **objp)
                        return err;
        }
 
-       guard(mutex)(&private->incoming.lock);
+       refcount_inc(&private->incoming.count);
        *objp = private->incoming.obj;
 
        return 0;
index e4b0ecbee32fe63b0bb1e7df7da3ff23b64219b5..4c08a7c6fb788a0b68cdcaa5da7a78b2309bc8a8 100644 (file)
@@ -105,6 +105,9 @@ static void liveupdate_test_init(void)
                        pr_err("liveupdate_flb_get_incoming for %s failed: %pe\n",
                               flb->compatible, ERR_PTR(err));
                }
+
+               if (!err)
+                       liveupdate_flb_put_incoming(flb);
        }
        initialized = true;
 }