diff options
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 52 |
1 files changed, 32 insertions, 20 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 6c19e94fd0a5..8392a9da6450 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -33,13 +33,9 @@ | |||
33 | */ | 33 | */ |
34 | void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) | 34 | void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) |
35 | { | 35 | { |
36 | BUG_ON(!list_empty(&child->ptrace_list)); | 36 | BUG_ON(!list_empty(&child->ptrace_entry)); |
37 | if (child->parent == new_parent) | 37 | list_add(&child->ptrace_entry, &new_parent->ptraced); |
38 | return; | ||
39 | list_add(&child->ptrace_list, &child->parent->ptrace_children); | ||
40 | remove_parent(child); | ||
41 | child->parent = new_parent; | 38 | child->parent = new_parent; |
42 | add_parent(child); | ||
43 | } | 39 | } |
44 | 40 | ||
45 | /* | 41 | /* |
@@ -73,12 +69,8 @@ void __ptrace_unlink(struct task_struct *child) | |||
73 | BUG_ON(!child->ptrace); | 69 | BUG_ON(!child->ptrace); |
74 | 70 | ||
75 | child->ptrace = 0; | 71 | child->ptrace = 0; |
76 | if (ptrace_reparented(child)) { | 72 | child->parent = child->real_parent; |
77 | list_del_init(&child->ptrace_list); | 73 | list_del_init(&child->ptrace_entry); |
78 | remove_parent(child); | ||
79 | child->parent = child->real_parent; | ||
80 | add_parent(child); | ||
81 | } | ||
82 | 74 | ||
83 | if (task_is_traced(child)) | 75 | if (task_is_traced(child)) |
84 | ptrace_untrace(child); | 76 | ptrace_untrace(child); |
@@ -121,7 +113,7 @@ int ptrace_check_attach(struct task_struct *child, int kill) | |||
121 | return ret; | 113 | return ret; |
122 | } | 114 | } |
123 | 115 | ||
124 | int __ptrace_may_attach(struct task_struct *task) | 116 | int __ptrace_may_access(struct task_struct *task, unsigned int mode) |
125 | { | 117 | { |
126 | /* May we inspect the given task? | 118 | /* May we inspect the given task? |
127 | * This check is used both for attaching with ptrace | 119 | * This check is used both for attaching with ptrace |
@@ -148,16 +140,16 @@ int __ptrace_may_attach(struct task_struct *task) | |||
148 | if (!dumpable && !capable(CAP_SYS_PTRACE)) | 140 | if (!dumpable && !capable(CAP_SYS_PTRACE)) |
149 | return -EPERM; | 141 | return -EPERM; |
150 | 142 | ||
151 | return security_ptrace(current, task); | 143 | return security_ptrace(current, task, mode); |
152 | } | 144 | } |
153 | 145 | ||
154 | int ptrace_may_attach(struct task_struct *task) | 146 | bool ptrace_may_access(struct task_struct *task, unsigned int mode) |
155 | { | 147 | { |
156 | int err; | 148 | int err; |
157 | task_lock(task); | 149 | task_lock(task); |
158 | err = __ptrace_may_attach(task); | 150 | err = __ptrace_may_access(task, mode); |
159 | task_unlock(task); | 151 | task_unlock(task); |
160 | return !err; | 152 | return (!err ? true : false); |
161 | } | 153 | } |
162 | 154 | ||
163 | int ptrace_attach(struct task_struct *task) | 155 | int ptrace_attach(struct task_struct *task) |
@@ -195,7 +187,7 @@ repeat: | |||
195 | /* the same process cannot be attached many times */ | 187 | /* the same process cannot be attached many times */ |
196 | if (task->ptrace & PT_PTRACED) | 188 | if (task->ptrace & PT_PTRACED) |
197 | goto bad; | 189 | goto bad; |
198 | retval = __ptrace_may_attach(task); | 190 | retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH); |
199 | if (retval) | 191 | if (retval) |
200 | goto bad; | 192 | goto bad; |
201 | 193 | ||
@@ -492,14 +484,34 @@ int ptrace_traceme(void) | |||
492 | /* | 484 | /* |
493 | * Are we already being traced? | 485 | * Are we already being traced? |
494 | */ | 486 | */ |
487 | repeat: | ||
495 | task_lock(current); | 488 | task_lock(current); |
496 | if (!(current->ptrace & PT_PTRACED)) { | 489 | if (!(current->ptrace & PT_PTRACED)) { |
497 | ret = security_ptrace(current->parent, current); | 490 | /* |
491 | * See ptrace_attach() comments about the locking here. | ||
492 | */ | ||
493 | unsigned long flags; | ||
494 | if (!write_trylock_irqsave(&tasklist_lock, flags)) { | ||
495 | task_unlock(current); | ||
496 | do { | ||
497 | cpu_relax(); | ||
498 | } while (!write_can_lock(&tasklist_lock)); | ||
499 | goto repeat; | ||
500 | } | ||
501 | |||
502 | ret = security_ptrace(current->parent, current, | ||
503 | PTRACE_MODE_ATTACH); | ||
504 | |||
498 | /* | 505 | /* |
499 | * Set the ptrace bit in the process ptrace flags. | 506 | * Set the ptrace bit in the process ptrace flags. |
507 | * Then link us on our parent's ptraced list. | ||
500 | */ | 508 | */ |
501 | if (!ret) | 509 | if (!ret) { |
502 | current->ptrace |= PT_PTRACED; | 510 | current->ptrace |= PT_PTRACED; |
511 | __ptrace_link(current, current->real_parent); | ||
512 | } | ||
513 | |||
514 | write_unlock_irqrestore(&tasklist_lock, flags); | ||
503 | } | 515 | } |
504 | task_unlock(current); | 516 | task_unlock(current); |
505 | return ret; | 517 | return ret; |