diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2011-03-23 15:52:50 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-03-23 17:01:18 -0400 |
commit | a9712bc12c40c172e393f85a9b2ba8db4bf59509 (patch) | |
tree | c40217e028ae937da7ad94e249e2247191ffcc8f /fs/proc | |
parent | 198214a7ee50375fa71a65e518341980cfd4b2f0 (diff) |
deal with races in /proc/*/{syscall,stack,personality}
All of those are rw-r--r-- and all are broken for suid - if you open
a file before the target does suid-root exec, you'll be still able
to access it. For personality it's not a big deal, but for syscall
and stack it's a real problem.
Fix: check that task is tracable for you at the time of read().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/proc')
-rw-r--r-- | fs/proc/base.c | 69 |
1 files changed, 50 insertions, 19 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index bc15df390ec4..7d5bb8b9a4ff 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -347,6 +347,23 @@ static int proc_pid_wchan(struct task_struct *task, char *buffer) | |||
347 | } | 347 | } |
348 | #endif /* CONFIG_KALLSYMS */ | 348 | #endif /* CONFIG_KALLSYMS */ |
349 | 349 | ||
350 | static int lock_trace(struct task_struct *task) | ||
351 | { | ||
352 | int err = mutex_lock_killable(&task->signal->cred_guard_mutex); | ||
353 | if (err) | ||
354 | return err; | ||
355 | if (!ptrace_may_access(task, PTRACE_MODE_ATTACH)) { | ||
356 | mutex_unlock(&task->signal->cred_guard_mutex); | ||
357 | return -EPERM; | ||
358 | } | ||
359 | return 0; | ||
360 | } | ||
361 | |||
362 | static void unlock_trace(struct task_struct *task) | ||
363 | { | ||
364 | mutex_unlock(&task->signal->cred_guard_mutex); | ||
365 | } | ||
366 | |||
350 | #ifdef CONFIG_STACKTRACE | 367 | #ifdef CONFIG_STACKTRACE |
351 | 368 | ||
352 | #define MAX_STACK_TRACE_DEPTH 64 | 369 | #define MAX_STACK_TRACE_DEPTH 64 |
@@ -356,6 +373,7 @@ static int proc_pid_stack(struct seq_file *m, struct pid_namespace *ns, | |||
356 | { | 373 | { |
357 | struct stack_trace trace; | 374 | struct stack_trace trace; |
358 | unsigned long *entries; | 375 | unsigned long *entries; |
376 | int err; | ||
359 | int i; | 377 | int i; |
360 | 378 | ||
361 | entries = kmalloc(MAX_STACK_TRACE_DEPTH * sizeof(*entries), GFP_KERNEL); | 379 | entries = kmalloc(MAX_STACK_TRACE_DEPTH * sizeof(*entries), GFP_KERNEL); |
@@ -366,15 +384,20 @@ static int proc_pid_stack(struct seq_file *m, struct pid_namespace *ns, | |||
366 | trace.max_entries = MAX_STACK_TRACE_DEPTH; | 384 | trace.max_entries = MAX_STACK_TRACE_DEPTH; |
367 | trace.entries = entries; | 385 | trace.entries = entries; |
368 | trace.skip = 0; | 386 | trace.skip = 0; |
369 | save_stack_trace_tsk(task, &trace); | ||
370 | 387 | ||
371 | for (i = 0; i < trace.nr_entries; i++) { | 388 | err = lock_trace(task); |
372 | seq_printf(m, "[<%p>] %pS\n", | 389 | if (!err) { |
373 | (void *)entries[i], (void *)entries[i]); | 390 | save_stack_trace_tsk(task, &trace); |
391 | |||
392 | for (i = 0; i < trace.nr_entries; i++) { | ||
393 | seq_printf(m, "[<%p>] %pS\n", | ||
394 | (void *)entries[i], (void *)entries[i]); | ||
395 | } | ||
396 | unlock_trace(task); | ||
374 | } | 397 | } |
375 | kfree(entries); | 398 | kfree(entries); |
376 | 399 | ||
377 | return 0; | 400 | return err; |
378 | } | 401 | } |
379 | #endif | 402 | #endif |
380 | 403 | ||
@@ -537,18 +560,22 @@ static int proc_pid_syscall(struct task_struct *task, char *buffer) | |||
537 | { | 560 | { |
538 | long nr; | 561 | long nr; |
539 | unsigned long args[6], sp, pc; | 562 | unsigned long args[6], sp, pc; |
563 | int res = lock_trace(task); | ||
564 | if (res) | ||
565 | return res; | ||
540 | 566 | ||
541 | if (task_current_syscall(task, &nr, args, 6, &sp, &pc)) | 567 | if (task_current_syscall(task, &nr, args, 6, &sp, &pc)) |
542 | return sprintf(buffer, "running\n"); | 568 | res = sprintf(buffer, "running\n"); |
543 | 569 | else if (nr < 0) | |
544 | if (nr < 0) | 570 | res = sprintf(buffer, "%ld 0x%lx 0x%lx\n", nr, sp, pc); |
545 | return sprintf(buffer, "%ld 0x%lx 0x%lx\n", nr, sp, pc); | 571 | else |
546 | 572 | res = sprintf(buffer, | |
547 | return sprintf(buffer, | ||
548 | "%ld 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", | 573 | "%ld 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", |
549 | nr, | 574 | nr, |
550 | args[0], args[1], args[2], args[3], args[4], args[5], | 575 | args[0], args[1], args[2], args[3], args[4], args[5], |
551 | sp, pc); | 576 | sp, pc); |
577 | unlock_trace(task); | ||
578 | return res; | ||
552 | } | 579 | } |
553 | #endif /* CONFIG_HAVE_ARCH_TRACEHOOK */ | 580 | #endif /* CONFIG_HAVE_ARCH_TRACEHOOK */ |
554 | 581 | ||
@@ -2775,8 +2802,12 @@ static int proc_tgid_io_accounting(struct task_struct *task, char *buffer) | |||
2775 | static int proc_pid_personality(struct seq_file *m, struct pid_namespace *ns, | 2802 | static int proc_pid_personality(struct seq_file *m, struct pid_namespace *ns, |
2776 | struct pid *pid, struct task_struct *task) | 2803 | struct pid *pid, struct task_struct *task) |
2777 | { | 2804 | { |
2778 | seq_printf(m, "%08x\n", task->personality); | 2805 | int err = lock_trace(task); |
2779 | return 0; | 2806 | if (!err) { |
2807 | seq_printf(m, "%08x\n", task->personality); | ||
2808 | unlock_trace(task); | ||
2809 | } | ||
2810 | return err; | ||
2780 | } | 2811 | } |
2781 | 2812 | ||
2782 | /* | 2813 | /* |
@@ -2795,7 +2826,7 @@ static const struct pid_entry tgid_base_stuff[] = { | |||
2795 | REG("environ", S_IRUSR, proc_environ_operations), | 2826 | REG("environ", S_IRUSR, proc_environ_operations), |
2796 | INF("auxv", S_IRUSR, proc_pid_auxv), | 2827 | INF("auxv", S_IRUSR, proc_pid_auxv), |
2797 | ONE("status", S_IRUGO, proc_pid_status), | 2828 | ONE("status", S_IRUGO, proc_pid_status), |
2798 | ONE("personality", S_IRUSR, proc_pid_personality), | 2829 | ONE("personality", S_IRUGO, proc_pid_personality), |
2799 | INF("limits", S_IRUGO, proc_pid_limits), | 2830 | INF("limits", S_IRUGO, proc_pid_limits), |
2800 | #ifdef CONFIG_SCHED_DEBUG | 2831 | #ifdef CONFIG_SCHED_DEBUG |
2801 | REG("sched", S_IRUGO|S_IWUSR, proc_pid_sched_operations), | 2832 | REG("sched", S_IRUGO|S_IWUSR, proc_pid_sched_operations), |
@@ -2805,7 +2836,7 @@ static const struct pid_entry tgid_base_stuff[] = { | |||
2805 | #endif | 2836 | #endif |
2806 | REG("comm", S_IRUGO|S_IWUSR, proc_pid_set_comm_operations), | 2837 | REG("comm", S_IRUGO|S_IWUSR, proc_pid_set_comm_operations), |
2807 | #ifdef CONFIG_HAVE_ARCH_TRACEHOOK | 2838 | #ifdef CONFIG_HAVE_ARCH_TRACEHOOK |
2808 | INF("syscall", S_IRUSR, proc_pid_syscall), | 2839 | INF("syscall", S_IRUGO, proc_pid_syscall), |
2809 | #endif | 2840 | #endif |
2810 | INF("cmdline", S_IRUGO, proc_pid_cmdline), | 2841 | INF("cmdline", S_IRUGO, proc_pid_cmdline), |
2811 | ONE("stat", S_IRUGO, proc_tgid_stat), | 2842 | ONE("stat", S_IRUGO, proc_tgid_stat), |
@@ -2833,7 +2864,7 @@ static const struct pid_entry tgid_base_stuff[] = { | |||
2833 | INF("wchan", S_IRUGO, proc_pid_wchan), | 2864 | INF("wchan", S_IRUGO, proc_pid_wchan), |
2834 | #endif | 2865 | #endif |
2835 | #ifdef CONFIG_STACKTRACE | 2866 | #ifdef CONFIG_STACKTRACE |
2836 | ONE("stack", S_IRUSR, proc_pid_stack), | 2867 | ONE("stack", S_IRUGO, proc_pid_stack), |
2837 | #endif | 2868 | #endif |
2838 | #ifdef CONFIG_SCHEDSTATS | 2869 | #ifdef CONFIG_SCHEDSTATS |
2839 | INF("schedstat", S_IRUGO, proc_pid_schedstat), | 2870 | INF("schedstat", S_IRUGO, proc_pid_schedstat), |
@@ -3135,14 +3166,14 @@ static const struct pid_entry tid_base_stuff[] = { | |||
3135 | REG("environ", S_IRUSR, proc_environ_operations), | 3166 | REG("environ", S_IRUSR, proc_environ_operations), |
3136 | INF("auxv", S_IRUSR, proc_pid_auxv), | 3167 | INF("auxv", S_IRUSR, proc_pid_auxv), |
3137 | ONE("status", S_IRUGO, proc_pid_status), | 3168 | ONE("status", S_IRUGO, proc_pid_status), |
3138 | ONE("personality", S_IRUSR, proc_pid_personality), | 3169 | ONE("personality", S_IRUGO, proc_pid_personality), |
3139 | INF("limits", S_IRUGO, proc_pid_limits), | 3170 | INF("limits", S_IRUGO, proc_pid_limits), |
3140 | #ifdef CONFIG_SCHED_DEBUG | 3171 | #ifdef CONFIG_SCHED_DEBUG |
3141 | REG("sched", S_IRUGO|S_IWUSR, proc_pid_sched_operations), | 3172 | REG("sched", S_IRUGO|S_IWUSR, proc_pid_sched_operations), |
3142 | #endif | 3173 | #endif |
3143 | REG("comm", S_IRUGO|S_IWUSR, proc_pid_set_comm_operations), | 3174 | REG("comm", S_IRUGO|S_IWUSR, proc_pid_set_comm_operations), |
3144 | #ifdef CONFIG_HAVE_ARCH_TRACEHOOK | 3175 | #ifdef CONFIG_HAVE_ARCH_TRACEHOOK |
3145 | INF("syscall", S_IRUSR, proc_pid_syscall), | 3176 | INF("syscall", S_IRUGO, proc_pid_syscall), |
3146 | #endif | 3177 | #endif |
3147 | INF("cmdline", S_IRUGO, proc_pid_cmdline), | 3178 | INF("cmdline", S_IRUGO, proc_pid_cmdline), |
3148 | ONE("stat", S_IRUGO, proc_tid_stat), | 3179 | ONE("stat", S_IRUGO, proc_tid_stat), |
@@ -3169,7 +3200,7 @@ static const struct pid_entry tid_base_stuff[] = { | |||
3169 | INF("wchan", S_IRUGO, proc_pid_wchan), | 3200 | INF("wchan", S_IRUGO, proc_pid_wchan), |
3170 | #endif | 3201 | #endif |
3171 | #ifdef CONFIG_STACKTRACE | 3202 | #ifdef CONFIG_STACKTRACE |
3172 | ONE("stack", S_IRUSR, proc_pid_stack), | 3203 | ONE("stack", S_IRUGO, proc_pid_stack), |
3173 | #endif | 3204 | #endif |
3174 | #ifdef CONFIG_SCHEDSTATS | 3205 | #ifdef CONFIG_SCHEDSTATS |
3175 | INF("schedstat", S_IRUGO, proc_pid_schedstat), | 3206 | INF("schedstat", S_IRUGO, proc_pid_schedstat), |