proc: protect ptrace_may_access() with exec_update_lock (part 1)

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: f83ce3e6b0 ("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>
This commit is contained in:
Jann Horn
2026-05-18 18:35:15 +02:00
committed by Christian Brauner
parent a76640171b
commit 6650527444
3 changed files with 41 additions and 20 deletions

View 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;

View 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,24 +2424,23 @@ 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)
goto out_put_task;
ret = mmap_read_lock_killable(mm);
if (ret) {
mmput(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)
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:

View 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;
}