diff options
| author | Markus Metzger <markus.t.metzger@intel.com> | 2008-12-19 09:10:24 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2008-12-20 03:15:46 -0500 |
| commit | bf53de907dfdaac178c92d774aae7370d7b97d20 (patch) | |
| tree | 738a07a8b4b22f7bb8ec2029c9ea9c635db6c62a | |
| parent | 30cd324e9787ccc9a5ede59742d5409857550692 (diff) | |
x86, bts: add fork and exit handling
Impact: introduce new ptrace facility
Add arch_ptrace_untrace() function that is called when the tracer
detaches (either voluntarily or when the tracing task dies);
ptrace_disable() is only called on a voluntary detach.
Add ptrace_fork() and arch_ptrace_fork(). They are called when a
traced task is forked.
Clear DS and BTS related fields on fork.
Release DS resources and reclaim memory in ptrace_untrace(). This
releases resources already when the tracing task dies. We used to do
that when the traced task dies.
Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
| -rw-r--r-- | arch/x86/include/asm/ds.h | 9 | ||||
| -rw-r--r-- | arch/x86/include/asm/ptrace.h | 7 | ||||
| -rw-r--r-- | arch/x86/kernel/ds.c | 11 | ||||
| -rw-r--r-- | arch/x86/kernel/process_32.c | 20 | ||||
| -rw-r--r-- | arch/x86/kernel/process_64.c | 20 | ||||
| -rw-r--r-- | arch/x86/kernel/ptrace.c | 50 | ||||
| -rw-r--r-- | include/linux/ptrace.h | 22 | ||||
| -rw-r--r-- | kernel/fork.c | 2 | ||||
| -rw-r--r-- | kernel/ptrace.c | 12 |
9 files changed, 121 insertions, 32 deletions
diff --git a/arch/x86/include/asm/ds.h b/arch/x86/include/asm/ds.h index ee0ea3a96c11..a8f672ba100c 100644 --- a/arch/x86/include/asm/ds.h +++ b/arch/x86/include/asm/ds.h | |||
| @@ -252,12 +252,21 @@ extern void __cpuinit ds_init_intel(struct cpuinfo_x86 *); | |||
| 252 | */ | 252 | */ |
| 253 | extern void ds_switch_to(struct task_struct *prev, struct task_struct *next); | 253 | extern void ds_switch_to(struct task_struct *prev, struct task_struct *next); |
| 254 | 254 | ||
| 255 | /* | ||
| 256 | * Task clone/init and cleanup work | ||
| 257 | */ | ||
| 258 | extern void ds_copy_thread(struct task_struct *tsk, struct task_struct *father); | ||
| 259 | extern void ds_exit_thread(struct task_struct *tsk); | ||
| 260 | |||
| 255 | #else /* CONFIG_X86_DS */ | 261 | #else /* CONFIG_X86_DS */ |
| 256 | 262 | ||
| 257 | struct cpuinfo_x86; | 263 | struct cpuinfo_x86; |
| 258 | static inline void __cpuinit ds_init_intel(struct cpuinfo_x86 *ignored) {} | 264 | static inline void __cpuinit ds_init_intel(struct cpuinfo_x86 *ignored) {} |
| 259 | static inline void ds_switch_to(struct task_struct *prev, | 265 | static inline void ds_switch_to(struct task_struct *prev, |
| 260 | struct task_struct *next) {} | 266 | struct task_struct *next) {} |
| 267 | static inline void ds_copy_thread(struct task_struct *tsk, | ||
| 268 | struct task_struct *father) {} | ||
| 269 | static inline void ds_exit_thread(struct task_struct *tsk) {} | ||
| 261 | 270 | ||
| 262 | #endif /* CONFIG_X86_DS */ | 271 | #endif /* CONFIG_X86_DS */ |
| 263 | #endif /* _ASM_X86_DS_H */ | 272 | #endif /* _ASM_X86_DS_H */ |
diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h index fbf744215911..6d34d954c228 100644 --- a/arch/x86/include/asm/ptrace.h +++ b/arch/x86/include/asm/ptrace.h | |||
| @@ -235,6 +235,13 @@ extern int do_get_thread_area(struct task_struct *p, int idx, | |||
| 235 | extern int do_set_thread_area(struct task_struct *p, int idx, | 235 | extern int do_set_thread_area(struct task_struct *p, int idx, |
| 236 | struct user_desc __user *info, int can_allocate); | 236 | struct user_desc __user *info, int can_allocate); |
| 237 | 237 | ||
| 238 | extern void x86_ptrace_untrace(struct task_struct *); | ||
| 239 | extern void x86_ptrace_fork(struct task_struct *child, | ||
| 240 | unsigned long clone_flags); | ||
| 241 | |||
| 242 | #define arch_ptrace_untrace(tsk) x86_ptrace_untrace(tsk) | ||
| 243 | #define arch_ptrace_fork(child, flags) x86_ptrace_fork(child, flags) | ||
| 244 | |||
| 238 | #endif /* __KERNEL__ */ | 245 | #endif /* __KERNEL__ */ |
| 239 | 246 | ||
| 240 | #endif /* !__ASSEMBLY__ */ | 247 | #endif /* !__ASSEMBLY__ */ |
diff --git a/arch/x86/kernel/ds.c b/arch/x86/kernel/ds.c index 98d271e60e08..da91701a2348 100644 --- a/arch/x86/kernel/ds.c +++ b/arch/x86/kernel/ds.c | |||
| @@ -1017,3 +1017,14 @@ void ds_switch_to(struct task_struct *prev, struct task_struct *next) | |||
| 1017 | 1017 | ||
| 1018 | update_debugctlmsr(next->thread.debugctlmsr); | 1018 | update_debugctlmsr(next->thread.debugctlmsr); |
| 1019 | } | 1019 | } |
| 1020 | |||
| 1021 | void ds_copy_thread(struct task_struct *tsk, struct task_struct *father) | ||
| 1022 | { | ||
| 1023 | clear_tsk_thread_flag(tsk, TIF_DS_AREA_MSR); | ||
| 1024 | tsk->thread.ds_ctx = NULL; | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | void ds_exit_thread(struct task_struct *tsk) | ||
| 1028 | { | ||
| 1029 | WARN_ON(tsk->thread.ds_ctx); | ||
| 1030 | } | ||
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index 605eff9a8ac0..3ba155d24884 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c | |||
| @@ -60,6 +60,7 @@ | |||
| 60 | #include <asm/idle.h> | 60 | #include <asm/idle.h> |
| 61 | #include <asm/syscalls.h> | 61 | #include <asm/syscalls.h> |
| 62 | #include <asm/smp.h> | 62 | #include <asm/smp.h> |
| 63 | #include <asm/ds.h> | ||
| 63 | 64 | ||
| 64 | asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); | 65 | asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); |
| 65 | 66 | ||
| @@ -251,17 +252,8 @@ void exit_thread(void) | |||
| 251 | tss->x86_tss.io_bitmap_base = INVALID_IO_BITMAP_OFFSET; | 252 | tss->x86_tss.io_bitmap_base = INVALID_IO_BITMAP_OFFSET; |
| 252 | put_cpu(); | 253 | put_cpu(); |
| 253 | } | 254 | } |
| 254 | #ifdef CONFIG_X86_DS | 255 | |
| 255 | /* Free any BTS tracers that have not been properly released. */ | 256 | ds_exit_thread(current); |
| 256 | if (unlikely(current->bts)) { | ||
| 257 | ds_release_bts(current->bts); | ||
| 258 | current->bts = NULL; | ||
| 259 | |||
| 260 | kfree(current->bts_buffer); | ||
| 261 | current->bts_buffer = NULL; | ||
| 262 | current->bts_size = 0; | ||
| 263 | } | ||
| 264 | #endif /* CONFIG_X86_DS */ | ||
| 265 | } | 257 | } |
| 266 | 258 | ||
| 267 | void flush_thread(void) | 259 | void flush_thread(void) |
| @@ -343,6 +335,12 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, | |||
| 343 | kfree(p->thread.io_bitmap_ptr); | 335 | kfree(p->thread.io_bitmap_ptr); |
| 344 | p->thread.io_bitmap_max = 0; | 336 | p->thread.io_bitmap_max = 0; |
| 345 | } | 337 | } |
| 338 | |||
| 339 | ds_copy_thread(p, current); | ||
| 340 | |||
| 341 | clear_tsk_thread_flag(p, TIF_DEBUGCTLMSR); | ||
| 342 | p->thread.debugctlmsr = 0; | ||
| 343 | |||
| 346 | return err; | 344 | return err; |
| 347 | } | 345 | } |
| 348 | 346 | ||
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 1cfd2a4bf853..416fb9282f4f 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c | |||
| @@ -53,6 +53,7 @@ | |||
| 53 | #include <asm/ia32.h> | 53 | #include <asm/ia32.h> |
| 54 | #include <asm/idle.h> | 54 | #include <asm/idle.h> |
| 55 | #include <asm/syscalls.h> | 55 | #include <asm/syscalls.h> |
| 56 | #include <asm/ds.h> | ||
| 56 | 57 | ||
| 57 | asmlinkage extern void ret_from_fork(void); | 58 | asmlinkage extern void ret_from_fork(void); |
| 58 | 59 | ||
| @@ -236,17 +237,8 @@ void exit_thread(void) | |||
| 236 | t->io_bitmap_max = 0; | 237 | t->io_bitmap_max = 0; |
| 237 | put_cpu(); | 238 | put_cpu(); |
| 238 | } | 239 | } |
| 239 | #ifdef CONFIG_X86_DS | 240 | |
| 240 | /* Free any BTS tracers that have not been properly released. */ | 241 | ds_exit_thread(current); |
| 241 | if (unlikely(current->bts)) { | ||
| 242 | ds_release_bts(current->bts); | ||
| 243 | current->bts = NULL; | ||
| 244 | |||
| 245 | kfree(current->bts_buffer); | ||
| 246 | current->bts_buffer = NULL; | ||
| 247 | current->bts_size = 0; | ||
| 248 | } | ||
| 249 | #endif /* CONFIG_X86_DS */ | ||
| 250 | } | 242 | } |
| 251 | 243 | ||
| 252 | void flush_thread(void) | 244 | void flush_thread(void) |
| @@ -376,6 +368,12 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, | |||
| 376 | if (err) | 368 | if (err) |
| 377 | goto out; | 369 | goto out; |
| 378 | } | 370 | } |
| 371 | |||
| 372 | ds_copy_thread(p, me); | ||
| 373 | |||
| 374 | clear_tsk_thread_flag(p, TIF_DEBUGCTLMSR); | ||
| 375 | p->thread.debugctlmsr = 0; | ||
| 376 | |||
| 379 | err = 0; | 377 | err = 0; |
| 380 | out: | 378 | out: |
| 381 | if (err && p->thread.io_bitmap_ptr) { | 379 | if (err && p->thread.io_bitmap_ptr) { |
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 45e9855da2d2..6ad2bb607650 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
| @@ -769,8 +769,47 @@ static int ptrace_bts_size(struct task_struct *child) | |||
| 769 | 769 | ||
| 770 | return (trace->ds.top - trace->ds.begin) / trace->ds.size; | 770 | return (trace->ds.top - trace->ds.begin) / trace->ds.size; |
| 771 | } | 771 | } |
| 772 | |||
| 773 | static void ptrace_bts_fork(struct task_struct *tsk) | ||
| 774 | { | ||
| 775 | tsk->bts = NULL; | ||
| 776 | tsk->bts_buffer = NULL; | ||
| 777 | tsk->bts_size = 0; | ||
| 778 | tsk->thread.bts_ovfl_signal = 0; | ||
| 779 | } | ||
| 780 | |||
| 781 | static void ptrace_bts_untrace(struct task_struct *child) | ||
| 782 | { | ||
| 783 | if (unlikely(child->bts)) { | ||
| 784 | ds_release_bts(child->bts); | ||
| 785 | child->bts = NULL; | ||
| 786 | |||
| 787 | kfree(child->bts_buffer); | ||
| 788 | child->bts_buffer = NULL; | ||
| 789 | child->bts_size = 0; | ||
| 790 | } | ||
| 791 | } | ||
| 792 | |||
| 793 | static void ptrace_bts_detach(struct task_struct *child) | ||
| 794 | { | ||
| 795 | ptrace_bts_untrace(child); | ||
| 796 | } | ||
| 797 | #else | ||
| 798 | static inline void ptrace_bts_fork(struct task_struct *tsk) {} | ||
| 799 | static inline void ptrace_bts_detach(struct task_struct *child) {} | ||
| 800 | static inline void ptrace_bts_untrace(struct task_struct *child) {} | ||
| 772 | #endif /* CONFIG_X86_PTRACE_BTS */ | 801 | #endif /* CONFIG_X86_PTRACE_BTS */ |
| 773 | 802 | ||
| 803 | void x86_ptrace_fork(struct task_struct *child, unsigned long clone_flags) | ||
| 804 | { | ||
| 805 | ptrace_bts_fork(child); | ||
| 806 | } | ||
| 807 | |||
| 808 | void x86_ptrace_untrace(struct task_struct *child) | ||
| 809 | { | ||
| 810 | ptrace_bts_untrace(child); | ||
| 811 | } | ||
| 812 | |||
| 774 | /* | 813 | /* |
| 775 | * Called by kernel/ptrace.c when detaching.. | 814 | * Called by kernel/ptrace.c when detaching.. |
| 776 | * | 815 | * |
| @@ -782,16 +821,7 @@ void ptrace_disable(struct task_struct *child) | |||
| 782 | #ifdef TIF_SYSCALL_EMU | 821 | #ifdef TIF_SYSCALL_EMU |
| 783 | clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); | 822 | clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); |
| 784 | #endif | 823 | #endif |
| 785 | #ifdef CONFIG_X86_PTRACE_BTS | 824 | ptrace_bts_detach(child); |
| 786 | if (child->bts) { | ||
| 787 | ds_release_bts(child->bts); | ||
| 788 | child->bts = NULL; | ||
| 789 | |||
| 790 | kfree(child->bts_buffer); | ||
| 791 | child->bts_buffer = NULL; | ||
| 792 | child->bts_size = 0; | ||
| 793 | } | ||
| 794 | #endif /* CONFIG_X86_PTRACE_BTS */ | ||
| 795 | } | 825 | } |
| 796 | 826 | ||
| 797 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION | 827 | #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION |
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 22641d5d45df..98b93ca4db06 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h | |||
| @@ -94,6 +94,7 @@ extern void ptrace_notify(int exit_code); | |||
| 94 | extern void __ptrace_link(struct task_struct *child, | 94 | extern void __ptrace_link(struct task_struct *child, |
| 95 | struct task_struct *new_parent); | 95 | struct task_struct *new_parent); |
| 96 | extern void __ptrace_unlink(struct task_struct *child); | 96 | extern void __ptrace_unlink(struct task_struct *child); |
| 97 | extern void ptrace_fork(struct task_struct *task, unsigned long clone_flags); | ||
| 97 | #define PTRACE_MODE_READ 1 | 98 | #define PTRACE_MODE_READ 1 |
| 98 | #define PTRACE_MODE_ATTACH 2 | 99 | #define PTRACE_MODE_ATTACH 2 |
| 99 | /* Returns 0 on success, -errno on denial. */ | 100 | /* Returns 0 on success, -errno on denial. */ |
| @@ -313,6 +314,27 @@ static inline void user_enable_block_step(struct task_struct *task) | |||
| 313 | #define arch_ptrace_stop(code, info) do { } while (0) | 314 | #define arch_ptrace_stop(code, info) do { } while (0) |
| 314 | #endif | 315 | #endif |
| 315 | 316 | ||
| 317 | #ifndef arch_ptrace_untrace | ||
| 318 | /* | ||
| 319 | * Do machine-specific work before untracing child. | ||
| 320 | * | ||
| 321 | * This is called for a normal detach as well as from ptrace_exit() | ||
| 322 | * when the tracing task dies. | ||
| 323 | * | ||
| 324 | * Called with write_lock(&tasklist_lock) held. | ||
| 325 | */ | ||
| 326 | #define arch_ptrace_untrace(task) do { } while (0) | ||
| 327 | #endif | ||
| 328 | |||
| 329 | #ifndef arch_ptrace_fork | ||
| 330 | /* | ||
| 331 | * Do machine-specific work to initialize a new task. | ||
| 332 | * | ||
| 333 | * This is called from copy_process(). | ||
| 334 | */ | ||
| 335 | #define arch_ptrace_fork(child, clone_flags) do { } while (0) | ||
| 336 | #endif | ||
| 337 | |||
| 316 | extern int task_current_syscall(struct task_struct *target, long *callno, | 338 | extern int task_current_syscall(struct task_struct *target, long *callno, |
| 317 | unsigned long args[6], unsigned int maxargs, | 339 | unsigned long args[6], unsigned int maxargs, |
| 318 | unsigned long *sp, unsigned long *pc); | 340 | unsigned long *sp, unsigned long *pc); |
diff --git a/kernel/fork.c b/kernel/fork.c index 7b93da72d4a2..65ce60adc8e8 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
| @@ -1096,6 +1096,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
| 1096 | #ifdef CONFIG_DEBUG_MUTEXES | 1096 | #ifdef CONFIG_DEBUG_MUTEXES |
| 1097 | p->blocked_on = NULL; /* not blocked yet */ | 1097 | p->blocked_on = NULL; /* not blocked yet */ |
| 1098 | #endif | 1098 | #endif |
| 1099 | if (unlikely(ptrace_reparented(current))) | ||
| 1100 | ptrace_fork(p, clone_flags); | ||
| 1099 | 1101 | ||
| 1100 | /* Perform scheduler related setup. Assign this task to a CPU. */ | 1102 | /* Perform scheduler related setup. Assign this task to a CPU. */ |
| 1101 | sched_fork(p, clone_flags); | 1103 | sched_fork(p, clone_flags); |
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 4c8bcd7dd8e0..100a71cfdaba 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
| @@ -25,6 +25,17 @@ | |||
| 25 | #include <asm/pgtable.h> | 25 | #include <asm/pgtable.h> |
| 26 | #include <asm/uaccess.h> | 26 | #include <asm/uaccess.h> |
| 27 | 27 | ||
| 28 | |||
| 29 | /* | ||
| 30 | * Initialize a new task whose father had been ptraced. | ||
| 31 | * | ||
| 32 | * Called from copy_process(). | ||
| 33 | */ | ||
| 34 | void ptrace_fork(struct task_struct *child, unsigned long clone_flags) | ||
| 35 | { | ||
| 36 | arch_ptrace_fork(child, clone_flags); | ||
| 37 | } | ||
| 38 | |||
| 28 | /* | 39 | /* |
| 29 | * ptrace a task: make the debugger its new parent and | 40 | * ptrace a task: make the debugger its new parent and |
| 30 | * move it to the ptrace list. | 41 | * move it to the ptrace list. |
| @@ -72,6 +83,7 @@ void __ptrace_unlink(struct task_struct *child) | |||
| 72 | child->parent = child->real_parent; | 83 | child->parent = child->real_parent; |
| 73 | list_del_init(&child->ptrace_entry); | 84 | list_del_init(&child->ptrace_entry); |
| 74 | 85 | ||
| 86 | arch_ptrace_untrace(child); | ||
| 75 | if (task_is_traced(child)) | 87 | if (task_is_traced(child)) |
| 76 | ptrace_untrace(child); | 88 | ptrace_untrace(child); |
| 77 | } | 89 | } |
