Move utrace into task_struct
Subsystems (4)
PATCH 1/1 Move utrace into task_struct
This patch moves "struct utrace" into "struct task_struct" directly instead of being referenced by a pointer from task_struct. The main reason is utrace code leaving stale ->utrace pointer and freeing "struct utrace" itself. This manifests as crashes in __rcu_process_callbacks() and other nasties.
As side effect this makes attaching and detaching logic much simpler and streamlined. As you see ~200 lines are gone.
I can't oops 2-way Opteron box with this patch applied.
NOTE: patch is against (utrace patch against 2.6.21)
NOTE: "utrace_engine_cache" still leaks
NOTE: "XXX ptrace_report_death" leaks still happen
NOTE: most certainly some locking is still missed and I need to test
on a couple more boxes.
---
include/linux/sched.h | 7 include/linux/tracehook.h | 15 - include/linux/utrace.h | 39 ++- kernel/fork.c | 9 kernel/utrace.c | 457 ++++++++++++---------------------------------- 5 files changed, 157 insertions(+), 370 deletions(-)
include/linux/sched.h +2 -5
@@ -83,6 +83,7 @@ #include <linux/resource.h>#include <linux/timer.h> #include <linux/hrtimer.h> #include <linux/task_io_accounting.h> +#include <linux/utrace.h> #include <asm/processor.h>
@@ -940,11 +941,7 @@ #endifvoid *security;struct audit_context *audit_context;seccomp_t seccomp; - -#ifdef CONFIG_UTRACE -struct utrace *utrace; -unsigned long utrace_flags; -#endif +struct utrace utrace;/* Thread group tracking */ u32 parent_exec_id;
include/linux/tracehook.h +2 -13
@@ -310,16 +310,6 @@ utrace_regset_copyin_ignore(unsigned int*** ***/ - -/* -* Called in copy_process when setting up the copied task_struct, -* with tasklist_lock held for writing. -*/ -static inline void tracehook_init_task (struct task_struct *child) -{ -utrace_init_task (child); -} -/* * Called from release_task, no locks held. * After this, there should be no tracing entanglements.
@@ -327,8 +317,7 @@ static inline void tracehook_init_task(sstatic inline void tracehook_release_task (struct task_struct *p) {smp_mb (); -if (tsk_utrace_struct (p) !=NULL ) -utrace_release_task (p); +utrace_release_task (p); }/*
@@ -339,7 +328,7 @@ static inline void tracehook_release_tas*/ static inline int tracehook_check_released (struct task_struct *p) { -return unlikely (tsk_utrace_struct (p) !=NULL ); +return 0 ; }/*
include/linux/utrace.h +22 -17
@@ -50,11 +50,30 @@ #include <linux/sched.h>struct linux_binprm ;struct pt_regs ; -struct utrace ; +struct task_struct ;struct utrace_signal ;struct utrace_regset ;struct utrace_regset_view ; +#ifdef CONFIG_UTRACE +struct utrace { +unsigned long flags; +union { +struct { +struct task_struct *cloning; +struct utrace_signal *signal; + } live; +struct { +unsigned long flags; + } exit; + } u; +struct list_head engines; +spinlock_t lock; +}; +#else +struct utrace { +}; +#endif /* * Flags in task_struct.utrace_flags and utrace_attached_engine.flags.
@@ -493,19 +512,8 @@ void utrace_signal_handler_singlestep(st/* * <linux/tracehook.h> uses these accessors to avoid #ifdef CONFIG_UTRACE. */ -static inline unsigned long tsk_utrace_flags (struct task_struct *tsk) -{ -return tsk->utrace_flags; -} -static inline struct utrace *tsk_utrace_struct (struct task_struct *tsk) -{ -return tsk->utrace; -} -static inline void utrace_init_task (struct task_struct *child) -{ - child->utrace_flags =0 ; - child->utrace =NULL ; -} +#define tsk_utrace_flags (tsk) ((tsk)->utrace.flags) +#define tsk_utrace_struct (tsk) (&(tsk)->utrace)#else /* !CONFIG_UTRACE */
@@ -517,9 +525,6 @@ static struct utrace *tsk_utrace_struct( {return NULL ; } -static inline void utrace_init_task (struct task_struct *child) -{ -}/* * The calls to these should all be in if (0) and optimized out entirely.
kernel/fork.c +7 -2
@@ -1241,8 +1241,13 @@ #endifif (likely (p->pid)) {add_parent (p); -tracehook_init_task (p); - +#ifdef CONFIG_UTRACE + p->utrace.flags =0 ; + p->utrace.u.live.cloning =NULL ; + p->utrace.u.live.signal =NULL ; +INIT_LIST_HEAD (&p->utrace.engines); +spin_lock_init (&p->utrace.lock); +#endif if (thread_group_leader (p)) { p->signal->tty = current->signal->tty; p->signal->pgrp =process_group (current);
kernel/utrace.c +124 -333
766 lines not on this page · raw message
Quoted 14 lines · Alexey Dobriyan
include/linux/utrace.h +3
-1@@ -50,11 +50,30 @@ #include <linux/sched.h>struct linux_binprm ;struct pt_regs ; -struct utrace ; +struct task_struct ;struct utrace_signal ;struct utrace_regset ;struct utrace_regset_view ; +#ifdef CONFIG_UTRACE +struct utrace {
include/linux/utrace.h #include <linux/sched.h> · line 69
Quoted 10 lines
include/linux/utrace.h +10
-0+unsigned long flags; +union { +struct { +struct task_struct *cloning; +struct utrace_signal *signal; + } live; +struct { +unsigned long flags; + } exit; + } u;
You can have anonymous unions. (Though that won't work with static initializers - are there any?)
Jan
That's just a workaround for an actual bug that I need to fix. You wouldn't really want to do it that way.
Thanks,
Roland
Quoted 2 lines · Roland McGrath
That's just a workaround for an actual bug that I need to fix. You wouldn't really want to do it that way.
It's actuall a very nice and needed simplification. It gets rid of an object with subtle life time rules, which is always a good thing. The utrace code really needs more patches in the style of Alexey's as this kind of overengineering is what will cause hard to debug problems over the long run.
Regardless of future of "struct utrace utrace;" patch looks like there is another race: engine's flags and ops settings in utrace_detach() and acting on them in report_quiescent():
utrace_detach() report_quiescent()
--------------- ------------------
[utrace lock held] [utrace lock is not held]
engine->flags = UTRACE_EVENT(QUIESCE) |
UTRACE_ACTION_QUIESCE; if (engine->flags & UTRACE_EVENT(QUIESCE)) REPORT(report_quiesce);
rcu_assign_pointer(engine->ops, &dead_engine_ops);
At the moment of REPORT call engine's ops are still "live" ptrace ops which do not have ->report_quiesce callback. So, there will oops while calling function at NULL address. "Dead" ptrace engine ops do have dummy callback but it wasn't yet glued.
I hit this once with "struct utrace utrace;" patch applied, but this bug is also present in stock utrace, I'm sure.