CVE-2026-46183
Description
In the Linux kernel, the following vulnerability has been resolved:
mm/damon/sysfs-schemes: protect path kfree() with damon_sysfs_lock
damon_sysfs_quot_goal->path can be read and written by users, via DAMON sysfs 'path' file. It can also be indirectly read, for the parameters {on,off}line committing to DAMON. The reads for parameters committing are protected by damon_sysfs_lock to avoid the sysfs files being destroyed while any of the parameters are being read. But the user-driven direct reads and writes are not protected by any lock, while the write is deallocating the path-pointing buffer. As a result, the readers could read the already freed buffer (user-after-free). Note that the user-reads don't race when the same open file is used by the writer, due to kernfs's open file locking. Nonetheless, doing the reads and writes with separate open files would be common. Fix it by protecting both the user-direct reads and writes with damon_sysfs_lock.
Affected products
2Patches
4cf3b71421ca0mm/damon/sysfs-schemes: protect path kfree() with damon_sysfs_lock
2 files changed · +22 −4
mm/damon/sysfs-schemes.c+11 −2 modifieddiff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 8d32a20531d496..245d63808411a2 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1197,8 +1197,13 @@ static ssize_t path_show(struct kobject *kobj, { struct damos_sysfs_quota_goal *goal = container_of(kobj, struct damos_sysfs_quota_goal, kobj); + int len; - return sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t path_store(struct kobject *kobj, @@ -1213,8 +1218,13 @@ static ssize_t path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(goal->path); goal->path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
mm/damon/sysfs-schemes.c+11 −2 modifieddiff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 8d32a20531d496..245d63808411a2 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1197,8 +1197,13 @@ static ssize_t path_show(struct kobject *kobj, { struct damos_sysfs_quota_goal *goal = container_of(kobj, struct damos_sysfs_quota_goal, kobj); + int len; - return sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t path_store(struct kobject *kobj, @@ -1213,8 +1218,13 @@ static ssize_t path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(goal->path); goal->path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
a34ca3e33da4mm/damon/sysfs-schemes: protect path kfree() with damon_sysfs_lock
2 files changed · +22 −4
mm/damon/sysfs-schemes.c+11 −2 modifieddiff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index fbba0016972d3f..9302ad0a603b09 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1197,8 +1197,13 @@ static ssize_t path_show(struct kobject *kobj, { struct damos_sysfs_quota_goal *goal = container_of(kobj, struct damos_sysfs_quota_goal, kobj); + int len; - return sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t path_store(struct kobject *kobj, @@ -1213,8 +1218,13 @@ static ssize_t path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(goal->path); goal->path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
mm/damon/sysfs-schemes.c+11 −2 modifieddiff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index fbba0016972d3f..9302ad0a603b09 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1197,8 +1197,13 @@ static ssize_t path_show(struct kobject *kobj, { struct damos_sysfs_quota_goal *goal = container_of(kobj, struct damos_sysfs_quota_goal, kobj); + int len; - return sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t path_store(struct kobject *kobj, @@ -1213,8 +1218,13 @@ static ssize_t path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(goal->path); goal->path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
a34ca3e33da4mm/damon/sysfs-schemes: protect path kfree() with damon_sysfs_lock
2 files changed · +22 −4
mm/damon/sysfs-schemes.c+11 −2 modifieddiff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index fbba0016972d3f..9302ad0a603b09 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1197,8 +1197,13 @@ static ssize_t path_show(struct kobject *kobj, { struct damos_sysfs_quota_goal *goal = container_of(kobj, struct damos_sysfs_quota_goal, kobj); + int len; - return sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t path_store(struct kobject *kobj, @@ -1213,8 +1218,13 @@ static ssize_t path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(goal->path); goal->path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
mm/damon/sysfs-schemes.c+11 −2 modifieddiff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index fbba0016972d3f..9302ad0a603b09 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1197,8 +1197,13 @@ static ssize_t path_show(struct kobject *kobj, { struct damos_sysfs_quota_goal *goal = container_of(kobj, struct damos_sysfs_quota_goal, kobj); + int len; - return sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t path_store(struct kobject *kobj, @@ -1213,8 +1218,13 @@ static ssize_t path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(goal->path); goal->path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
cf3b71421ca0mm/damon/sysfs-schemes: protect path kfree() with damon_sysfs_lock
2 files changed · +22 −4
mm/damon/sysfs-schemes.c+11 −2 modifieddiff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 8d32a20531d496..245d63808411a2 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1197,8 +1197,13 @@ static ssize_t path_show(struct kobject *kobj, { struct damos_sysfs_quota_goal *goal = container_of(kobj, struct damos_sysfs_quota_goal, kobj); + int len; - return sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t path_store(struct kobject *kobj, @@ -1213,8 +1218,13 @@ static ssize_t path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(goal->path); goal->path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
mm/damon/sysfs-schemes.c+11 −2 modifieddiff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 8d32a20531d496..245d63808411a2 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1197,8 +1197,13 @@ static ssize_t path_show(struct kobject *kobj, { struct damos_sysfs_quota_goal *goal = container_of(kobj, struct damos_sysfs_quota_goal, kobj); + int len; - return sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", goal->path ? goal->path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t path_store(struct kobject *kobj, @@ -1213,8 +1218,13 @@ static ssize_t path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(goal->path); goal->path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing lock protection around concurrent reads and writes of `damon_sysfs_quot_goal->path` allows a use-after-free when a write frees the path buffer while a concurrent read accesses it."
Attack vector
An attacker with local access to the DAMON sysfs interface can trigger a use-after-free by opening the `path` file under a quota goal directory with two separate file descriptors. One descriptor writes a new path value, causing `kfree(goal->path)` to deallocate the buffer, while the other descriptor concurrently reads the `path` file. Because the read (`path_show`) and write (`path_store`) operations were not protected by `damon_sysfs_lock`, the reader may dereference the freed `goal->path` pointer [patch_id=2897972]. The race does not occur when the same open file is used for both operations due to kernfs's internal open-file locking, but using separate file descriptors is a common usage pattern.
Affected code
The vulnerable functions are `path_show()` and `path_store()` in `mm/damon/sysfs-schemes.c`, which handle reads and writes to the DAMON sysfs `path` file under a quota goal directory [patch_id=2897972]. Both functions operate on `goal->path` (a `char *` field of `struct damos_sysfs_quota_goal`) without holding `damon_sysfs_lock`.
What the fix does
The patch adds `mutex_trylock(&damon_sysfs_lock)` / `mutex_unlock(&damon_sysfs_lock)` around both `path_show()` and `path_store()` in `mm/damon/sysfs-schemes.c` [patch_id=2897972]. In `path_show()`, the lock is acquired before reading `goal->path` and released after `sysfs_emit()`. In `path_store()`, the lock is acquired before `kfree(goal->path)` and the assignment `goal->path = path`, and released after the assignment. If the lock cannot be acquired, `path_store()` frees the temporary copy and returns `-EBUSY`. This ensures that the deallocation of the old path buffer never races with a concurrent reader, closing the use-after-free window.
Preconditions
- authThe attacker must have local access to the DAMON sysfs interface (typically root or CAP_SYS_ADMIN).
- inputThe attacker must open the DAMON sysfs 'path' file with two separate file descriptors.
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.