diff options
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 170 |
1 files changed, 117 insertions, 53 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 99bbaa3e5b0d..2df115790cd9 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 | /* |
@@ -37,35 +38,33 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) | |||
37 | child->parent = new_parent; | 38 | child->parent = new_parent; |
38 | } | 39 | } |
39 | 40 | ||
40 | /* | 41 | /** |
41 | * Turn a tracing stop into a normal stop now, since with no tracer there | 42 | * __ptrace_unlink - unlink ptracee and restore its execution state |
42 | * would be no way to wake it up with SIGCONT or SIGKILL. If there was a | 43 | * @child: ptracee to be unlinked |
43 | * signal sent that would resume the child, but didn't because it was in | ||
44 | * TASK_TRACED, resume it now. | ||
45 | * Requires that irqs be disabled. | ||
46 | */ | ||
47 | static void ptrace_untrace(struct task_struct *child) | ||
48 | { | ||
49 | spin_lock(&child->sighand->siglock); | ||
50 | if (task_is_traced(child)) { | ||
51 | /* | ||
52 | * If the group stop is completed or in progress, | ||
53 | * this thread was already counted as stopped. | ||
54 | */ | ||
55 | if (child->signal->flags & SIGNAL_STOP_STOPPED || | ||
56 | child->signal->group_stop_count) | ||
57 | __set_task_state(child, TASK_STOPPED); | ||
58 | else | ||
59 | signal_wake_up(child, 1); | ||
60 | } | ||
61 | spin_unlock(&child->sighand->siglock); | ||
62 | } | ||
63 | |||
64 | /* | ||
65 | * unptrace a task: move it back to its original parent and | ||
66 | * remove it from the ptrace list. | ||
67 | * | 44 | * |
68 | * Must be called with the tasklist lock write-held. | 45 | * Remove @child from the ptrace list, move it back to the original parent, |
46 | * and restore the execution state so that it conforms to the group stop | ||
47 | * state. | ||
48 | * | ||
49 | * Unlinking can happen via two paths - explicit PTRACE_DETACH or ptracer | ||
50 | * exiting. For PTRACE_DETACH, unless the ptracee has been killed between | ||
51 | * ptrace_check_attach() and here, it's guaranteed to be in TASK_TRACED. | ||
52 | * If the ptracer is exiting, the ptracee can be in any state. | ||
53 | * | ||
54 | * After detach, the ptracee should be in a state which conforms to the | ||
55 | * group stop. If the group is stopped or in the process of stopping, the | ||
56 | * ptracee should be put into TASK_STOPPED; otherwise, it should be woken | ||
57 | * up from TASK_TRACED. | ||
58 | * | ||
59 | * If the ptracee is in TASK_TRACED and needs to be moved to TASK_STOPPED, | ||
60 | * it goes through TRACED -> RUNNING -> STOPPED transition which is similar | ||
61 | * to but in the opposite direction of what happens while attaching to a | ||
62 | * stopped task. However, in this direction, the intermediate RUNNING | ||
63 | * state is not hidden even from the current ptracer and if it immediately | ||
64 | * re-attaches and performs a WNOHANG wait(2), it may fail. | ||
65 | * | ||
66 | * CONTEXT: | ||
67 | * write_lock_irq(tasklist_lock) | ||
69 | */ | 68 | */ |
70 | void __ptrace_unlink(struct task_struct *child) | 69 | void __ptrace_unlink(struct task_struct *child) |
71 | { | 70 | { |
@@ -75,8 +74,27 @@ void __ptrace_unlink(struct task_struct *child) | |||
75 | child->parent = child->real_parent; | 74 | child->parent = child->real_parent; |
76 | list_del_init(&child->ptrace_entry); | 75 | list_del_init(&child->ptrace_entry); |
77 | 76 | ||
78 | if (task_is_traced(child)) | 77 | spin_lock(&child->sighand->siglock); |
79 | ptrace_untrace(child); | 78 | |
79 | /* | ||
80 | * Reinstate GROUP_STOP_PENDING if group stop is in effect and | ||
81 | * @child isn't dead. | ||
82 | */ | ||
83 | if (!(child->flags & PF_EXITING) && | ||
84 | (child->signal->flags & SIGNAL_STOP_STOPPED || | ||
85 | child->signal->group_stop_count)) | ||
86 | child->group_stop |= GROUP_STOP_PENDING; | ||
87 | |||
88 | /* | ||
89 | * If transition to TASK_STOPPED is pending or in TASK_TRACED, kick | ||
90 | * @child in the butt. Note that @resume should be used iff @child | ||
91 | * is in TASK_TRACED; otherwise, we might unduly disrupt | ||
92 | * TASK_KILLABLE sleeps. | ||
93 | */ | ||
94 | if (child->group_stop & GROUP_STOP_PENDING || task_is_traced(child)) | ||
95 | signal_wake_up(child, task_is_traced(child)); | ||
96 | |||
97 | spin_unlock(&child->sighand->siglock); | ||
80 | } | 98 | } |
81 | 99 | ||
82 | /* | 100 | /* |
@@ -95,16 +113,14 @@ int ptrace_check_attach(struct task_struct *child, int kill) | |||
95 | */ | 113 | */ |
96 | read_lock(&tasklist_lock); | 114 | read_lock(&tasklist_lock); |
97 | if ((child->ptrace & PT_PTRACED) && child->parent == current) { | 115 | if ((child->ptrace & PT_PTRACED) && child->parent == current) { |
98 | ret = 0; | ||
99 | /* | 116 | /* |
100 | * child->sighand can't be NULL, release_task() | 117 | * child->sighand can't be NULL, release_task() |
101 | * does ptrace_unlink() before __exit_signal(). | 118 | * does ptrace_unlink() before __exit_signal(). |
102 | */ | 119 | */ |
103 | spin_lock_irq(&child->sighand->siglock); | 120 | spin_lock_irq(&child->sighand->siglock); |
104 | if (task_is_stopped(child)) | 121 | WARN_ON_ONCE(task_is_stopped(child)); |
105 | child->state = TASK_TRACED; | 122 | if (task_is_traced(child) || kill) |
106 | else if (!task_is_traced(child) && !kill) | 123 | ret = 0; |
107 | ret = -ESRCH; | ||
108 | spin_unlock_irq(&child->sighand->siglock); | 124 | spin_unlock_irq(&child->sighand->siglock); |
109 | } | 125 | } |
110 | read_unlock(&tasklist_lock); | 126 | read_unlock(&tasklist_lock); |
@@ -134,21 +150,24 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode) | |||
134 | return 0; | 150 | return 0; |
135 | rcu_read_lock(); | 151 | rcu_read_lock(); |
136 | tcred = __task_cred(task); | 152 | tcred = __task_cred(task); |
137 | if ((cred->uid != tcred->euid || | 153 | if (cred->user->user_ns == tcred->user->user_ns && |
138 | cred->uid != tcred->suid || | 154 | (cred->uid == tcred->euid && |
139 | cred->uid != tcred->uid || | 155 | cred->uid == tcred->suid && |
140 | cred->gid != tcred->egid || | 156 | cred->uid == tcred->uid && |
141 | cred->gid != tcred->sgid || | 157 | cred->gid == tcred->egid && |
142 | cred->gid != tcred->gid) && | 158 | cred->gid == tcred->sgid && |
143 | !capable(CAP_SYS_PTRACE)) { | 159 | cred->gid == tcred->gid)) |
144 | rcu_read_unlock(); | 160 | goto ok; |
145 | return -EPERM; | 161 | if (ns_capable(tcred->user->user_ns, CAP_SYS_PTRACE)) |
146 | } | 162 | goto ok; |
163 | rcu_read_unlock(); | ||
164 | return -EPERM; | ||
165 | ok: | ||
147 | rcu_read_unlock(); | 166 | rcu_read_unlock(); |
148 | smp_rmb(); | 167 | smp_rmb(); |
149 | if (task->mm) | 168 | if (task->mm) |
150 | dumpable = get_dumpable(task->mm); | 169 | dumpable = get_dumpable(task->mm); |
151 | if (!dumpable && !capable(CAP_SYS_PTRACE)) | 170 | if (!dumpable && !task_ns_capable(task, CAP_SYS_PTRACE)) |
152 | return -EPERM; | 171 | return -EPERM; |
153 | 172 | ||
154 | return security_ptrace_access_check(task, mode); | 173 | return security_ptrace_access_check(task, mode); |
@@ -163,8 +182,9 @@ bool ptrace_may_access(struct task_struct *task, unsigned int mode) | |||
163 | return !err; | 182 | return !err; |
164 | } | 183 | } |
165 | 184 | ||
166 | int ptrace_attach(struct task_struct *task) | 185 | static int ptrace_attach(struct task_struct *task) |
167 | { | 186 | { |
187 | bool wait_trap = false; | ||
168 | int retval; | 188 | int retval; |
169 | 189 | ||
170 | audit_ptrace(task); | 190 | audit_ptrace(task); |
@@ -198,18 +218,48 @@ int ptrace_attach(struct task_struct *task) | |||
198 | goto unlock_tasklist; | 218 | goto unlock_tasklist; |
199 | 219 | ||
200 | task->ptrace = PT_PTRACED; | 220 | task->ptrace = PT_PTRACED; |
201 | if (capable(CAP_SYS_PTRACE)) | 221 | if (task_ns_capable(task, CAP_SYS_PTRACE)) |
202 | task->ptrace |= PT_PTRACE_CAP; | 222 | task->ptrace |= PT_PTRACE_CAP; |
203 | 223 | ||
204 | __ptrace_link(task, current); | 224 | __ptrace_link(task, current); |
205 | send_sig_info(SIGSTOP, SEND_SIG_FORCED, task); | 225 | send_sig_info(SIGSTOP, SEND_SIG_FORCED, task); |
206 | 226 | ||
227 | spin_lock(&task->sighand->siglock); | ||
228 | |||
229 | /* | ||
230 | * If the task is already STOPPED, set GROUP_STOP_PENDING and | ||
231 | * TRAPPING, and kick it so that it transits to TRACED. TRAPPING | ||
232 | * will be cleared if the child completes the transition or any | ||
233 | * event which clears the group stop states happens. We'll wait | ||
234 | * for the transition to complete before returning from this | ||
235 | * function. | ||
236 | * | ||
237 | * This hides STOPPED -> RUNNING -> TRACED transition from the | ||
238 | * attaching thread but a different thread in the same group can | ||
239 | * still observe the transient RUNNING state. IOW, if another | ||
240 | * thread's WNOHANG wait(2) on the stopped tracee races against | ||
241 | * ATTACH, the wait(2) may fail due to the transient RUNNING. | ||
242 | * | ||
243 | * The following task_is_stopped() test is safe as both transitions | ||
244 | * in and out of STOPPED are protected by siglock. | ||
245 | */ | ||
246 | if (task_is_stopped(task)) { | ||
247 | task->group_stop |= GROUP_STOP_PENDING | GROUP_STOP_TRAPPING; | ||
248 | signal_wake_up(task, 1); | ||
249 | wait_trap = true; | ||
250 | } | ||
251 | |||
252 | spin_unlock(&task->sighand->siglock); | ||
253 | |||
207 | retval = 0; | 254 | retval = 0; |
208 | unlock_tasklist: | 255 | unlock_tasklist: |
209 | write_unlock_irq(&tasklist_lock); | 256 | write_unlock_irq(&tasklist_lock); |
210 | unlock_creds: | 257 | unlock_creds: |
211 | mutex_unlock(&task->signal->cred_guard_mutex); | 258 | mutex_unlock(&task->signal->cred_guard_mutex); |
212 | out: | 259 | out: |
260 | if (wait_trap) | ||
261 | wait_event(current->signal->wait_chldexit, | ||
262 | !(task->group_stop & GROUP_STOP_TRAPPING)); | ||
213 | return retval; | 263 | return retval; |
214 | } | 264 | } |
215 | 265 | ||
@@ -219,7 +269,7 @@ out: | |||
219 | * Performs checks and sets PT_PTRACED. | 269 | * Performs checks and sets PT_PTRACED. |
220 | * Should be used by all ptrace implementations for PTRACE_TRACEME. | 270 | * Should be used by all ptrace implementations for PTRACE_TRACEME. |
221 | */ | 271 | */ |
222 | int ptrace_traceme(void) | 272 | static int ptrace_traceme(void) |
223 | { | 273 | { |
224 | int ret = -EPERM; | 274 | int ret = -EPERM; |
225 | 275 | ||
@@ -293,7 +343,7 @@ static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p) | |||
293 | return false; | 343 | return false; |
294 | } | 344 | } |
295 | 345 | ||
296 | int ptrace_detach(struct task_struct *child, unsigned int data) | 346 | static int ptrace_detach(struct task_struct *child, unsigned int data) |
297 | { | 347 | { |
298 | bool dead = false; | 348 | bool dead = false; |
299 | 349 | ||
@@ -312,8 +362,6 @@ int ptrace_detach(struct task_struct *child, unsigned int data) | |||
312 | if (child->ptrace) { | 362 | if (child->ptrace) { |
313 | child->exit_code = data; | 363 | child->exit_code = data; |
314 | dead = __ptrace_detach(current, child); | 364 | dead = __ptrace_detach(current, child); |
315 | if (!child->exit_state) | ||
316 | wake_up_process(child); | ||
317 | } | 365 | } |
318 | write_unlock_irq(&tasklist_lock); | 366 | write_unlock_irq(&tasklist_lock); |
319 | 367 | ||
@@ -514,7 +562,7 @@ static int ptrace_resume(struct task_struct *child, long request, | |||
514 | } | 562 | } |
515 | 563 | ||
516 | child->exit_code = data; | 564 | child->exit_code = data; |
517 | wake_up_process(child); | 565 | wake_up_state(child, __TASK_TRACED); |
518 | 566 | ||
519 | return 0; | 567 | return 0; |
520 | } | 568 | } |
@@ -876,3 +924,19 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid, | |||
876 | return ret; | 924 | return ret; |
877 | } | 925 | } |
878 | #endif /* CONFIG_COMPAT */ | 926 | #endif /* CONFIG_COMPAT */ |
927 | |||
928 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
929 | int ptrace_get_breakpoints(struct task_struct *tsk) | ||
930 | { | ||
931 | if (atomic_inc_not_zero(&tsk->ptrace_bp_refcnt)) | ||
932 | return 0; | ||
933 | |||
934 | return -1; | ||
935 | } | ||
936 | |||
937 | void ptrace_put_breakpoints(struct task_struct *tsk) | ||
938 | { | ||
939 | if (atomic_dec_and_test(&tsk->ptrace_bp_refcnt)) | ||
940 | flush_ptrace_hw_breakpoint(tsk); | ||
941 | } | ||
942 | #endif /* CONFIG_HAVE_HW_BREAKPOINT */ | ||