VYPR
Unrated severityNVD Advisory· Published May 28, 2026

CVE-2026-46183

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

2

Patches

4
cf3b71421ca0

mm/damon/sysfs-schemes: protect path kfree() with damon_sysfs_lock

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitSeongJae ParkApr 23, 2026Fixed in 7.1-rc2via kernel-cna
2 files changed · +22 4
  • mm/damon/sysfs-schemes.c+11 2 modified
    diff --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 modified
    diff --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
    
    
    
a34ca3e33da4

mm/damon/sysfs-schemes: protect path kfree() with damon_sysfs_lock

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitSeongJae ParkApr 23, 2026Fixed in 7.0.7via kernel-cna
2 files changed · +22 4
  • mm/damon/sysfs-schemes.c+11 2 modified
    diff --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 modified
    diff --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
    
    
    
a34ca3e33da4

mm/damon/sysfs-schemes: protect path kfree() with damon_sysfs_lock

2 files changed · +22 4
  • mm/damon/sysfs-schemes.c+11 2 modified
    diff --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 modified
    diff --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
    
    
    
cf3b71421ca0

mm/damon/sysfs-schemes: protect path kfree() with damon_sysfs_lock

2 files changed · +22 4
  • mm/damon/sysfs-schemes.c+11 2 modified
    diff --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 modified
    diff --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

2

News mentions

0

No linked articles in our index yet.