]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
proc: protect ptrace_may_access() with exec_update_lock (part 1)
authorJann Horn <jannh@google.com>
Mon, 18 May 2026 16:35:15 +0000 (18:35 +0200)
committerChristian Brauner <brauner@kernel.org>
Fri, 5 Jun 2026 08:00:55 +0000 (10:00 +0200)
Fix the easy cases where procfs currently calls ptrace_may_access() without
exec_update_lock protection, where the fix is to simply add the extra lock
or use mm_access():

 - do_task_stat(): grab exec_update_lock
 - proc_pid_wchan(): grab exec_update_lock
 - proc_map_files_lookup(): use mm_access() instead of get_task_mm()
 - proc_map_files_readdir(): use mm_access() instead of get_task_mm()
 - proc_ns_get_link(): grab exec_update_lock
 - proc_ns_readlink(): grab exec_update_lock

Fixes: f83ce3e6b02d ("proc: avoid information leaks to non-privileged processes")
Cc: stable@vger.kernel.org
Signed-off-by: Jann Horn <jannh@google.com>
Link: https://patch.msgid.link/20260518-procfs-lockfix-part1-v1-1-5c3d20e0ac33@google.com
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
fs/proc/array.c
fs/proc/base.c
fs/proc/namespaces.c

index 90fb0c6b5f99fe2e5337a4e92ac99a1b4f15c90b..479ea8cb4ef43d4e594b001a89bad107cbe9d32a 100644 (file)
@@ -482,6 +482,11 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
        unsigned long flags;
        int exit_code = task->exit_code;
        struct signal_struct *sig = task->signal;
+       int ret;
+
+       ret = down_read_killable(&task->signal->exec_update_lock);
+       if (ret)
+               return ret;
 
        state = *get_task_state(task);
        vsize = eip = esp = 0;
@@ -657,6 +662,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
                seq_puts(m, " 0");
 
        seq_putc(m, '\n');
+       up_read(&task->signal->exec_update_lock);
        if (mm)
                mmput(mm);
        return 0;
index d9acfa89c894bd1608580331e1d5b3018c59123b..cc99d953a0a3550b19d4885222b578361d4286c4 100644 (file)
@@ -423,18 +423,24 @@ static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns,
 {
        unsigned long wchan;
        char symname[KSYM_NAME_LEN];
+       int err;
 
+       err = down_read_killable(&task->signal->exec_update_lock);
+       if (err)
+               return err;
        if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
                goto print0;
 
        wchan = get_wchan(task);
        if (wchan && !lookup_symbol_name(wchan, symname)) {
                seq_puts(m, symname);
+               up_read(&task->signal->exec_update_lock);
                return 0;
        }
 
 print0:
        seq_putc(m, '0');
+       up_read(&task->signal->exec_update_lock);
        return 0;
 }
 #endif /* CONFIG_KALLSYMS */
@@ -2360,17 +2366,15 @@ static struct dentry *proc_map_files_lookup(struct inode *dir,
        if (!task)
                goto out;
 
-       result = ERR_PTR(-EACCES);
-       if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
-               goto out_put_task;
-
        result = ERR_PTR(-ENOENT);
        if (dname_to_vma_addr(dentry, &vm_start, &vm_end))
                goto out_put_task;
 
-       mm = get_task_mm(task);
-       if (!mm)
+       mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
+       if (IS_ERR(mm)) {
+               result = ERR_CAST(mm);
                goto out_put_task;
+       }
 
        result = ERR_PTR(-EINTR);
        if (mmap_read_lock_killable(mm))
@@ -2420,23 +2424,22 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx)
        if (!task)
                goto out;
 
-       ret = -EACCES;
-       if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
-               goto out_put_task;
-
        ret = 0;
        if (!dir_emit_dots(file, ctx))
                goto out_put_task;
 
-       mm = get_task_mm(task);
-       if (!mm)
+       mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
+       if (IS_ERR(mm)) {
+               ret = PTR_ERR(mm);
+               /* if the task has no mm, the directory should just be empty */
+               if (ret == -ESRCH)
+                       ret = 0;
                goto out_put_task;
+       }
 
        ret = mmap_read_lock_killable(mm);
-       if (ret) {
-               mmput(mm);
-               goto out_put_task;
-       }
+       if (ret)
+               goto out_put_mm;
 
        nr_files = 0;
 
@@ -2462,8 +2465,7 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx)
                if (!p) {
                        ret = -ENOMEM;
                        mmap_read_unlock(mm);
-                       mmput(mm);
-                       goto out_put_task;
+                       goto out_put_mm;
                }
 
                p->start = vma->vm_start;
@@ -2471,7 +2473,6 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx)
                p->mode = vma->vm_file->f_mode;
        }
        mmap_read_unlock(mm);
-       mmput(mm);
 
        for (i = 0; i < nr_files; i++) {
                char buf[4 * sizeof(long) + 2]; /* max: %lx-%lx\0 */
@@ -2488,6 +2489,8 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx)
                ctx->pos++;
        }
 
+out_put_mm:
+       mmput(mm);
 out_put_task:
        put_task_struct(task);
 out:
index 39f4169f669f632fb46155e8a6cab79298e00465..2f46f13967445c0a5a41aa911afb6ef7223d7b1f 100644 (file)
@@ -55,6 +55,10 @@ static const char *proc_ns_get_link(struct dentry *dentry,
        if (!task)
                return ERR_PTR(-EACCES);
 
+       error = down_read_killable(&task->signal->exec_update_lock);
+       if (error)
+               goto out_put_task;
+
        if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
                goto out;
 
@@ -64,6 +68,8 @@ static const char *proc_ns_get_link(struct dentry *dentry,
 
        error = nd_jump_link(&ns_path);
 out:
+       up_read(&task->signal->exec_update_lock);
+out_put_task:
        put_task_struct(task);
        return ERR_PTR(error);
 }
@@ -80,11 +86,17 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl
        if (!task)
                return res;
 
+       res = down_read_killable(&task->signal->exec_update_lock);
+       if (res)
+               goto out_put_task;
+
        if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
                res = ns_get_name(name, sizeof(name), task, ns_ops);
                if (res >= 0)
                        res = readlink_copy(buffer, buflen, name, strlen(name));
        }
+       up_read(&task->signal->exec_update_lock);
+out_put_task:
        put_task_struct(task);
        return res;
 }