mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02: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:
parent
a76640171b
commit
6650527444
|
|
@ -482,6 +482,11 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int exit_code = task->exit_code;
|
int exit_code = task->exit_code;
|
||||||
struct signal_struct *sig = task->signal;
|
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);
|
state = *get_task_state(task);
|
||||||
vsize = eip = esp = 0;
|
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_puts(m, " 0");
|
||||||
|
|
||||||
seq_putc(m, '\n');
|
seq_putc(m, '\n');
|
||||||
|
up_read(&task->signal->exec_update_lock);
|
||||||
if (mm)
|
if (mm)
|
||||||
mmput(mm);
|
mmput(mm);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -423,18 +423,24 @@ static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns,
|
||||||
{
|
{
|
||||||
unsigned long wchan;
|
unsigned long wchan;
|
||||||
char symname[KSYM_NAME_LEN];
|
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))
|
if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
|
||||||
goto print0;
|
goto print0;
|
||||||
|
|
||||||
wchan = get_wchan(task);
|
wchan = get_wchan(task);
|
||||||
if (wchan && !lookup_symbol_name(wchan, symname)) {
|
if (wchan && !lookup_symbol_name(wchan, symname)) {
|
||||||
seq_puts(m, symname);
|
seq_puts(m, symname);
|
||||||
|
up_read(&task->signal->exec_update_lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
print0:
|
print0:
|
||||||
seq_putc(m, '0');
|
seq_putc(m, '0');
|
||||||
|
up_read(&task->signal->exec_update_lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_KALLSYMS */
|
#endif /* CONFIG_KALLSYMS */
|
||||||
|
|
@ -2360,17 +2366,15 @@ static struct dentry *proc_map_files_lookup(struct inode *dir,
|
||||||
if (!task)
|
if (!task)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
result = ERR_PTR(-EACCES);
|
|
||||||
if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
|
|
||||||
goto out_put_task;
|
|
||||||
|
|
||||||
result = ERR_PTR(-ENOENT);
|
result = ERR_PTR(-ENOENT);
|
||||||
if (dname_to_vma_addr(dentry, &vm_start, &vm_end))
|
if (dname_to_vma_addr(dentry, &vm_start, &vm_end))
|
||||||
goto out_put_task;
|
goto out_put_task;
|
||||||
|
|
||||||
mm = get_task_mm(task);
|
mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
|
||||||
if (!mm)
|
if (IS_ERR(mm)) {
|
||||||
|
result = ERR_CAST(mm);
|
||||||
goto out_put_task;
|
goto out_put_task;
|
||||||
|
}
|
||||||
|
|
||||||
result = ERR_PTR(-EINTR);
|
result = ERR_PTR(-EINTR);
|
||||||
if (mmap_read_lock_killable(mm))
|
if (mmap_read_lock_killable(mm))
|
||||||
|
|
@ -2420,24 +2424,23 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx)
|
||||||
if (!task)
|
if (!task)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
ret = -EACCES;
|
|
||||||
if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
|
|
||||||
goto out_put_task;
|
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
if (!dir_emit_dots(file, ctx))
|
if (!dir_emit_dots(file, ctx))
|
||||||
goto out_put_task;
|
goto out_put_task;
|
||||||
|
|
||||||
mm = get_task_mm(task);
|
mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
|
||||||
if (!mm)
|
if (IS_ERR(mm)) {
|
||||||
goto out_put_task;
|
ret = PTR_ERR(mm);
|
||||||
|
/* if the task has no mm, the directory should just be empty */
|
||||||
ret = mmap_read_lock_killable(mm);
|
if (ret == -ESRCH)
|
||||||
if (ret) {
|
ret = 0;
|
||||||
mmput(mm);
|
|
||||||
goto out_put_task;
|
goto out_put_task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = mmap_read_lock_killable(mm);
|
||||||
|
if (ret)
|
||||||
|
goto out_put_mm;
|
||||||
|
|
||||||
nr_files = 0;
|
nr_files = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -2462,8 +2465,7 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx)
|
||||||
if (!p) {
|
if (!p) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
mmap_read_unlock(mm);
|
mmap_read_unlock(mm);
|
||||||
mmput(mm);
|
goto out_put_mm;
|
||||||
goto out_put_task;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p->start = vma->vm_start;
|
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;
|
p->mode = vma->vm_file->f_mode;
|
||||||
}
|
}
|
||||||
mmap_read_unlock(mm);
|
mmap_read_unlock(mm);
|
||||||
mmput(mm);
|
|
||||||
|
|
||||||
for (i = 0; i < nr_files; i++) {
|
for (i = 0; i < nr_files; i++) {
|
||||||
char buf[4 * sizeof(long) + 2]; /* max: %lx-%lx\0 */
|
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++;
|
ctx->pos++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out_put_mm:
|
||||||
|
mmput(mm);
|
||||||
out_put_task:
|
out_put_task:
|
||||||
put_task_struct(task);
|
put_task_struct(task);
|
||||||
out:
|
out:
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,10 @@ static const char *proc_ns_get_link(struct dentry *dentry,
|
||||||
if (!task)
|
if (!task)
|
||||||
return ERR_PTR(-EACCES);
|
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))
|
if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
|
@ -64,6 +68,8 @@ static const char *proc_ns_get_link(struct dentry *dentry,
|
||||||
|
|
||||||
error = nd_jump_link(&ns_path);
|
error = nd_jump_link(&ns_path);
|
||||||
out:
|
out:
|
||||||
|
up_read(&task->signal->exec_update_lock);
|
||||||
|
out_put_task:
|
||||||
put_task_struct(task);
|
put_task_struct(task);
|
||||||
return ERR_PTR(error);
|
return ERR_PTR(error);
|
||||||
}
|
}
|
||||||
|
|
@ -80,11 +86,17 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl
|
||||||
if (!task)
|
if (!task)
|
||||||
return res;
|
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)) {
|
if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
|
||||||
res = ns_get_name(name, sizeof(name), task, ns_ops);
|
res = ns_get_name(name, sizeof(name), task, ns_ops);
|
||||||
if (res >= 0)
|
if (res >= 0)
|
||||||
res = readlink_copy(buffer, buflen, name, strlen(name));
|
res = readlink_copy(buffer, buflen, name, strlen(name));
|
||||||
}
|
}
|
||||||
|
up_read(&task->signal->exec_update_lock);
|
||||||
|
out_put_task:
|
||||||
put_task_struct(task);
|
put_task_struct(task);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user