mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 03:27:30 -04:00
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:
committed by
Christian Brauner
parent
a76640171b
commit
6650527444
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user