aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2011-04-07 10:53:20 -0400
committerFrederic Weisbecker <fweisbec@gmail.com>2011-04-25 11:28:24 -0400
commitbf26c018490c2fce7fe9b629083b96ce0e6ad019 (patch)
treeff595f6268cb6fc675beb6accf6abb0701829443
parentf4929bd37208540c2c6f416e9035ff1938f2dbc6 (diff)
ptrace: Prepare to fix racy accesses on task breakpoints
When a task is traced and is in a stopped state, the tracer may execute a ptrace request to examine the tracee state and get its task struct. Right after, the tracee can be killed and thus its breakpoints released. This can happen concurrently when the tracer is in the middle of reading or modifying these breakpoints, leading to dereferencing a freed pointer. Hence, to prepare the fix, create a generic breakpoint reference holding API. When a reference on the breakpoints of a task is held, the breakpoints won't be released until the last reference is dropped. After that, no more ptrace request on the task's breakpoints can be serviced for the tracer. Reported-by: Oleg Nesterov <oleg@redhat.com> Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Will Deacon <will.deacon@arm.com> Cc: Prasad <prasad@linux.vnet.ibm.com> Cc: Paul Mundt <lethal@linux-sh.org> Cc: v2.6.33.. <stable@kernel.org> Link: http://lkml.kernel.org/r/1302284067-7860-2-git-send-email-fweisbec@gmail.com
-rw-r--r--include/linux/ptrace.h13
-rw-r--r--include/linux/sched.h3
-rw-r--r--kernel/exit.c2
-rw-r--r--kernel/ptrace.c17
4 files changed, 33 insertions, 2 deletions
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index a1147e5dd245..9178d5cc0b01 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -189,6 +189,10 @@ static inline void ptrace_init_task(struct task_struct *child, bool ptrace)
189 child->ptrace = current->ptrace; 189 child->ptrace = current->ptrace;
190 __ptrace_link(child, current->parent); 190 __ptrace_link(child, current->parent);
191 } 191 }
192
193#ifdef CONFIG_HAVE_HW_BREAKPOINT
194 atomic_set(&child->ptrace_bp_refcnt, 1);
195#endif
192} 196}
193 197
194/** 198/**
@@ -350,6 +354,13 @@ extern int task_current_syscall(struct task_struct *target, long *callno,
350 unsigned long args[6], unsigned int maxargs, 354 unsigned long args[6], unsigned int maxargs,
351 unsigned long *sp, unsigned long *pc); 355 unsigned long *sp, unsigned long *pc);
352 356
353#endif 357#ifdef CONFIG_HAVE_HW_BREAKPOINT
358extern int ptrace_get_breakpoints(struct task_struct *tsk);
359extern void ptrace_put_breakpoints(struct task_struct *tsk);
360#else
361static inline void ptrace_put_breakpoints(struct task_struct *tsk) { }
362#endif /* CONFIG_HAVE_HW_BREAKPOINT */
363
364#endif /* __KERNEL */
354 365
355#endif 366#endif
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 18d63cea2848..781abd137673 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1537,6 +1537,9 @@ struct task_struct {
1537 unsigned long memsw_nr_pages; /* uncharged mem+swap usage */ 1537 unsigned long memsw_nr_pages; /* uncharged mem+swap usage */
1538 } memcg_batch; 1538 } memcg_batch;
1539#endif 1539#endif
1540#ifdef CONFIG_HAVE_HW_BREAKPOINT
1541 atomic_t ptrace_bp_refcnt;
1542#endif
1540}; 1543};
1541 1544
1542/* Future-safe accessor for struct task_struct's cpus_allowed. */ 1545/* Future-safe accessor for struct task_struct's cpus_allowed. */
diff --git a/kernel/exit.c b/kernel/exit.c
index f5d2f63bae0b..8dd874181542 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -1016,7 +1016,7 @@ NORET_TYPE void do_exit(long code)
1016 /* 1016 /*
1017 * FIXME: do that only when needed, using sched_exit tracepoint 1017 * FIXME: do that only when needed, using sched_exit tracepoint
1018 */ 1018 */
1019 flush_ptrace_hw_breakpoint(tsk); 1019 ptrace_put_breakpoints(tsk);
1020 1020
1021 exit_notify(tsk, group_dead); 1021 exit_notify(tsk, group_dead);
1022#ifdef CONFIG_NUMA 1022#ifdef CONFIG_NUMA
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 0fc1eed28d27..dc7ab65f3b36 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -22,6 +22,7 @@
22#include <linux/syscalls.h> 22#include <linux/syscalls.h>
23#include <linux/uaccess.h> 23#include <linux/uaccess.h>
24#include <linux/regset.h> 24#include <linux/regset.h>
25#include <linux/hw_breakpoint.h>
25 26
26 27
27/* 28/*
@@ -879,3 +880,19 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
879 return ret; 880 return ret;
880} 881}
881#endif /* CONFIG_COMPAT */ 882#endif /* CONFIG_COMPAT */
883
884#ifdef CONFIG_HAVE_HW_BREAKPOINT
885int ptrace_get_breakpoints(struct task_struct *tsk)
886{
887 if (atomic_inc_not_zero(&tsk->ptrace_bp_refcnt))
888 return 0;
889
890 return -1;
891}
892
893void ptrace_put_breakpoints(struct task_struct *tsk)
894{
895 if (atomic_dec_and_test(&tsk->ptrace_bp_refcnt))
896 flush_ptrace_hw_breakpoint(tsk);
897}
898#endif /* CONFIG_HAVE_HW_BREAKPOINT */