xref: /openbmc/linux/kernel/ptrace.c (revision 9e48858f)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/kernel/ptrace.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * (C) Copyright 1999 Linus Torvalds
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Common interfaces for "ptrace()" which we do not want
71da177e4SLinus Torvalds  * to continually duplicate across every architecture.
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
10c59ede7bSRandy.Dunlap #include <linux/capability.h>
111da177e4SLinus Torvalds #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/sched.h>
131da177e4SLinus Torvalds #include <linux/errno.h>
141da177e4SLinus Torvalds #include <linux/mm.h>
151da177e4SLinus Torvalds #include <linux/highmem.h>
161da177e4SLinus Torvalds #include <linux/pagemap.h>
171da177e4SLinus Torvalds #include <linux/smp_lock.h>
181da177e4SLinus Torvalds #include <linux/ptrace.h>
191da177e4SLinus Torvalds #include <linux/security.h>
207ed20e1aSJesper Juhl #include <linux/signal.h>
21a5cb013dSAl Viro #include <linux/audit.h>
22b488893aSPavel Emelyanov #include <linux/pid_namespace.h>
23f17d30a8SAdrian Bunk #include <linux/syscalls.h>
243a709703SRoland McGrath #include <linux/uaccess.h>
251da177e4SLinus Torvalds 
26bf53de90SMarkus Metzger 
27bf53de90SMarkus Metzger /*
281da177e4SLinus Torvalds  * ptrace a task: make the debugger its new parent and
291da177e4SLinus Torvalds  * move it to the ptrace list.
301da177e4SLinus Torvalds  *
311da177e4SLinus Torvalds  * Must be called with the tasklist lock write-held.
321da177e4SLinus Torvalds  */
3336c8b586SIngo Molnar void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
341da177e4SLinus Torvalds {
35f470021aSRoland McGrath 	BUG_ON(!list_empty(&child->ptrace_entry));
36f470021aSRoland McGrath 	list_add(&child->ptrace_entry, &new_parent->ptraced);
371da177e4SLinus Torvalds 	child->parent = new_parent;
381da177e4SLinus Torvalds }
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds /*
411da177e4SLinus Torvalds  * Turn a tracing stop into a normal stop now, since with no tracer there
421da177e4SLinus Torvalds  * would be no way to wake it up with SIGCONT or SIGKILL.  If there was a
431da177e4SLinus Torvalds  * signal sent that would resume the child, but didn't because it was in
441da177e4SLinus Torvalds  * TASK_TRACED, resume it now.
451da177e4SLinus Torvalds  * Requires that irqs be disabled.
461da177e4SLinus Torvalds  */
47b747c8c1SAdrian Bunk static void ptrace_untrace(struct task_struct *child)
481da177e4SLinus Torvalds {
491da177e4SLinus Torvalds 	spin_lock(&child->sighand->siglock);
506618a3e2SMatthew Wilcox 	if (task_is_traced(child)) {
511ee11844SOleg Nesterov 		/*
521ee11844SOleg Nesterov 		 * If the group stop is completed or in progress,
531ee11844SOleg Nesterov 		 * this thread was already counted as stopped.
541ee11844SOleg Nesterov 		 */
551ee11844SOleg Nesterov 		if (child->signal->flags & SIGNAL_STOP_STOPPED ||
561ee11844SOleg Nesterov 		    child->signal->group_stop_count)
57d9ae90acSOleg Nesterov 			__set_task_state(child, TASK_STOPPED);
581ee11844SOleg Nesterov 		else
591da177e4SLinus Torvalds 			signal_wake_up(child, 1);
601da177e4SLinus Torvalds 	}
611da177e4SLinus Torvalds 	spin_unlock(&child->sighand->siglock);
621da177e4SLinus Torvalds }
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds /*
651da177e4SLinus Torvalds  * unptrace a task: move it back to its original parent and
661da177e4SLinus Torvalds  * remove it from the ptrace list.
671da177e4SLinus Torvalds  *
681da177e4SLinus Torvalds  * Must be called with the tasklist lock write-held.
691da177e4SLinus Torvalds  */
7036c8b586SIngo Molnar void __ptrace_unlink(struct task_struct *child)
711da177e4SLinus Torvalds {
725ecfbae0SOleg Nesterov 	BUG_ON(!child->ptrace);
735ecfbae0SOleg Nesterov 
741da177e4SLinus Torvalds 	child->ptrace = 0;
751da177e4SLinus Torvalds 	child->parent = child->real_parent;
76f470021aSRoland McGrath 	list_del_init(&child->ptrace_entry);
771da177e4SLinus Torvalds 
78bf53de90SMarkus Metzger 	arch_ptrace_untrace(child);
796618a3e2SMatthew Wilcox 	if (task_is_traced(child))
801da177e4SLinus Torvalds 		ptrace_untrace(child);
811da177e4SLinus Torvalds }
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds /*
841da177e4SLinus Torvalds  * Check that we have indeed attached to the thing..
851da177e4SLinus Torvalds  */
861da177e4SLinus Torvalds int ptrace_check_attach(struct task_struct *child, int kill)
871da177e4SLinus Torvalds {
881da177e4SLinus Torvalds 	int ret = -ESRCH;
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds 	/*
911da177e4SLinus Torvalds 	 * We take the read lock around doing both checks to close a
921da177e4SLinus Torvalds 	 * possible race where someone else was tracing our child and
931da177e4SLinus Torvalds 	 * detached between these two checks.  After this locked check,
941da177e4SLinus Torvalds 	 * we are sure that this is our traced child and that can only
951da177e4SLinus Torvalds 	 * be changed by us so it's not changing right after this.
961da177e4SLinus Torvalds 	 */
971da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
98c0c0b649SOleg Nesterov 	if ((child->ptrace & PT_PTRACED) && child->parent == current) {
991da177e4SLinus Torvalds 		ret = 0;
100c0c0b649SOleg Nesterov 		/*
101c0c0b649SOleg Nesterov 		 * child->sighand can't be NULL, release_task()
102c0c0b649SOleg Nesterov 		 * does ptrace_unlink() before __exit_signal().
103c0c0b649SOleg Nesterov 		 */
1041da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
105d9ae90acSOleg Nesterov 		if (task_is_stopped(child))
1061da177e4SLinus Torvalds 			child->state = TASK_TRACED;
107d9ae90acSOleg Nesterov 		else if (!task_is_traced(child) && !kill)
1081da177e4SLinus Torvalds 			ret = -ESRCH;
1091da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
1101da177e4SLinus Torvalds 	}
1111da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
1121da177e4SLinus Torvalds 
113d9ae90acSOleg Nesterov 	if (!ret && !kill)
11485ba2d86SRoland McGrath 		ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH;
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 	/* All systems go.. */
1171da177e4SLinus Torvalds 	return ret;
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds 
120006ebb40SStephen Smalley int __ptrace_may_access(struct task_struct *task, unsigned int mode)
121ab8d11beSMiklos Szeredi {
122c69e8d9cSDavid Howells 	const struct cred *cred = current_cred(), *tcred;
123b6dff3ecSDavid Howells 
124df26c40eSEric W. Biederman 	/* May we inspect the given task?
125df26c40eSEric W. Biederman 	 * This check is used both for attaching with ptrace
126df26c40eSEric W. Biederman 	 * and for allowing access to sensitive information in /proc.
127df26c40eSEric W. Biederman 	 *
128df26c40eSEric W. Biederman 	 * ptrace_attach denies several cases that /proc allows
129df26c40eSEric W. Biederman 	 * because setting up the necessary parent/child relationship
130df26c40eSEric W. Biederman 	 * or halting the specified task is impossible.
131df26c40eSEric W. Biederman 	 */
132df26c40eSEric W. Biederman 	int dumpable = 0;
133df26c40eSEric W. Biederman 	/* Don't let security modules deny introspection */
134df26c40eSEric W. Biederman 	if (task == current)
135df26c40eSEric W. Biederman 		return 0;
136c69e8d9cSDavid Howells 	rcu_read_lock();
137c69e8d9cSDavid Howells 	tcred = __task_cred(task);
138c69e8d9cSDavid Howells 	if ((cred->uid != tcred->euid ||
139c69e8d9cSDavid Howells 	     cred->uid != tcred->suid ||
140c69e8d9cSDavid Howells 	     cred->uid != tcred->uid  ||
141c69e8d9cSDavid Howells 	     cred->gid != tcred->egid ||
142c69e8d9cSDavid Howells 	     cred->gid != tcred->sgid ||
143c69e8d9cSDavid Howells 	     cred->gid != tcred->gid) &&
144c69e8d9cSDavid Howells 	    !capable(CAP_SYS_PTRACE)) {
145c69e8d9cSDavid Howells 		rcu_read_unlock();
146ab8d11beSMiklos Szeredi 		return -EPERM;
147c69e8d9cSDavid Howells 	}
148c69e8d9cSDavid Howells 	rcu_read_unlock();
149ab8d11beSMiklos Szeredi 	smp_rmb();
150df26c40eSEric W. Biederman 	if (task->mm)
1516c5d5238SKawai, Hidehiro 		dumpable = get_dumpable(task->mm);
152df26c40eSEric W. Biederman 	if (!dumpable && !capable(CAP_SYS_PTRACE))
153ab8d11beSMiklos Szeredi 		return -EPERM;
154ab8d11beSMiklos Szeredi 
1559e48858fSIngo Molnar 	return security_ptrace_access_check(task, mode);
156ab8d11beSMiklos Szeredi }
157ab8d11beSMiklos Szeredi 
158006ebb40SStephen Smalley bool ptrace_may_access(struct task_struct *task, unsigned int mode)
159ab8d11beSMiklos Szeredi {
160ab8d11beSMiklos Szeredi 	int err;
161ab8d11beSMiklos Szeredi 	task_lock(task);
162006ebb40SStephen Smalley 	err = __ptrace_may_access(task, mode);
163ab8d11beSMiklos Szeredi 	task_unlock(task);
1643a709703SRoland McGrath 	return !err;
165ab8d11beSMiklos Szeredi }
166ab8d11beSMiklos Szeredi 
1671da177e4SLinus Torvalds int ptrace_attach(struct task_struct *task)
1681da177e4SLinus Torvalds {
1691da177e4SLinus Torvalds 	int retval;
170f5b40e36SLinus Torvalds 
171a5cb013dSAl Viro 	audit_ptrace(task);
172a5cb013dSAl Viro 
1731da177e4SLinus Torvalds 	retval = -EPERM;
174b79b7ba9SOleg Nesterov 	if (unlikely(task->flags & PF_KTHREAD))
175b79b7ba9SOleg Nesterov 		goto out;
176bac0abd6SPavel Emelyanov 	if (same_thread_group(task, current))
177f5b40e36SLinus Torvalds 		goto out;
178f5b40e36SLinus Torvalds 
179f2f0b00aSOleg Nesterov 	/*
180f2f0b00aSOleg Nesterov 	 * Protect exec's credential calculations against our interference;
1815e751e99SDavid Howells 	 * interference; SUID, SGID and LSM creds get determined differently
1825e751e99SDavid Howells 	 * under ptrace.
183d84f4f99SDavid Howells 	 */
1845e751e99SDavid Howells 	retval = mutex_lock_interruptible(&task->cred_guard_mutex);
185d84f4f99SDavid Howells 	if (retval < 0)
186d84f4f99SDavid Howells 		goto out;
1874b105cbbSOleg Nesterov 
188f5b40e36SLinus Torvalds 	task_lock(task);
189006ebb40SStephen Smalley 	retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
1904b105cbbSOleg Nesterov 	task_unlock(task);
1911da177e4SLinus Torvalds 	if (retval)
1924b105cbbSOleg Nesterov 		goto unlock_creds;
1931da177e4SLinus Torvalds 
1944b105cbbSOleg Nesterov 	write_lock_irq(&tasklist_lock);
195b79b7ba9SOleg Nesterov 	retval = -EPERM;
196b79b7ba9SOleg Nesterov 	if (unlikely(task->exit_state))
1974b105cbbSOleg Nesterov 		goto unlock_tasklist;
198f2f0b00aSOleg Nesterov 	if (task->ptrace)
1994b105cbbSOleg Nesterov 		goto unlock_tasklist;
200b79b7ba9SOleg Nesterov 
201f2f0b00aSOleg Nesterov 	task->ptrace = PT_PTRACED;
2021da177e4SLinus Torvalds 	if (capable(CAP_SYS_PTRACE))
2031da177e4SLinus Torvalds 		task->ptrace |= PT_PTRACE_CAP;
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds 	__ptrace_link(task, current);
20633e9fc7dSOleg Nesterov 	send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
207b79b7ba9SOleg Nesterov 
208b79b7ba9SOleg Nesterov 	retval = 0;
2094b105cbbSOleg Nesterov unlock_tasklist:
2104b105cbbSOleg Nesterov 	write_unlock_irq(&tasklist_lock);
2114b105cbbSOleg Nesterov unlock_creds:
2125e751e99SDavid Howells 	mutex_unlock(&task->cred_guard_mutex);
213f5b40e36SLinus Torvalds out:
2141da177e4SLinus Torvalds 	return retval;
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds 
217f2f0b00aSOleg Nesterov /**
218f2f0b00aSOleg Nesterov  * ptrace_traceme  --  helper for PTRACE_TRACEME
219f2f0b00aSOleg Nesterov  *
220f2f0b00aSOleg Nesterov  * Performs checks and sets PT_PTRACED.
221f2f0b00aSOleg Nesterov  * Should be used by all ptrace implementations for PTRACE_TRACEME.
222f2f0b00aSOleg Nesterov  */
223f2f0b00aSOleg Nesterov int ptrace_traceme(void)
224f2f0b00aSOleg Nesterov {
225f2f0b00aSOleg Nesterov 	int ret = -EPERM;
226f2f0b00aSOleg Nesterov 
2274b105cbbSOleg Nesterov 	write_lock_irq(&tasklist_lock);
2284b105cbbSOleg Nesterov 	/* Are we already being traced? */
229f2f0b00aSOleg Nesterov 	if (!current->ptrace) {
230f2f0b00aSOleg Nesterov 		ret = security_ptrace_traceme(current->parent);
231f2f0b00aSOleg Nesterov 		/*
232f2f0b00aSOleg Nesterov 		 * Check PF_EXITING to ensure ->real_parent has not passed
233f2f0b00aSOleg Nesterov 		 * exit_ptrace(). Otherwise we don't report the error but
234f2f0b00aSOleg Nesterov 		 * pretend ->real_parent untraces us right after return.
235f2f0b00aSOleg Nesterov 		 */
236f2f0b00aSOleg Nesterov 		if (!ret && !(current->real_parent->flags & PF_EXITING)) {
237f2f0b00aSOleg Nesterov 			current->ptrace = PT_PTRACED;
238f2f0b00aSOleg Nesterov 			__ptrace_link(current, current->real_parent);
239f2f0b00aSOleg Nesterov 		}
240f2f0b00aSOleg Nesterov 	}
2414b105cbbSOleg Nesterov 	write_unlock_irq(&tasklist_lock);
2424b105cbbSOleg Nesterov 
243f2f0b00aSOleg Nesterov 	return ret;
244f2f0b00aSOleg Nesterov }
245f2f0b00aSOleg Nesterov 
24639c626aeSOleg Nesterov /*
24739c626aeSOleg Nesterov  * Called with irqs disabled, returns true if childs should reap themselves.
24839c626aeSOleg Nesterov  */
24939c626aeSOleg Nesterov static int ignoring_children(struct sighand_struct *sigh)
25039c626aeSOleg Nesterov {
25139c626aeSOleg Nesterov 	int ret;
25239c626aeSOleg Nesterov 	spin_lock(&sigh->siglock);
25339c626aeSOleg Nesterov 	ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) ||
25439c626aeSOleg Nesterov 	      (sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT);
25539c626aeSOleg Nesterov 	spin_unlock(&sigh->siglock);
25639c626aeSOleg Nesterov 	return ret;
25739c626aeSOleg Nesterov }
25839c626aeSOleg Nesterov 
25939c626aeSOleg Nesterov /*
26039c626aeSOleg Nesterov  * Called with tasklist_lock held for writing.
26139c626aeSOleg Nesterov  * Unlink a traced task, and clean it up if it was a traced zombie.
26239c626aeSOleg Nesterov  * Return true if it needs to be reaped with release_task().
26339c626aeSOleg Nesterov  * (We can't call release_task() here because we already hold tasklist_lock.)
26439c626aeSOleg Nesterov  *
26539c626aeSOleg Nesterov  * If it's a zombie, our attachedness prevented normal parent notification
26639c626aeSOleg Nesterov  * or self-reaping.  Do notification now if it would have happened earlier.
26739c626aeSOleg Nesterov  * If it should reap itself, return true.
26839c626aeSOleg Nesterov  *
26939c626aeSOleg Nesterov  * If it's our own child, there is no notification to do.
27039c626aeSOleg Nesterov  * But if our normal children self-reap, then this child
27139c626aeSOleg Nesterov  * was prevented by ptrace and we must reap it now.
27239c626aeSOleg Nesterov  */
27339c626aeSOleg Nesterov static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
27439c626aeSOleg Nesterov {
27539c626aeSOleg Nesterov 	__ptrace_unlink(p);
27639c626aeSOleg Nesterov 
27739c626aeSOleg Nesterov 	if (p->exit_state == EXIT_ZOMBIE) {
27839c626aeSOleg Nesterov 		if (!task_detached(p) && thread_group_empty(p)) {
27939c626aeSOleg Nesterov 			if (!same_thread_group(p->real_parent, tracer))
28039c626aeSOleg Nesterov 				do_notify_parent(p, p->exit_signal);
28139c626aeSOleg Nesterov 			else if (ignoring_children(tracer->sighand))
28239c626aeSOleg Nesterov 				p->exit_signal = -1;
28339c626aeSOleg Nesterov 		}
28439c626aeSOleg Nesterov 		if (task_detached(p)) {
28539c626aeSOleg Nesterov 			/* Mark it as in the process of being reaped. */
28639c626aeSOleg Nesterov 			p->exit_state = EXIT_DEAD;
28739c626aeSOleg Nesterov 			return true;
28839c626aeSOleg Nesterov 		}
28939c626aeSOleg Nesterov 	}
29039c626aeSOleg Nesterov 
29139c626aeSOleg Nesterov 	return false;
29239c626aeSOleg Nesterov }
29339c626aeSOleg Nesterov 
2941da177e4SLinus Torvalds int ptrace_detach(struct task_struct *child, unsigned int data)
2951da177e4SLinus Torvalds {
29639c626aeSOleg Nesterov 	bool dead = false;
2974576145cSOleg Nesterov 
2987ed20e1aSJesper Juhl 	if (!valid_signal(data))
2991da177e4SLinus Torvalds 		return -EIO;
3001da177e4SLinus Torvalds 
3011da177e4SLinus Torvalds 	/* Architecture-specific hardware disable .. */
3021da177e4SLinus Torvalds 	ptrace_disable(child);
3037d941432SRoland McGrath 	clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
3041da177e4SLinus Torvalds 
30595c3eb76SOleg Nesterov 	write_lock_irq(&tasklist_lock);
30639c626aeSOleg Nesterov 	/*
30739c626aeSOleg Nesterov 	 * This child can be already killed. Make sure de_thread() or
30839c626aeSOleg Nesterov 	 * our sub-thread doing do_wait() didn't do release_task() yet.
30939c626aeSOleg Nesterov 	 */
31095c3eb76SOleg Nesterov 	if (child->ptrace) {
31195c3eb76SOleg Nesterov 		child->exit_code = data;
3124576145cSOleg Nesterov 		dead = __ptrace_detach(current, child);
313edaba2c5SOleg Nesterov 		if (!child->exit_state)
314edaba2c5SOleg Nesterov 			wake_up_process(child);
31595c3eb76SOleg Nesterov 	}
3161da177e4SLinus Torvalds 	write_unlock_irq(&tasklist_lock);
3171da177e4SLinus Torvalds 
3184576145cSOleg Nesterov 	if (unlikely(dead))
3194576145cSOleg Nesterov 		release_task(child);
3204576145cSOleg Nesterov 
3211da177e4SLinus Torvalds 	return 0;
3221da177e4SLinus Torvalds }
3231da177e4SLinus Torvalds 
32439c626aeSOleg Nesterov /*
32539c626aeSOleg Nesterov  * Detach all tasks we were using ptrace on.
32639c626aeSOleg Nesterov  */
32739c626aeSOleg Nesterov void exit_ptrace(struct task_struct *tracer)
32839c626aeSOleg Nesterov {
32939c626aeSOleg Nesterov 	struct task_struct *p, *n;
33039c626aeSOleg Nesterov 	LIST_HEAD(ptrace_dead);
33139c626aeSOleg Nesterov 
33239c626aeSOleg Nesterov 	write_lock_irq(&tasklist_lock);
33339c626aeSOleg Nesterov 	list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) {
33439c626aeSOleg Nesterov 		if (__ptrace_detach(tracer, p))
33539c626aeSOleg Nesterov 			list_add(&p->ptrace_entry, &ptrace_dead);
33639c626aeSOleg Nesterov 	}
33739c626aeSOleg Nesterov 	write_unlock_irq(&tasklist_lock);
33839c626aeSOleg Nesterov 
33939c626aeSOleg Nesterov 	BUG_ON(!list_empty(&tracer->ptraced));
34039c626aeSOleg Nesterov 
34139c626aeSOleg Nesterov 	list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) {
34239c626aeSOleg Nesterov 		list_del_init(&p->ptrace_entry);
34339c626aeSOleg Nesterov 		release_task(p);
34439c626aeSOleg Nesterov 	}
34539c626aeSOleg Nesterov }
34639c626aeSOleg Nesterov 
3471da177e4SLinus Torvalds int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
3481da177e4SLinus Torvalds {
3491da177e4SLinus Torvalds 	int copied = 0;
3501da177e4SLinus Torvalds 
3511da177e4SLinus Torvalds 	while (len > 0) {
3521da177e4SLinus Torvalds 		char buf[128];
3531da177e4SLinus Torvalds 		int this_len, retval;
3541da177e4SLinus Torvalds 
3551da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
3561da177e4SLinus Torvalds 		retval = access_process_vm(tsk, src, buf, this_len, 0);
3571da177e4SLinus Torvalds 		if (!retval) {
3581da177e4SLinus Torvalds 			if (copied)
3591da177e4SLinus Torvalds 				break;
3601da177e4SLinus Torvalds 			return -EIO;
3611da177e4SLinus Torvalds 		}
3621da177e4SLinus Torvalds 		if (copy_to_user(dst, buf, retval))
3631da177e4SLinus Torvalds 			return -EFAULT;
3641da177e4SLinus Torvalds 		copied += retval;
3651da177e4SLinus Torvalds 		src += retval;
3661da177e4SLinus Torvalds 		dst += retval;
3671da177e4SLinus Torvalds 		len -= retval;
3681da177e4SLinus Torvalds 	}
3691da177e4SLinus Torvalds 	return copied;
3701da177e4SLinus Torvalds }
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
3731da177e4SLinus Torvalds {
3741da177e4SLinus Torvalds 	int copied = 0;
3751da177e4SLinus Torvalds 
3761da177e4SLinus Torvalds 	while (len > 0) {
3771da177e4SLinus Torvalds 		char buf[128];
3781da177e4SLinus Torvalds 		int this_len, retval;
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
3811da177e4SLinus Torvalds 		if (copy_from_user(buf, src, this_len))
3821da177e4SLinus Torvalds 			return -EFAULT;
3831da177e4SLinus Torvalds 		retval = access_process_vm(tsk, dst, buf, this_len, 1);
3841da177e4SLinus Torvalds 		if (!retval) {
3851da177e4SLinus Torvalds 			if (copied)
3861da177e4SLinus Torvalds 				break;
3871da177e4SLinus Torvalds 			return -EIO;
3881da177e4SLinus Torvalds 		}
3891da177e4SLinus Torvalds 		copied += retval;
3901da177e4SLinus Torvalds 		src += retval;
3911da177e4SLinus Torvalds 		dst += retval;
3921da177e4SLinus Torvalds 		len -= retval;
3931da177e4SLinus Torvalds 	}
3941da177e4SLinus Torvalds 	return copied;
3951da177e4SLinus Torvalds }
3961da177e4SLinus Torvalds 
3971da177e4SLinus Torvalds static int ptrace_setoptions(struct task_struct *child, long data)
3981da177e4SLinus Torvalds {
3991da177e4SLinus Torvalds 	child->ptrace &= ~PT_TRACE_MASK;
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACESYSGOOD)
4021da177e4SLinus Torvalds 		child->ptrace |= PT_TRACESYSGOOD;
4031da177e4SLinus Torvalds 
4041da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEFORK)
4051da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_FORK;
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORK)
4081da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK;
4091da177e4SLinus Torvalds 
4101da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACECLONE)
4111da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_CLONE;
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXEC)
4141da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXEC;
4151da177e4SLinus Torvalds 
4161da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORKDONE)
4171da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK_DONE;
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXIT)
4201da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXIT;
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
4231da177e4SLinus Torvalds }
4241da177e4SLinus Torvalds 
425e16b2781SRoland McGrath static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info)
4261da177e4SLinus Torvalds {
427e4961254SOleg Nesterov 	unsigned long flags;
4281da177e4SLinus Torvalds 	int error = -ESRCH;
4291da177e4SLinus Torvalds 
430e4961254SOleg Nesterov 	if (lock_task_sighand(child, &flags)) {
4311da177e4SLinus Torvalds 		error = -EINVAL;
4321da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
433e16b2781SRoland McGrath 			*info = *child->last_siginfo;
4341da177e4SLinus Torvalds 			error = 0;
4351da177e4SLinus Torvalds 		}
436e4961254SOleg Nesterov 		unlock_task_sighand(child, &flags);
4371da177e4SLinus Torvalds 	}
4381da177e4SLinus Torvalds 	return error;
4391da177e4SLinus Torvalds }
4401da177e4SLinus Torvalds 
441e16b2781SRoland McGrath static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info)
4421da177e4SLinus Torvalds {
443e4961254SOleg Nesterov 	unsigned long flags;
4441da177e4SLinus Torvalds 	int error = -ESRCH;
4451da177e4SLinus Torvalds 
446e4961254SOleg Nesterov 	if (lock_task_sighand(child, &flags)) {
4471da177e4SLinus Torvalds 		error = -EINVAL;
4481da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
449e16b2781SRoland McGrath 			*child->last_siginfo = *info;
4501da177e4SLinus Torvalds 			error = 0;
4511da177e4SLinus Torvalds 		}
452e4961254SOleg Nesterov 		unlock_task_sighand(child, &flags);
4531da177e4SLinus Torvalds 	}
4541da177e4SLinus Torvalds 	return error;
4551da177e4SLinus Torvalds }
4561da177e4SLinus Torvalds 
45736df29d7SRoland McGrath 
45836df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
45936df29d7SRoland McGrath #define is_singlestep(request)		((request) == PTRACE_SINGLESTEP)
46036df29d7SRoland McGrath #else
46136df29d7SRoland McGrath #define is_singlestep(request)		0
46236df29d7SRoland McGrath #endif
46336df29d7SRoland McGrath 
4645b88abbfSRoland McGrath #ifdef PTRACE_SINGLEBLOCK
4655b88abbfSRoland McGrath #define is_singleblock(request)		((request) == PTRACE_SINGLEBLOCK)
4665b88abbfSRoland McGrath #else
4675b88abbfSRoland McGrath #define is_singleblock(request)		0
4685b88abbfSRoland McGrath #endif
4695b88abbfSRoland McGrath 
47036df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
47136df29d7SRoland McGrath #define is_sysemu_singlestep(request)	((request) == PTRACE_SYSEMU_SINGLESTEP)
47236df29d7SRoland McGrath #else
47336df29d7SRoland McGrath #define is_sysemu_singlestep(request)	0
47436df29d7SRoland McGrath #endif
47536df29d7SRoland McGrath 
47636df29d7SRoland McGrath static int ptrace_resume(struct task_struct *child, long request, long data)
47736df29d7SRoland McGrath {
47836df29d7SRoland McGrath 	if (!valid_signal(data))
47936df29d7SRoland McGrath 		return -EIO;
48036df29d7SRoland McGrath 
48136df29d7SRoland McGrath 	if (request == PTRACE_SYSCALL)
48236df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
48336df29d7SRoland McGrath 	else
48436df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
48536df29d7SRoland McGrath 
48636df29d7SRoland McGrath #ifdef TIF_SYSCALL_EMU
48736df29d7SRoland McGrath 	if (request == PTRACE_SYSEMU || request == PTRACE_SYSEMU_SINGLESTEP)
48836df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_EMU);
48936df29d7SRoland McGrath 	else
49036df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
49136df29d7SRoland McGrath #endif
49236df29d7SRoland McGrath 
4935b88abbfSRoland McGrath 	if (is_singleblock(request)) {
4945b88abbfSRoland McGrath 		if (unlikely(!arch_has_block_step()))
4955b88abbfSRoland McGrath 			return -EIO;
4965b88abbfSRoland McGrath 		user_enable_block_step(child);
4975b88abbfSRoland McGrath 	} else if (is_singlestep(request) || is_sysemu_singlestep(request)) {
49836df29d7SRoland McGrath 		if (unlikely(!arch_has_single_step()))
49936df29d7SRoland McGrath 			return -EIO;
50036df29d7SRoland McGrath 		user_enable_single_step(child);
5013a709703SRoland McGrath 	} else {
50236df29d7SRoland McGrath 		user_disable_single_step(child);
5033a709703SRoland McGrath 	}
50436df29d7SRoland McGrath 
50536df29d7SRoland McGrath 	child->exit_code = data;
50636df29d7SRoland McGrath 	wake_up_process(child);
50736df29d7SRoland McGrath 
50836df29d7SRoland McGrath 	return 0;
50936df29d7SRoland McGrath }
51036df29d7SRoland McGrath 
5111da177e4SLinus Torvalds int ptrace_request(struct task_struct *child, long request,
5121da177e4SLinus Torvalds 		   long addr, long data)
5131da177e4SLinus Torvalds {
5141da177e4SLinus Torvalds 	int ret = -EIO;
515e16b2781SRoland McGrath 	siginfo_t siginfo;
5161da177e4SLinus Torvalds 
5171da177e4SLinus Torvalds 	switch (request) {
51816c3e389SRoland McGrath 	case PTRACE_PEEKTEXT:
51916c3e389SRoland McGrath 	case PTRACE_PEEKDATA:
52016c3e389SRoland McGrath 		return generic_ptrace_peekdata(child, addr, data);
52116c3e389SRoland McGrath 	case PTRACE_POKETEXT:
52216c3e389SRoland McGrath 	case PTRACE_POKEDATA:
52316c3e389SRoland McGrath 		return generic_ptrace_pokedata(child, addr, data);
52416c3e389SRoland McGrath 
5251da177e4SLinus Torvalds #ifdef PTRACE_OLDSETOPTIONS
5261da177e4SLinus Torvalds 	case PTRACE_OLDSETOPTIONS:
5271da177e4SLinus Torvalds #endif
5281da177e4SLinus Torvalds 	case PTRACE_SETOPTIONS:
5291da177e4SLinus Torvalds 		ret = ptrace_setoptions(child, data);
5301da177e4SLinus Torvalds 		break;
5311da177e4SLinus Torvalds 	case PTRACE_GETEVENTMSG:
5321da177e4SLinus Torvalds 		ret = put_user(child->ptrace_message, (unsigned long __user *) data);
5331da177e4SLinus Torvalds 		break;
534e16b2781SRoland McGrath 
5351da177e4SLinus Torvalds 	case PTRACE_GETSIGINFO:
536e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
537e16b2781SRoland McGrath 		if (!ret)
538e16b2781SRoland McGrath 			ret = copy_siginfo_to_user((siginfo_t __user *) data,
539e16b2781SRoland McGrath 						   &siginfo);
5401da177e4SLinus Torvalds 		break;
541e16b2781SRoland McGrath 
5421da177e4SLinus Torvalds 	case PTRACE_SETSIGINFO:
543e16b2781SRoland McGrath 		if (copy_from_user(&siginfo, (siginfo_t __user *) data,
544e16b2781SRoland McGrath 				   sizeof siginfo))
545e16b2781SRoland McGrath 			ret = -EFAULT;
546e16b2781SRoland McGrath 		else
547e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
5481da177e4SLinus Torvalds 		break;
549e16b2781SRoland McGrath 
5501bcf5482SAlexey Dobriyan 	case PTRACE_DETACH:	 /* detach a process that was attached. */
5511bcf5482SAlexey Dobriyan 		ret = ptrace_detach(child, data);
5521bcf5482SAlexey Dobriyan 		break;
55336df29d7SRoland McGrath 
55436df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
55536df29d7SRoland McGrath 	case PTRACE_SINGLESTEP:
55636df29d7SRoland McGrath #endif
5575b88abbfSRoland McGrath #ifdef PTRACE_SINGLEBLOCK
5585b88abbfSRoland McGrath 	case PTRACE_SINGLEBLOCK:
5595b88abbfSRoland McGrath #endif
56036df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
56136df29d7SRoland McGrath 	case PTRACE_SYSEMU:
56236df29d7SRoland McGrath 	case PTRACE_SYSEMU_SINGLESTEP:
56336df29d7SRoland McGrath #endif
56436df29d7SRoland McGrath 	case PTRACE_SYSCALL:
56536df29d7SRoland McGrath 	case PTRACE_CONT:
56636df29d7SRoland McGrath 		return ptrace_resume(child, request, data);
56736df29d7SRoland McGrath 
56836df29d7SRoland McGrath 	case PTRACE_KILL:
56936df29d7SRoland McGrath 		if (child->exit_state)	/* already dead */
57036df29d7SRoland McGrath 			return 0;
57136df29d7SRoland McGrath 		return ptrace_resume(child, request, SIGKILL);
57236df29d7SRoland McGrath 
5731da177e4SLinus Torvalds 	default:
5741da177e4SLinus Torvalds 		break;
5751da177e4SLinus Torvalds 	}
5761da177e4SLinus Torvalds 
5771da177e4SLinus Torvalds 	return ret;
5781da177e4SLinus Torvalds }
579481bed45SChristoph Hellwig 
5808053bdd5SOleg Nesterov static struct task_struct *ptrace_get_task_struct(pid_t pid)
5816b9c7ed8SChristoph Hellwig {
5826b9c7ed8SChristoph Hellwig 	struct task_struct *child;
5836b9c7ed8SChristoph Hellwig 
5848053bdd5SOleg Nesterov 	rcu_read_lock();
585228ebcbeSPavel Emelyanov 	child = find_task_by_vpid(pid);
586481bed45SChristoph Hellwig 	if (child)
587481bed45SChristoph Hellwig 		get_task_struct(child);
5888053bdd5SOleg Nesterov 	rcu_read_unlock();
589f400e198SSukadev Bhattiprolu 
590481bed45SChristoph Hellwig 	if (!child)
5916b9c7ed8SChristoph Hellwig 		return ERR_PTR(-ESRCH);
5926b9c7ed8SChristoph Hellwig 	return child;
593481bed45SChristoph Hellwig }
594481bed45SChristoph Hellwig 
5950ac15559SChristoph Hellwig #ifndef arch_ptrace_attach
5960ac15559SChristoph Hellwig #define arch_ptrace_attach(child)	do { } while (0)
5970ac15559SChristoph Hellwig #endif
5980ac15559SChristoph Hellwig 
5991e7bfb21SHeiko Carstens SYSCALL_DEFINE4(ptrace, long, request, long, pid, long, addr, long, data)
600481bed45SChristoph Hellwig {
601481bed45SChristoph Hellwig 	struct task_struct *child;
602481bed45SChristoph Hellwig 	long ret;
603481bed45SChristoph Hellwig 
604481bed45SChristoph Hellwig 	/*
605481bed45SChristoph Hellwig 	 * This lock_kernel fixes a subtle race with suid exec
606481bed45SChristoph Hellwig 	 */
607481bed45SChristoph Hellwig 	lock_kernel();
6086b9c7ed8SChristoph Hellwig 	if (request == PTRACE_TRACEME) {
6096b9c7ed8SChristoph Hellwig 		ret = ptrace_traceme();
6106ea6dd93SHaavard Skinnemoen 		if (!ret)
6116ea6dd93SHaavard Skinnemoen 			arch_ptrace_attach(current);
612481bed45SChristoph Hellwig 		goto out;
6136b9c7ed8SChristoph Hellwig 	}
6146b9c7ed8SChristoph Hellwig 
6156b9c7ed8SChristoph Hellwig 	child = ptrace_get_task_struct(pid);
6166b9c7ed8SChristoph Hellwig 	if (IS_ERR(child)) {
6176b9c7ed8SChristoph Hellwig 		ret = PTR_ERR(child);
6186b9c7ed8SChristoph Hellwig 		goto out;
6196b9c7ed8SChristoph Hellwig 	}
620481bed45SChristoph Hellwig 
621481bed45SChristoph Hellwig 	if (request == PTRACE_ATTACH) {
622481bed45SChristoph Hellwig 		ret = ptrace_attach(child);
6230ac15559SChristoph Hellwig 		/*
6240ac15559SChristoph Hellwig 		 * Some architectures need to do book-keeping after
6250ac15559SChristoph Hellwig 		 * a ptrace attach.
6260ac15559SChristoph Hellwig 		 */
6270ac15559SChristoph Hellwig 		if (!ret)
6280ac15559SChristoph Hellwig 			arch_ptrace_attach(child);
629005f18dfSChristoph Hellwig 		goto out_put_task_struct;
630481bed45SChristoph Hellwig 	}
631481bed45SChristoph Hellwig 
632481bed45SChristoph Hellwig 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
633481bed45SChristoph Hellwig 	if (ret < 0)
634481bed45SChristoph Hellwig 		goto out_put_task_struct;
635481bed45SChristoph Hellwig 
636481bed45SChristoph Hellwig 	ret = arch_ptrace(child, request, addr, data);
637481bed45SChristoph Hellwig 
638481bed45SChristoph Hellwig  out_put_task_struct:
639481bed45SChristoph Hellwig 	put_task_struct(child);
640481bed45SChristoph Hellwig  out:
641481bed45SChristoph Hellwig 	unlock_kernel();
642481bed45SChristoph Hellwig 	return ret;
643481bed45SChristoph Hellwig }
64476647323SAlexey Dobriyan 
64576647323SAlexey Dobriyan int generic_ptrace_peekdata(struct task_struct *tsk, long addr, long data)
64676647323SAlexey Dobriyan {
64776647323SAlexey Dobriyan 	unsigned long tmp;
64876647323SAlexey Dobriyan 	int copied;
64976647323SAlexey Dobriyan 
65076647323SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
65176647323SAlexey Dobriyan 	if (copied != sizeof(tmp))
65276647323SAlexey Dobriyan 		return -EIO;
65376647323SAlexey Dobriyan 	return put_user(tmp, (unsigned long __user *)data);
65476647323SAlexey Dobriyan }
655f284ce72SAlexey Dobriyan 
656f284ce72SAlexey Dobriyan int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data)
657f284ce72SAlexey Dobriyan {
658f284ce72SAlexey Dobriyan 	int copied;
659f284ce72SAlexey Dobriyan 
660f284ce72SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
661f284ce72SAlexey Dobriyan 	return (copied == sizeof(data)) ? 0 : -EIO;
662f284ce72SAlexey Dobriyan }
663032d82d9SRoland McGrath 
66496b8936aSChristoph Hellwig #if defined CONFIG_COMPAT
665032d82d9SRoland McGrath #include <linux/compat.h>
666032d82d9SRoland McGrath 
667032d82d9SRoland McGrath int compat_ptrace_request(struct task_struct *child, compat_long_t request,
668032d82d9SRoland McGrath 			  compat_ulong_t addr, compat_ulong_t data)
669032d82d9SRoland McGrath {
670032d82d9SRoland McGrath 	compat_ulong_t __user *datap = compat_ptr(data);
671032d82d9SRoland McGrath 	compat_ulong_t word;
672e16b2781SRoland McGrath 	siginfo_t siginfo;
673032d82d9SRoland McGrath 	int ret;
674032d82d9SRoland McGrath 
675032d82d9SRoland McGrath 	switch (request) {
676032d82d9SRoland McGrath 	case PTRACE_PEEKTEXT:
677032d82d9SRoland McGrath 	case PTRACE_PEEKDATA:
678032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &word, sizeof(word), 0);
679032d82d9SRoland McGrath 		if (ret != sizeof(word))
680032d82d9SRoland McGrath 			ret = -EIO;
681032d82d9SRoland McGrath 		else
682032d82d9SRoland McGrath 			ret = put_user(word, datap);
683032d82d9SRoland McGrath 		break;
684032d82d9SRoland McGrath 
685032d82d9SRoland McGrath 	case PTRACE_POKETEXT:
686032d82d9SRoland McGrath 	case PTRACE_POKEDATA:
687032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &data, sizeof(data), 1);
688032d82d9SRoland McGrath 		ret = (ret != sizeof(data) ? -EIO : 0);
689032d82d9SRoland McGrath 		break;
690032d82d9SRoland McGrath 
691032d82d9SRoland McGrath 	case PTRACE_GETEVENTMSG:
692032d82d9SRoland McGrath 		ret = put_user((compat_ulong_t) child->ptrace_message, datap);
693032d82d9SRoland McGrath 		break;
694032d82d9SRoland McGrath 
695e16b2781SRoland McGrath 	case PTRACE_GETSIGINFO:
696e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
697e16b2781SRoland McGrath 		if (!ret)
698e16b2781SRoland McGrath 			ret = copy_siginfo_to_user32(
699e16b2781SRoland McGrath 				(struct compat_siginfo __user *) datap,
700e16b2781SRoland McGrath 				&siginfo);
701e16b2781SRoland McGrath 		break;
702e16b2781SRoland McGrath 
703e16b2781SRoland McGrath 	case PTRACE_SETSIGINFO:
704e16b2781SRoland McGrath 		memset(&siginfo, 0, sizeof siginfo);
705e16b2781SRoland McGrath 		if (copy_siginfo_from_user32(
706e16b2781SRoland McGrath 			    &siginfo, (struct compat_siginfo __user *) datap))
707e16b2781SRoland McGrath 			ret = -EFAULT;
708e16b2781SRoland McGrath 		else
709e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
710e16b2781SRoland McGrath 		break;
711e16b2781SRoland McGrath 
712032d82d9SRoland McGrath 	default:
713032d82d9SRoland McGrath 		ret = ptrace_request(child, request, addr, data);
714032d82d9SRoland McGrath 	}
715032d82d9SRoland McGrath 
716032d82d9SRoland McGrath 	return ret;
717032d82d9SRoland McGrath }
718c269f196SRoland McGrath 
719c269f196SRoland McGrath asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
720c269f196SRoland McGrath 				  compat_long_t addr, compat_long_t data)
721c269f196SRoland McGrath {
722c269f196SRoland McGrath 	struct task_struct *child;
723c269f196SRoland McGrath 	long ret;
724c269f196SRoland McGrath 
725c269f196SRoland McGrath 	/*
726c269f196SRoland McGrath 	 * This lock_kernel fixes a subtle race with suid exec
727c269f196SRoland McGrath 	 */
728c269f196SRoland McGrath 	lock_kernel();
729c269f196SRoland McGrath 	if (request == PTRACE_TRACEME) {
730c269f196SRoland McGrath 		ret = ptrace_traceme();
731c269f196SRoland McGrath 		goto out;
732c269f196SRoland McGrath 	}
733c269f196SRoland McGrath 
734c269f196SRoland McGrath 	child = ptrace_get_task_struct(pid);
735c269f196SRoland McGrath 	if (IS_ERR(child)) {
736c269f196SRoland McGrath 		ret = PTR_ERR(child);
737c269f196SRoland McGrath 		goto out;
738c269f196SRoland McGrath 	}
739c269f196SRoland McGrath 
740c269f196SRoland McGrath 	if (request == PTRACE_ATTACH) {
741c269f196SRoland McGrath 		ret = ptrace_attach(child);
742c269f196SRoland McGrath 		/*
743c269f196SRoland McGrath 		 * Some architectures need to do book-keeping after
744c269f196SRoland McGrath 		 * a ptrace attach.
745c269f196SRoland McGrath 		 */
746c269f196SRoland McGrath 		if (!ret)
747c269f196SRoland McGrath 			arch_ptrace_attach(child);
748c269f196SRoland McGrath 		goto out_put_task_struct;
749c269f196SRoland McGrath 	}
750c269f196SRoland McGrath 
751c269f196SRoland McGrath 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
752c269f196SRoland McGrath 	if (!ret)
753c269f196SRoland McGrath 		ret = compat_arch_ptrace(child, request, addr, data);
754c269f196SRoland McGrath 
755c269f196SRoland McGrath  out_put_task_struct:
756c269f196SRoland McGrath 	put_task_struct(child);
757c269f196SRoland McGrath  out:
758c269f196SRoland McGrath 	unlock_kernel();
759c269f196SRoland McGrath 	return ret;
760c269f196SRoland McGrath }
76196b8936aSChristoph Hellwig #endif	/* CONFIG_COMPAT */
762