CVE-2026-46121
Description
In the Linux kernel, the following vulnerability has been resolved:
mm/damon/sysfs-schemes: protect memcg_path kfree() with damon_sysfs_lock
Patch series "mm/damon/sysfs-schemes: fix use-after-free for [memcg_]path".
Reads of 'memcg_path' and 'path' files in DAMON sysfs interface could race with their writes, results in use-after-free. Fix those.
This patch (of 2):
damon_sysfs_scheme_filter->mmecg_path can be read and written by users, via DAMON sysfs memcg_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 memcg_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.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
DAMON sysfs interface in the Linux kernel has a use-after-free race when memcg_path is read and written via separate file descriptors simultaneously.
Vulnerability
A use-after-free vulnerability exists in the DAMON sysfs interface of the Linux kernel, specifically in the memcg_path file. The damon_sysfs_scheme_filter->memcg_path buffer is deallocated during write operations but can be read concurrently by user-space readers using separate open file descriptors. While kernfs locks protect writes when the same file descriptor is used, multiple file descriptors allow a race condition. This affects the DAMON sysfs memcg_path file across all kernel versions where this interface is present. The fix is included in the stable kernel tree commit c88802d0e8edd14b6cd2daf3000f99adbc4c85c5 [1].
Exploitation
An attacker must have local user access to the system and the ability to open separate file descriptors for the same memcg_path file in the DAMON sysfs directory. While a write operation is freeing and reassigning the memcg_path buffer, a concurrent read operation on a different descriptor accesses the freed buffer. No elevated privileges are required beyond being able to read/write the DAMON sysfs files (typically root or a user with appropriate capabilities). The race window is small but exploitable with repeated attempts [1].
Impact
Successful exploitation results in a use-after-free condition, where the kernel reads from memory that has already been freed. This can lead to information disclosure (reading sensitive kernel memory) or a system crash (denial of service). In more severe cases, it could potentially be leveraged for privilege escalation if the freed memory is reallocated with attacker-controlled content [1].
Mitigation
The vulnerability is fixed in the Linux kernel stable tree with commit c88802d0e8edd14b6cd2daf3000f99adbc4c85c5 [1]. Users should update their kernel to a version containing this commit. The fix protects both direct user reads and writes by holding damon_sysfs_lock during the operation, preventing the race condition. No workaround exists other than applying the kernel update.
AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
10b1e9f2d58707mm/damon/sysfs-schemes: protect memcg_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 c774f1e5c0a5d9..dd04bd38567a84 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -360,9 +360,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -376,8 +381,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_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 c774f1e5c0a5d9..dd04bd38567a84 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -360,9 +360,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -376,8 +381,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
c88802d0e8edmm/damon/sysfs-schemes: protect memcg_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 3748982c2b9ee8..c2890f32368fce 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -407,9 +407,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -423,8 +428,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_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 3748982c2b9ee8..c2890f32368fce 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -407,9 +407,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -423,8 +428,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
1e68eb96e8bemm/damon/sysfs-schemes: protect memcg_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 5186966dafb350..8d32a20531d496 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -533,9 +533,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -550,8 +555,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_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 5186966dafb350..8d32a20531d496 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -533,9 +533,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -550,8 +555,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
eafd6f5372d2mm/damon/sysfs-schemes: protect memcg_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 50d000d61c9076..ca82dc78f0b98e 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -494,9 +494,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -511,8 +516,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_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 50d000d61c9076..ca82dc78f0b98e 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -494,9 +494,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -511,8 +516,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
baecc45ad60emm/damon/sysfs-schemes: protect memcg_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 3a0782e576fab7..fbba0016972d3f 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -533,9 +533,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -550,8 +555,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_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 3a0782e576fab7..fbba0016972d3f 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -533,9 +533,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -550,8 +555,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
c88802d0e8edmm/damon/sysfs-schemes: protect memcg_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 3748982c2b9ee8..c2890f32368fce 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -407,9 +407,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -423,8 +428,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_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 3748982c2b9ee8..c2890f32368fce 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -407,9 +407,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -423,8 +428,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
1e68eb96e8bemm/damon/sysfs-schemes: protect memcg_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 5186966dafb350..8d32a20531d496 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -533,9 +533,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -550,8 +555,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_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 5186966dafb350..8d32a20531d496 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -533,9 +533,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -550,8 +555,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
b1e9f2d58707mm/damon/sysfs-schemes: protect memcg_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 c774f1e5c0a5d9..dd04bd38567a84 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -360,9 +360,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -376,8 +381,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_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 c774f1e5c0a5d9..dd04bd38567a84 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -360,9 +360,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -376,8 +381,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
eafd6f5372d2mm/damon/sysfs-schemes: protect memcg_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 50d000d61c9076..ca82dc78f0b98e 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -494,9 +494,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -511,8 +516,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_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 50d000d61c9076..ca82dc78f0b98e 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -494,9 +494,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -511,8 +516,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
baecc45ad60emm/damon/sysfs-schemes: protect memcg_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 3a0782e576fab7..fbba0016972d3f 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -533,9 +533,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -550,8 +555,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_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 3a0782e576fab7..fbba0016972d3f 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -533,9 +533,14 @@ static ssize_t memcg_path_show(struct kobject *kobj, { struct damon_sysfs_scheme_filter *filter = container_of(kobj, struct damon_sysfs_scheme_filter, kobj); + int len; - return sysfs_emit(buf, "%s\n", + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + len = sysfs_emit(buf, "%s\n", filter->memcg_path ? filter->memcg_path : ""); + mutex_unlock(&damon_sysfs_lock); + return len; } static ssize_t memcg_path_store(struct kobject *kobj, @@ -550,8 +555,13 @@ static ssize_t memcg_path_store(struct kobject *kobj, return -ENOMEM; strscpy(path, buf, count + 1); + if (!mutex_trylock(&damon_sysfs_lock)) { + kfree(path); + return -EBUSY; + } kfree(filter->memcg_path); filter->memcg_path = path; + mutex_unlock(&damon_sysfs_lock); return count; } -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing synchronization in DAMON sysfs memcg_path file operations allows a concurrent write (which kfree's the buffer) to race with a read (which dereferences the buffer), causing use-after-free."
Attack vector
An attacker with local access to the DAMON sysfs interface can trigger a use-after-free by opening the `memcg_path` file with two separate file descriptors. One descriptor writes a new path, causing `memcg_path_store` to `kfree()` the existing buffer, while the other descriptor concurrently reads `memcg_path_show`, which dereferences the now-freed pointer. The race is possible because kernfs locking only protects concurrent operations on the same open file, not across separate opens [patch_id=2898522].
Affected code
The vulnerability resides in `mm/damon/sysfs-schemes.c` within the `memcg_path_show()` and `memcg_path_store()` functions of `damon_sysfs_scheme_filter`. The `filter->memcg_path` pointer is read and written via the DAMON sysfs `memcg_path` file without any locking, while the write path (`memcg_path_store`) calls `kfree(filter->memcg_path)` to deallocate the buffer [patch_id=2898522].
What the fix does
The patch wraps both `memcg_path_show()` and `memcg_path_store()` with `mutex_trylock(&damon_sysfs_lock)` / `mutex_unlock(&damon_sysfs_lock)`. In the show path, the lock is acquired before reading `filter->memcg_path` and released after `sysfs_emit`. In the store path, the lock is acquired just before `kfree(filter->memcg_path)` and released after the pointer is updated. If the lock cannot be acquired, the functions return `-EBUSY`. This serializes all direct user reads and writes of `memcg_path`, preventing a concurrent `kfree` from racing with a read [patch_id=2898522].
Preconditions
- authThe attacker must have local access to the DAMON sysfs interface (the memcg_path file under the DAMON sysfs scheme filter directory).
- inputThe attacker must be able to open the memcg_path file with two separate file descriptors to bypass kernfs's per-open-file locking.
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/1e68eb96e8beb1abefd12dd22c5637795d8a877envd
- git.kernel.org/stable/c/b1e9f2d5870776347edef927f9bb3ea19b8e3abbnvd
- git.kernel.org/stable/c/baecc45ad60e621ef14d6c1e7f41ef36bbfdf910nvd
- git.kernel.org/stable/c/c88802d0e8edd14b6cd2daf3000f99adbc4c85c5nvd
- git.kernel.org/stable/c/eafd6f5372d29b0dd213799b92c2c9c7ad31d7danvd
News mentions
0No linked articles in our index yet.