From: Ravi Jonnalagadda <ravis.opensrc@gmail.com>
To: sj@kernel.org, damon@lists.linux.dev, linux-mm@kvack.org,
linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org
Cc: akpm@linux-foundation.org, corbet@lwn.net, bijan311@gmail.com,
ajayjoshi@micron.com, honggyu.kim@sk.com, yunjeong.mun@sk.com,
ravis.opensrc@gmail.com, bharata@amd.com
Subject: [RFC PATCH 1/7] mm/damon/core: refcount ops owner module to prevent rmmod UAF
Date: Sat, 16 May 2026 15:34:26 -0700 [thread overview]
Message-ID: <20260516223439.4033-2-ravis.opensrc@gmail.com> (raw)
In-Reply-To: <20260516223439.4033-1-ravis.opensrc@gmail.com>
damon_select_ops() copies the registered damon_operations struct into
ctx->ops by value. After damon_unregister_ops() is called from a
backend module's exit path, the registry slot is cleared but any
surviving ctx still holds function pointers that resolve into the
unloaded module's text. Restarting kdamond on such a ctx, or invoking
any ops callback, jumps into freed code.
Add a struct module *owner field to damon_operations. In
damon_select_ops(), take a reference to ops->owner via try_module_get()
after locating the registry entry; on failure return -EBUSY without
binding the ctx. If the ctx already had an ops bound (re-select
case), drop the previous owner's reference before installing the new
one to keep the refcount balanced. In damon_destroy_ctx(), release
the reference via module_put(ctx->ops.owner).
In damon_commit_ctx(), the live ops field is overwritten by a value
copy from src. Balance the refcount when the owner changes: take a
ref on the new owner (return -EBUSY on failure) and put the ref on the
old owner before the assignment.
Built-in ops sets (vaddr, paddr) leave owner = NULL; try_module_get(NULL)
returns true and module_put(NULL) is a no-op. Loadable backends set
owner = THIS_MODULE in their registration.
Also add damon_unregister_ops() so loadable backends have a clean exit
path.
Signed-off-by: Ravi Jonnalagadda <ravis.opensrc@gmail.com>
---
include/linux/damon.h | 4 ++++
mm/damon/core.c | 46 ++++++++++++++++++++++++++++++++++---
mm/damon/tests/core-kunit.h | 2 +-
3 files changed, 48 insertions(+), 4 deletions(-)
diff --git a/include/linux/damon.h b/include/linux/damon.h
index df7910a39b407..8e6e1cd89e551 100644
--- a/include/linux/damon.h
+++ b/include/linux/damon.h
@@ -682,6 +682,8 @@ enum damon_ops_id {
* struct damon_operations - Monitoring operations for given use cases.
*
* @id: Identifier of this operations set.
+ * @owner: Module that provides this operations set, or NULL
+ * for built-in ops.
* @init: Initialize operations-related data structures.
* @update: Update operations-related data structures.
* @prepare_access_checks: Prepare next access check of target regions.
@@ -728,6 +730,7 @@ enum damon_ops_id {
*/
struct damon_operations {
enum damon_ops_id id;
+ struct module *owner;
void (*init)(struct damon_ctx *context);
void (*update)(struct damon_ctx *context);
void (*prepare_access_checks)(struct damon_ctx *context);
@@ -1206,6 +1209,7 @@ int damon_commit_ctx(struct damon_ctx *old_ctx, struct damon_ctx *new_ctx);
int damon_nr_running_ctxs(void);
bool damon_is_registered_ops(enum damon_ops_id id);
int damon_register_ops(struct damon_operations *ops);
+int damon_unregister_ops(enum damon_ops_id id);
int damon_select_ops(struct damon_ctx *ctx, enum damon_ops_id id);
static inline bool damon_target_has_pid(const struct damon_ctx *ctx)
diff --git a/mm/damon/core.c b/mm/damon/core.c
index e4b9adc0a64dd..b605d36b29b1a 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -12,6 +12,7 @@
#include <linux/kthread.h>
#include <linux/memcontrol.h>
#include <linux/mm.h>
+#include <linux/module.h>
#include <linux/psi.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -93,6 +94,31 @@ int damon_register_ops(struct damon_operations *ops)
mutex_unlock(&damon_ops_lock);
return err;
}
+EXPORT_SYMBOL_GPL(damon_register_ops);
+
+/**
+ * damon_unregister_ops() - Unregister a monitoring operations set.
+ * @id: ID of the operations set to unregister.
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+int damon_unregister_ops(enum damon_ops_id id)
+{
+ if (id >= NR_DAMON_OPS)
+ return -EINVAL;
+
+ /*
+ * Callers (typically the owning module exit path) hold a
+ * module ref via try_module_get() in damon_select_ops(); the
+ * unregister cannot race with active ctxs because module_exit
+ * runs only at owner refcount 0.
+ */
+ mutex_lock(&damon_ops_lock);
+ memset(&damon_registered_ops[id], 0, sizeof(damon_registered_ops[id]));
+ mutex_unlock(&damon_ops_lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(damon_unregister_ops);
/**
* damon_select_ops() - Select a monitoring operations to use with the context.
@@ -112,10 +138,18 @@ int damon_select_ops(struct damon_ctx *ctx, enum damon_ops_id id)
return -EINVAL;
mutex_lock(&damon_ops_lock);
- if (!__damon_is_registered_ops(id))
+ if (!__damon_is_registered_ops(id)) {
err = -EINVAL;
- else
- ctx->ops = damon_registered_ops[id];
+ goto out;
+ }
+ if (!try_module_get(damon_registered_ops[id].owner)) {
+ err = -EBUSY;
+ goto out;
+ }
+ /* Drop previous owner ref if this ctx had ops selected before. */
+ module_put(ctx->ops.owner);
+ ctx->ops = damon_registered_ops[id];
+out:
mutex_unlock(&damon_ops_lock);
return err;
}
@@ -835,6 +869,7 @@ void damon_destroy_ctx(struct damon_ctx *ctx)
damon_for_each_sample_filter_safe(f, next_f, &ctx->sample_control)
damon_destroy_sample_filter(f, &ctx->sample_control);
+ module_put(ctx->ops.owner);
kfree(ctx);
}
@@ -1749,6 +1784,11 @@ int damon_commit_ctx(struct damon_ctx *dst, struct damon_ctx *src)
return err;
}
dst->pause = src->pause;
+ if (src->ops.owner != dst->ops.owner) {
+ if (!try_module_get(src->ops.owner))
+ return -EBUSY;
+ module_put(dst->ops.owner);
+ }
dst->ops = src->ops;
err = damon_commit_probes(dst, src);
if (err)
diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h
index 0369c717b93db..300659b115602 100644
--- a/mm/damon/tests/core-kunit.h
+++ b/mm/damon/tests/core-kunit.h
@@ -342,7 +342,7 @@ static void damon_test_split_regions_of(struct kunit *test)
static void damon_test_ops_registration(struct kunit *test)
{
struct damon_ctx *c = damon_new_ctx();
- struct damon_operations ops = {.id = DAMON_OPS_VADDR}, bak;
+ struct damon_operations ops = {.id = DAMON_OPS_VADDR}, bak = {};
bool need_cleanup = false;
if (!c)
--
2.43.0
next prev parent reply other threads:[~2026-05-16 22:34 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-16 22:34 [RFC PATCH 0/7] mm/damon: hardware-sampled access reports + AMD IBS Op example Ravi Jonnalagadda
2026-05-16 22:34 ` Ravi Jonnalagadda [this message]
2026-05-16 22:34 ` [RFC PATCH 2/7] mm/damon/paddr: export damon_pa_* ops for IBS module Ravi Jonnalagadda
2026-05-16 22:34 ` [RFC PATCH 3/7] mm/damon/core: replace mutex-protected report buffer with per-CPU lockless ring Ravi Jonnalagadda
2026-05-16 22:34 ` [RFC PATCH 4/7] mm/damon/core: flat-array snapshot + bsearch in ring-drain loop Ravi Jonnalagadda
2026-05-16 22:34 ` [RFC PATCH 5/7] mm/damon: add sysfs binding and dispatch hookup for paddr_ibs operations Ravi Jonnalagadda
2026-05-16 22:34 ` [RFC PATCH 6/7] mm/damon/core: accept paddr_ibs in node_eligible_mem_bp ops check Ravi Jonnalagadda
2026-05-16 22:34 ` [RFC PATCH 7/7] mm/damon/damon_ibs: add AMD IBS-based access sampling backend Ravi Jonnalagadda
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260516223439.4033-2-ravis.opensrc@gmail.com \
--to=ravis.opensrc@gmail.com \
--cc=ajayjoshi@micron.com \
--cc=akpm@linux-foundation.org \
--cc=bharata@amd.com \
--cc=bijan311@gmail.com \
--cc=corbet@lwn.net \
--cc=damon@lists.linux.dev \
--cc=honggyu.kim@sk.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=sj@kernel.org \
--cc=yunjeong.mun@sk.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.