CVE-2025-38106 in Linux
Riassunto
di VulDB • 18/06/2026
Based on the KASAN report and the description provided, here is the analysis and the fix for the use-after-free bug in `io_uring`.
### Analysis
1. **The Bug**: A `task_struct` (specifically `sq->thread` in the `io_uring` context) is being accessed after it has been freed. 2. **The Cause**: The code in `__io_uring_show_fdinfo()` accesses `sq->thread` while holding `ctx->uring_lock`. However, `uring_lock` does not prevent the `sq->thread` task from exiting or being released. The task can be freed by the kernel's exit path (as seen in the "Freed by task 22" stack trace involving `rcu_core` and `do_exit`) while `__io_uring_show_fdinfo()` is still trying to read it. 3. **The Solution**: Use RCU (Read-Copy-Update) to safely access the `task_struct`. * Assign `sq->thread` using `rcu_assign_pointer()`. * Look up `sq->thread` using `rcu_dereference()`. * Grab a reference to the task using `get_task_struct()` to prevent it from being freed while in use. * Release the reference using `put_task_struct()` when done.
### The Fix
The fix needs to be applied in the `__io_uring_show_fdinfo()` function (and potentially where `sq->thread` is assigned, though the prompt implies the access is the main issue).
Here is the patch for `fs/io_uring.c`:
```c diff --git a/fs/io_uring.c b/fs/io_uring.c index 1234567890..abcdef1234 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -... +... @@ static int __io_uring_show_fdinfo(struct seq_file *m, struct io_ring_ctx *ctx) {
struct io_sq_data *sq = ctx->sq_data; + struct task_struct *task; if (!sq) return 0; + task = rcu_dereference(sq->thread); + if (task) {
+ get_task_struct(task); + seq_printf(m, "sq_thread: %s\n", task->comm); + put_task_struct(task); + } + return 0; } ```
Additionally, ensure that `sq->thread` is assigned using `rcu_assign_pointer()` when the thread is created or assigned, and that any previous assignments are updated to use RCU semantics if they weren't already. For example, in `io_sq_offload_create()`:
```c diff --git a/fs/io_uring.c b/fs/io_uring.c index 1234567890..abcdef1234 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -... +... @@ static int io_sq_offload_create(struct io_ring_ctx *ctx, unsigned int issue_flags) {
// ... existing code ... + rcu_assign_pointer(sq->thread, thread); + // ... existing code ... } ```
And in `io_sq_offload_stop()` or similar cleanup functions, ensure the pointer is cleared with `synchronize_rcu()` or `call_rcu()` to ensure all RCU readers have finished before freeing the `sq_data` structure if it contains the pointer. However, since `sq_data` is typically freed after the context is destroyed, and `__io_uring_show_fdinfo` is called while the context is alive, the primary fix is in the reader side (`__io_uring_show_fdinfo`).
### Explanation of the Fix
1. **`rcu_dereference(sq->thread)`**: This safely reads the pointer to the `task_struct`. It ensures that the compiler and CPU do not reorder the read in a way that could see a partially initialized or freed pointer. 2. **`get_task_struct(task)`**: This increments the reference count of the `task_struct`. This prevents the task from being freed by the kernel's exit path while we are using it. 3. **`put_task_struct(task)`**: This decrements the reference count. If the count reaches zero, the task will be freed. This must be called after we are done using the task. 4. **`rcu_assign_pointer(sq->thread, thread)`**: This ensures that the write to `sq->thread` is visible to all RCU readers before the pointer is considered valid.
This
If you want to get best quality of vulnerability data, you may have to visit VulDB.