[PATCH v2 5/9] dlm: Free dlm_ls in kobject release callback

From: Ian Rogers

Date: Tue Oct 27 2026 - 18:48:01 EST


struct dlm_ls embeds a struct kobject (ls_kobj), but dlm_ktype lacked a
.release callback and dlm freed the lockspace structure directly via
kfree(ls). With CONFIG_DEBUG_KOBJECT_RELEASE=y, freeing ls before the
delayed kobject release runs causes a use-after-free, and unloading the
module while the delayed release is pending results in an instruction
page fault.

Add a completion ls_kobj_done and dlm_ls_release() to complete it when
the last kobject reference is dropped, waiting for it in free_lockspace()
before kfree(ls) and in the new_lockspace() error path before module_put().
Also delete the kobject via kobject_del() before stopping dlm_recoverd
to close the window where concurrent sysfs writes could wake the stopped
recovery task.

Fixes: 3514faca19a6 ("kobject: remove struct kobj_type from struct kset")
Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
fs/dlm/dlm_internal.h | 1 +
fs/dlm/lockspace.c | 26 ++++++++++++++++++++------
2 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/fs/dlm/dlm_internal.h b/fs/dlm/dlm_internal.h
index 74c2e77d1b55..95b3bff0cb57 100644
--- a/fs/dlm/dlm_internal.h
+++ b/fs/dlm/dlm_internal.h
@@ -576,6 +576,7 @@ struct dlm_ls {
int ls_create_count; /* create/release refcount */
unsigned long ls_flags; /* LSFL_ */
struct kobject ls_kobj;
+ struct completion ls_kobj_done;

struct xarray ls_lkbxa;
rwlock_t ls_lkbxa_lock;
diff --git a/fs/dlm/lockspace.c b/fs/dlm/lockspace.c
index a9c98b4f378f..2dccd080a10f 100644
--- a/fs/dlm/lockspace.c
+++ b/fs/dlm/lockspace.c
@@ -179,7 +179,15 @@ static const struct sysfs_ops dlm_attr_ops = {
.store = dlm_attr_store,
};

+static void dlm_ls_release(struct kobject *kobj)
+{
+ struct dlm_ls *ls = container_of(kobj, struct dlm_ls, ls_kobj);
+
+ complete(&ls->ls_kobj_done);
+}
+
static struct kobj_type dlm_ktype = {
+ .release = dlm_ls_release,
.default_groups = dlm_groups,
.sysfs_ops = &dlm_attr_ops,
};
@@ -355,6 +363,8 @@ static void free_lockspace(struct work_struct *work)
*/
rhashtable_free_and_destroy(&ls->ls_rsbtbl, rhash_free_rsb, NULL);

+ kobject_put(&ls->ls_kobj);
+ wait_for_completion(&ls->ls_kobj_done);
kfree(ls);
}

@@ -435,7 +445,10 @@ static int new_lockspace(const char *name, const char *cluster,
ls->ls_lvblen = lvblen;
atomic_set(&ls->ls_count, 0);
init_waitqueue_head(&ls->ls_count_wait);
+ init_completion(&ls->ls_kobj_done);
ls->ls_flags = 0;
+ ls->ls_kobj.kset = dlm_kset;
+ kobject_init(&ls->ls_kobj, &dlm_ktype);

if (ops && dlm_config.ci_recover_callbacks) {
ls->ls_ops = ops;
@@ -567,9 +580,7 @@ static int new_lockspace(const char *name, const char *cluster,
wait_event(ls->ls_recover_lock_wait,
test_bit(LSFL_RECOVER_LOCK, &ls->ls_flags));

- ls->ls_kobj.kset = dlm_kset;
- error = kobject_init_and_add(&ls->ls_kobj, &dlm_ktype, NULL,
- "%s", ls->ls_name);
+ error = kobject_add(&ls->ls_kobj, NULL, "%s", ls->ls_name);
if (error)
goto out_recoverd;
kobject_uevent(&ls->ls_kobj, KOBJ_ADD);
@@ -582,7 +593,7 @@ static int new_lockspace(const char *name, const char *cluster,

error = do_uevent(ls, 1, 0);
if (error < 0)
- goto out_recoverd;
+ goto out_kobj;

/* wait until recovery is successful or failed */
wait_for_completion(&ls->ls_recovery_done);
@@ -600,6 +611,8 @@ static int new_lockspace(const char *name, const char *cluster,
do_uevent(ls, 0, 0);
dlm_clear_members(ls);
kfree(ls->ls_node_array);
+ out_kobj:
+ kobject_del(&ls->ls_kobj);
out_recoverd:
dlm_recoverd_stop(ls);
out_callback:
@@ -615,6 +628,7 @@ static int new_lockspace(const char *name, const char *cluster,
rhashtable_destroy(&ls->ls_rsbtbl);
out_lsfree:
kobject_put(&ls->ls_kobj);
+ wait_for_completion(&ls->ls_kobj_done);
kfree(ls);
out:
module_put(THIS_MODULE);
@@ -740,6 +754,8 @@ static int release_lockspace(struct dlm_ls *ls, unsigned int release_option)
dlm_user_daemon_available())
do_uevent(ls, 0, (release_option == DLM_RELEASE_RECOVER));

+ kobject_del(&ls->ls_kobj);
+
dlm_recoverd_stop(ls);

/* clear the LSFL_RUNNING flag to fast up
@@ -759,8 +775,6 @@ static int release_lockspace(struct dlm_ls *ls, unsigned int release_option)

dlm_delete_debug_file(ls);

- kobject_put(&ls->ls_kobj);
-
xa_destroy(&ls->ls_recover_xa);
kfree(ls->ls_recover_buf);

--
2.56.0.313.g2479322e68-goog