[PATCH v3 1/2] debugfs: fix use-after-free in debugfs_read_file_str()

From: Aldo Ariel Panzardo

Date: Sat Sep 26 2026 - 10:14:29 EST


debugfs_write_file_str() publishes a new string pointer via
rcu_assign_pointer(), waits for a grace period with synchronize_rcu(),
then frees the old string.

debugfs_read_file_str() dereferences file->private_data without holding
an RCU read-side critical section: it loads the pointer, calls strlen()
on it, and then copies the string. If a concurrent writer completes
synchronize_rcu() and kfree()s the old string between the load and the
use, the reader accesses freed memory.

Fix by wrapping the pointer dereference and copy inside
rcu_read_lock()/rcu_read_unlock(), using rcu_dereference() to annotate
the load. Switch to kmemdup() with GFP_ATOMIC so the allocation stays
within the RCU critical section.

Tested on QEMU/KVM (7.3.0-rc4 + KASAN). While KASAN did not
independently confirm this particular race window (it is a very narrow
reader-side window), the companion double-free in the write path
confirms that the RCU synchronization in this file is incomplete (see
the following patch).

Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: sashiko.dev <sashiko-bot@xxxxxxxxxx>
Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@xxxxxxxxx>
---
fs/debugfs/file.c | 25 +++++++++----------------
1 file changed, 9 insertions(+), 16 deletions(-)

diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index 08de6652a..ac4199e9f 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -1018,34 +1018,27 @@ ssize_t debugfs_read_file_str(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct dentry *dentry = F_DENTRY(file);
- char *str, *copy = NULL;
- int copy_len, len;
+ char *str, *copy;
+ int len;
ssize_t ret;

ret = debugfs_file_get(dentry);
if (unlikely(ret))
return ret;

- str = *(char **)file->private_data;
+ rcu_read_lock();
+ str = rcu_dereference(*(char __rcu **)file->private_data);
len = strlen(str) + 1;
- copy = kmalloc(len, GFP_KERNEL);
- if (!copy) {
- debugfs_file_put(dentry);
- return -ENOMEM;
- }
+ copy = kmemdup(str, len, GFP_ATOMIC);
+ rcu_read_unlock();

- copy_len = strscpy(copy, str, len);
debugfs_file_put(dentry);
- if (copy_len < 0) {
- kfree(copy);
- return copy_len;
- }
-
- copy[copy_len] = '\n';
+ if (!copy)
+ return -ENOMEM;

+ copy[len - 1] = '\n';
ret = simple_read_from_buffer(user_buf, count, ppos, copy, len);
kfree(copy);
-
return ret;
}

--
2.43.0