xref: /openbmc/linux/kernel/ptrace.c (revision e3bd058f)
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/ptrace.h>
181da177e4SLinus Torvalds #include <linux/security.h>
197ed20e1aSJesper Juhl #include <linux/signal.h>
20a5cb013dSAl Viro #include <linux/audit.h>
21b488893aSPavel Emelyanov #include <linux/pid_namespace.h>
22f17d30a8SAdrian Bunk #include <linux/syscalls.h>
233a709703SRoland McGrath #include <linux/uaccess.h>
242225a122SSuresh Siddha #include <linux/regset.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 
40e3bd058fSTejun Heo /**
41e3bd058fSTejun Heo  * __ptrace_unlink - unlink ptracee and restore its execution state
42e3bd058fSTejun Heo  * @child: ptracee to be unlinked
43e3bd058fSTejun Heo  *
44e3bd058fSTejun Heo  * Remove @child from the ptrace list, move it back to the original parent.
45e3bd058fSTejun Heo  *
46e3bd058fSTejun Heo  * CONTEXT:
47e3bd058fSTejun Heo  * write_lock_irq(tasklist_lock)
481da177e4SLinus Torvalds  */
49e3bd058fSTejun Heo void __ptrace_unlink(struct task_struct *child)
501da177e4SLinus Torvalds {
51e3bd058fSTejun Heo 	BUG_ON(!child->ptrace);
52e3bd058fSTejun Heo 
53e3bd058fSTejun Heo 	child->ptrace = 0;
54e3bd058fSTejun Heo 	child->parent = child->real_parent;
55e3bd058fSTejun Heo 	list_del_init(&child->ptrace_entry);
56e3bd058fSTejun Heo 
571da177e4SLinus Torvalds 	spin_lock(&child->sighand->siglock);
586618a3e2SMatthew Wilcox 	if (task_is_traced(child)) {
591ee11844SOleg Nesterov 		/*
60d79fdd6dSTejun Heo 		 * If group stop is completed or in progress, it should
61d79fdd6dSTejun Heo 		 * participate in the group stop.  Set GROUP_STOP_PENDING
62d79fdd6dSTejun Heo 		 * before kicking it.
63d79fdd6dSTejun Heo 		 *
64d79fdd6dSTejun Heo 		 * This involves TRACED -> RUNNING -> STOPPED transition
65d79fdd6dSTejun Heo 		 * which is similar to but in the opposite direction of
66d79fdd6dSTejun Heo 		 * what happens while attaching to a stopped task.
67d79fdd6dSTejun Heo 		 * However, in this direction, the intermediate RUNNING
68d79fdd6dSTejun Heo 		 * state is not hidden even from the current ptracer and if
69d79fdd6dSTejun Heo 		 * it immediately re-attaches and performs a WNOHANG
70d79fdd6dSTejun Heo 		 * wait(2), it may fail.
711ee11844SOleg Nesterov 		 */
721ee11844SOleg Nesterov 		if (child->signal->flags & SIGNAL_STOP_STOPPED ||
731ee11844SOleg Nesterov 		    child->signal->group_stop_count)
74d79fdd6dSTejun Heo 			child->group_stop |= GROUP_STOP_PENDING;
751da177e4SLinus Torvalds 		signal_wake_up(child, 1);
761da177e4SLinus Torvalds 	}
771da177e4SLinus Torvalds 	spin_unlock(&child->sighand->siglock);
781da177e4SLinus Torvalds }
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds /*
811da177e4SLinus Torvalds  * Check that we have indeed attached to the thing..
821da177e4SLinus Torvalds  */
831da177e4SLinus Torvalds int ptrace_check_attach(struct task_struct *child, int kill)
841da177e4SLinus Torvalds {
851da177e4SLinus Torvalds 	int ret = -ESRCH;
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	/*
881da177e4SLinus Torvalds 	 * We take the read lock around doing both checks to close a
891da177e4SLinus Torvalds 	 * possible race where someone else was tracing our child and
901da177e4SLinus Torvalds 	 * detached between these two checks.  After this locked check,
911da177e4SLinus Torvalds 	 * we are sure that this is our traced child and that can only
921da177e4SLinus Torvalds 	 * be changed by us so it's not changing right after this.
931da177e4SLinus Torvalds 	 */
941da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
95c0c0b649SOleg Nesterov 	if ((child->ptrace & PT_PTRACED) && child->parent == current) {
961da177e4SLinus Torvalds 		ret = 0;
97c0c0b649SOleg Nesterov 		/*
98c0c0b649SOleg Nesterov 		 * child->sighand can't be NULL, release_task()
99c0c0b649SOleg Nesterov 		 * does ptrace_unlink() before __exit_signal().
100c0c0b649SOleg Nesterov 		 */
1011da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
102d9ae90acSOleg Nesterov 		if (task_is_stopped(child))
1031da177e4SLinus Torvalds 			child->state = TASK_TRACED;
104d9ae90acSOleg Nesterov 		else if (!task_is_traced(child) && !kill)
1051da177e4SLinus Torvalds 			ret = -ESRCH;
1061da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
1071da177e4SLinus Torvalds 	}
1081da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
1091da177e4SLinus Torvalds 
110d9ae90acSOleg Nesterov 	if (!ret && !kill)
11185ba2d86SRoland McGrath 		ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH;
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds 	/* All systems go.. */
1141da177e4SLinus Torvalds 	return ret;
1151da177e4SLinus Torvalds }
1161da177e4SLinus Torvalds 
117006ebb40SStephen Smalley int __ptrace_may_access(struct task_struct *task, unsigned int mode)
118ab8d11beSMiklos Szeredi {
119c69e8d9cSDavid Howells 	const struct cred *cred = current_cred(), *tcred;
120b6dff3ecSDavid Howells 
121df26c40eSEric W. Biederman 	/* May we inspect the given task?
122df26c40eSEric W. Biederman 	 * This check is used both for attaching with ptrace
123df26c40eSEric W. Biederman 	 * and for allowing access to sensitive information in /proc.
124df26c40eSEric W. Biederman 	 *
125df26c40eSEric W. Biederman 	 * ptrace_attach denies several cases that /proc allows
126df26c40eSEric W. Biederman 	 * because setting up the necessary parent/child relationship
127df26c40eSEric W. Biederman 	 * or halting the specified task is impossible.
128df26c40eSEric W. Biederman 	 */
129df26c40eSEric W. Biederman 	int dumpable = 0;
130df26c40eSEric W. Biederman 	/* Don't let security modules deny introspection */
131df26c40eSEric W. Biederman 	if (task == current)
132df26c40eSEric W. Biederman 		return 0;
133c69e8d9cSDavid Howells 	rcu_read_lock();
134c69e8d9cSDavid Howells 	tcred = __task_cred(task);
135c69e8d9cSDavid Howells 	if ((cred->uid != tcred->euid ||
136c69e8d9cSDavid Howells 	     cred->uid != tcred->suid ||
137c69e8d9cSDavid Howells 	     cred->uid != tcred->uid  ||
138c69e8d9cSDavid Howells 	     cred->gid != tcred->egid ||
139c69e8d9cSDavid Howells 	     cred->gid != tcred->sgid ||
140c69e8d9cSDavid Howells 	     cred->gid != tcred->gid) &&
141c69e8d9cSDavid Howells 	    !capable(CAP_SYS_PTRACE)) {
142c69e8d9cSDavid Howells 		rcu_read_unlock();
143ab8d11beSMiklos Szeredi 		return -EPERM;
144c69e8d9cSDavid Howells 	}
145c69e8d9cSDavid Howells 	rcu_read_unlock();
146ab8d11beSMiklos Szeredi 	smp_rmb();
147df26c40eSEric W. Biederman 	if (task->mm)
1486c5d5238SKawai, Hidehiro 		dumpable = get_dumpable(task->mm);
149df26c40eSEric W. Biederman 	if (!dumpable && !capable(CAP_SYS_PTRACE))
150ab8d11beSMiklos Szeredi 		return -EPERM;
151ab8d11beSMiklos Szeredi 
1529e48858fSIngo Molnar 	return security_ptrace_access_check(task, mode);
153ab8d11beSMiklos Szeredi }
154ab8d11beSMiklos Szeredi 
155006ebb40SStephen Smalley bool ptrace_may_access(struct task_struct *task, unsigned int mode)
156ab8d11beSMiklos Szeredi {
157ab8d11beSMiklos Szeredi 	int err;
158ab8d11beSMiklos Szeredi 	task_lock(task);
159006ebb40SStephen Smalley 	err = __ptrace_may_access(task, mode);
160ab8d11beSMiklos Szeredi 	task_unlock(task);
1613a709703SRoland McGrath 	return !err;
162ab8d11beSMiklos Szeredi }
163ab8d11beSMiklos Szeredi 
164e3e89cc5SLinus Torvalds static int ptrace_attach(struct task_struct *task)
1651da177e4SLinus Torvalds {
166d79fdd6dSTejun Heo 	bool wait_trap = false;
1671da177e4SLinus Torvalds 	int retval;
168f5b40e36SLinus Torvalds 
169a5cb013dSAl Viro 	audit_ptrace(task);
170a5cb013dSAl Viro 
1711da177e4SLinus Torvalds 	retval = -EPERM;
172b79b7ba9SOleg Nesterov 	if (unlikely(task->flags & PF_KTHREAD))
173b79b7ba9SOleg Nesterov 		goto out;
174bac0abd6SPavel Emelyanov 	if (same_thread_group(task, current))
175f5b40e36SLinus Torvalds 		goto out;
176f5b40e36SLinus Torvalds 
177f2f0b00aSOleg Nesterov 	/*
178f2f0b00aSOleg Nesterov 	 * Protect exec's credential calculations against our interference;
1795e751e99SDavid Howells 	 * interference; SUID, SGID and LSM creds get determined differently
1805e751e99SDavid Howells 	 * under ptrace.
181d84f4f99SDavid Howells 	 */
182793285fcSOleg Nesterov 	retval = -ERESTARTNOINTR;
1839b1bf12dSKOSAKI Motohiro 	if (mutex_lock_interruptible(&task->signal->cred_guard_mutex))
184d84f4f99SDavid Howells 		goto out;
1854b105cbbSOleg Nesterov 
186f5b40e36SLinus Torvalds 	task_lock(task);
187006ebb40SStephen Smalley 	retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
1884b105cbbSOleg Nesterov 	task_unlock(task);
1891da177e4SLinus Torvalds 	if (retval)
1904b105cbbSOleg Nesterov 		goto unlock_creds;
1911da177e4SLinus Torvalds 
1924b105cbbSOleg Nesterov 	write_lock_irq(&tasklist_lock);
193b79b7ba9SOleg Nesterov 	retval = -EPERM;
194b79b7ba9SOleg Nesterov 	if (unlikely(task->exit_state))
1954b105cbbSOleg Nesterov 		goto unlock_tasklist;
196f2f0b00aSOleg Nesterov 	if (task->ptrace)
1974b105cbbSOleg Nesterov 		goto unlock_tasklist;
198b79b7ba9SOleg Nesterov 
199f2f0b00aSOleg Nesterov 	task->ptrace = PT_PTRACED;
2001da177e4SLinus Torvalds 	if (capable(CAP_SYS_PTRACE))
2011da177e4SLinus Torvalds 		task->ptrace |= PT_PTRACE_CAP;
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds 	__ptrace_link(task, current);
20433e9fc7dSOleg Nesterov 	send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
205b79b7ba9SOleg Nesterov 
206d79fdd6dSTejun Heo 	spin_lock(&task->sighand->siglock);
207d79fdd6dSTejun Heo 
208d79fdd6dSTejun Heo 	/*
209d79fdd6dSTejun Heo 	 * If the task is already STOPPED, set GROUP_STOP_PENDING and
210d79fdd6dSTejun Heo 	 * TRAPPING, and kick it so that it transits to TRACED.  TRAPPING
211d79fdd6dSTejun Heo 	 * will be cleared if the child completes the transition or any
212d79fdd6dSTejun Heo 	 * event which clears the group stop states happens.  We'll wait
213d79fdd6dSTejun Heo 	 * for the transition to complete before returning from this
214d79fdd6dSTejun Heo 	 * function.
215d79fdd6dSTejun Heo 	 *
216d79fdd6dSTejun Heo 	 * This hides STOPPED -> RUNNING -> TRACED transition from the
217d79fdd6dSTejun Heo 	 * attaching thread but a different thread in the same group can
218d79fdd6dSTejun Heo 	 * still observe the transient RUNNING state.  IOW, if another
219d79fdd6dSTejun Heo 	 * thread's WNOHANG wait(2) on the stopped tracee races against
220d79fdd6dSTejun Heo 	 * ATTACH, the wait(2) may fail due to the transient RUNNING.
221d79fdd6dSTejun Heo 	 *
222d79fdd6dSTejun Heo 	 * The following task_is_stopped() test is safe as both transitions
223d79fdd6dSTejun Heo 	 * in and out of STOPPED are protected by siglock.
224d79fdd6dSTejun Heo 	 */
225d79fdd6dSTejun Heo 	if (task_is_stopped(task)) {
226d79fdd6dSTejun Heo 		task->group_stop |= GROUP_STOP_PENDING | GROUP_STOP_TRAPPING;
227d79fdd6dSTejun Heo 		signal_wake_up(task, 1);
228d79fdd6dSTejun Heo 		wait_trap = true;
229d79fdd6dSTejun Heo 	}
230d79fdd6dSTejun Heo 
231d79fdd6dSTejun Heo 	spin_unlock(&task->sighand->siglock);
232d79fdd6dSTejun Heo 
233b79b7ba9SOleg Nesterov 	retval = 0;
2344b105cbbSOleg Nesterov unlock_tasklist:
2354b105cbbSOleg Nesterov 	write_unlock_irq(&tasklist_lock);
2364b105cbbSOleg Nesterov unlock_creds:
2379b1bf12dSKOSAKI Motohiro 	mutex_unlock(&task->signal->cred_guard_mutex);
238f5b40e36SLinus Torvalds out:
239d79fdd6dSTejun Heo 	if (wait_trap)
240d79fdd6dSTejun Heo 		wait_event(current->signal->wait_chldexit,
241d79fdd6dSTejun Heo 			   !(task->group_stop & GROUP_STOP_TRAPPING));
2421da177e4SLinus Torvalds 	return retval;
2431da177e4SLinus Torvalds }
2441da177e4SLinus Torvalds 
245f2f0b00aSOleg Nesterov /**
246f2f0b00aSOleg Nesterov  * ptrace_traceme  --  helper for PTRACE_TRACEME
247f2f0b00aSOleg Nesterov  *
248f2f0b00aSOleg Nesterov  * Performs checks and sets PT_PTRACED.
249f2f0b00aSOleg Nesterov  * Should be used by all ptrace implementations for PTRACE_TRACEME.
250f2f0b00aSOleg Nesterov  */
251e3e89cc5SLinus Torvalds static int ptrace_traceme(void)
252f2f0b00aSOleg Nesterov {
253f2f0b00aSOleg Nesterov 	int ret = -EPERM;
254f2f0b00aSOleg Nesterov 
2554b105cbbSOleg Nesterov 	write_lock_irq(&tasklist_lock);
2564b105cbbSOleg Nesterov 	/* Are we already being traced? */
257f2f0b00aSOleg Nesterov 	if (!current->ptrace) {
258f2f0b00aSOleg Nesterov 		ret = security_ptrace_traceme(current->parent);
259f2f0b00aSOleg Nesterov 		/*
260f2f0b00aSOleg Nesterov 		 * Check PF_EXITING to ensure ->real_parent has not passed
261f2f0b00aSOleg Nesterov 		 * exit_ptrace(). Otherwise we don't report the error but
262f2f0b00aSOleg Nesterov 		 * pretend ->real_parent untraces us right after return.
263f2f0b00aSOleg Nesterov 		 */
264f2f0b00aSOleg Nesterov 		if (!ret && !(current->real_parent->flags & PF_EXITING)) {
265f2f0b00aSOleg Nesterov 			current->ptrace = PT_PTRACED;
266f2f0b00aSOleg Nesterov 			__ptrace_link(current, current->real_parent);
267f2f0b00aSOleg Nesterov 		}
268f2f0b00aSOleg Nesterov 	}
2694b105cbbSOleg Nesterov 	write_unlock_irq(&tasklist_lock);
2704b105cbbSOleg Nesterov 
271f2f0b00aSOleg Nesterov 	return ret;
272f2f0b00aSOleg Nesterov }
273f2f0b00aSOleg Nesterov 
27439c626aeSOleg Nesterov /*
27539c626aeSOleg Nesterov  * Called with irqs disabled, returns true if childs should reap themselves.
27639c626aeSOleg Nesterov  */
27739c626aeSOleg Nesterov static int ignoring_children(struct sighand_struct *sigh)
27839c626aeSOleg Nesterov {
27939c626aeSOleg Nesterov 	int ret;
28039c626aeSOleg Nesterov 	spin_lock(&sigh->siglock);
28139c626aeSOleg Nesterov 	ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) ||
28239c626aeSOleg Nesterov 	      (sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT);
28339c626aeSOleg Nesterov 	spin_unlock(&sigh->siglock);
28439c626aeSOleg Nesterov 	return ret;
28539c626aeSOleg Nesterov }
28639c626aeSOleg Nesterov 
28739c626aeSOleg Nesterov /*
28839c626aeSOleg Nesterov  * Called with tasklist_lock held for writing.
28939c626aeSOleg Nesterov  * Unlink a traced task, and clean it up if it was a traced zombie.
29039c626aeSOleg Nesterov  * Return true if it needs to be reaped with release_task().
29139c626aeSOleg Nesterov  * (We can't call release_task() here because we already hold tasklist_lock.)
29239c626aeSOleg Nesterov  *
29339c626aeSOleg Nesterov  * If it's a zombie, our attachedness prevented normal parent notification
29439c626aeSOleg Nesterov  * or self-reaping.  Do notification now if it would have happened earlier.
29539c626aeSOleg Nesterov  * If it should reap itself, return true.
29639c626aeSOleg Nesterov  *
297a7f0765eSOleg Nesterov  * If it's our own child, there is no notification to do. But if our normal
298a7f0765eSOleg Nesterov  * children self-reap, then this child was prevented by ptrace and we must
299a7f0765eSOleg Nesterov  * reap it now, in that case we must also wake up sub-threads sleeping in
300a7f0765eSOleg Nesterov  * do_wait().
30139c626aeSOleg Nesterov  */
30239c626aeSOleg Nesterov static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
30339c626aeSOleg Nesterov {
30439c626aeSOleg Nesterov 	__ptrace_unlink(p);
30539c626aeSOleg Nesterov 
30639c626aeSOleg Nesterov 	if (p->exit_state == EXIT_ZOMBIE) {
30739c626aeSOleg Nesterov 		if (!task_detached(p) && thread_group_empty(p)) {
30839c626aeSOleg Nesterov 			if (!same_thread_group(p->real_parent, tracer))
30939c626aeSOleg Nesterov 				do_notify_parent(p, p->exit_signal);
310a7f0765eSOleg Nesterov 			else if (ignoring_children(tracer->sighand)) {
311a7f0765eSOleg Nesterov 				__wake_up_parent(p, tracer);
31239c626aeSOleg Nesterov 				p->exit_signal = -1;
31339c626aeSOleg Nesterov 			}
314a7f0765eSOleg Nesterov 		}
31539c626aeSOleg Nesterov 		if (task_detached(p)) {
31639c626aeSOleg Nesterov 			/* Mark it as in the process of being reaped. */
31739c626aeSOleg Nesterov 			p->exit_state = EXIT_DEAD;
31839c626aeSOleg Nesterov 			return true;
31939c626aeSOleg Nesterov 		}
32039c626aeSOleg Nesterov 	}
32139c626aeSOleg Nesterov 
32239c626aeSOleg Nesterov 	return false;
32339c626aeSOleg Nesterov }
32439c626aeSOleg Nesterov 
325e3e89cc5SLinus Torvalds static int ptrace_detach(struct task_struct *child, unsigned int data)
3261da177e4SLinus Torvalds {
32739c626aeSOleg Nesterov 	bool dead = false;
3284576145cSOleg Nesterov 
3297ed20e1aSJesper Juhl 	if (!valid_signal(data))
3301da177e4SLinus Torvalds 		return -EIO;
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 	/* Architecture-specific hardware disable .. */
3331da177e4SLinus Torvalds 	ptrace_disable(child);
3347d941432SRoland McGrath 	clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
3351da177e4SLinus Torvalds 
33695c3eb76SOleg Nesterov 	write_lock_irq(&tasklist_lock);
33739c626aeSOleg Nesterov 	/*
33839c626aeSOleg Nesterov 	 * This child can be already killed. Make sure de_thread() or
33939c626aeSOleg Nesterov 	 * our sub-thread doing do_wait() didn't do release_task() yet.
34039c626aeSOleg Nesterov 	 */
34195c3eb76SOleg Nesterov 	if (child->ptrace) {
34295c3eb76SOleg Nesterov 		child->exit_code = data;
3434576145cSOleg Nesterov 		dead = __ptrace_detach(current, child);
34495c3eb76SOleg Nesterov 	}
3451da177e4SLinus Torvalds 	write_unlock_irq(&tasklist_lock);
3461da177e4SLinus Torvalds 
3474576145cSOleg Nesterov 	if (unlikely(dead))
3484576145cSOleg Nesterov 		release_task(child);
3494576145cSOleg Nesterov 
3501da177e4SLinus Torvalds 	return 0;
3511da177e4SLinus Torvalds }
3521da177e4SLinus Torvalds 
35339c626aeSOleg Nesterov /*
354c7e49c14SOleg Nesterov  * Detach all tasks we were using ptrace on. Called with tasklist held
355c7e49c14SOleg Nesterov  * for writing, and returns with it held too. But note it can release
356c7e49c14SOleg Nesterov  * and reacquire the lock.
35739c626aeSOleg Nesterov  */
35839c626aeSOleg Nesterov void exit_ptrace(struct task_struct *tracer)
359c4b5ed25SNamhyung Kim 	__releases(&tasklist_lock)
360c4b5ed25SNamhyung Kim 	__acquires(&tasklist_lock)
36139c626aeSOleg Nesterov {
36239c626aeSOleg Nesterov 	struct task_struct *p, *n;
36339c626aeSOleg Nesterov 	LIST_HEAD(ptrace_dead);
36439c626aeSOleg Nesterov 
365c7e49c14SOleg Nesterov 	if (likely(list_empty(&tracer->ptraced)))
366c7e49c14SOleg Nesterov 		return;
367c7e49c14SOleg Nesterov 
36839c626aeSOleg Nesterov 	list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) {
36939c626aeSOleg Nesterov 		if (__ptrace_detach(tracer, p))
37039c626aeSOleg Nesterov 			list_add(&p->ptrace_entry, &ptrace_dead);
37139c626aeSOleg Nesterov 	}
37239c626aeSOleg Nesterov 
373c7e49c14SOleg Nesterov 	write_unlock_irq(&tasklist_lock);
37439c626aeSOleg Nesterov 	BUG_ON(!list_empty(&tracer->ptraced));
37539c626aeSOleg Nesterov 
37639c626aeSOleg Nesterov 	list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) {
37739c626aeSOleg Nesterov 		list_del_init(&p->ptrace_entry);
37839c626aeSOleg Nesterov 		release_task(p);
37939c626aeSOleg Nesterov 	}
380c7e49c14SOleg Nesterov 
381c7e49c14SOleg Nesterov 	write_lock_irq(&tasklist_lock);
38239c626aeSOleg Nesterov }
38339c626aeSOleg Nesterov 
3841da177e4SLinus Torvalds int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
3851da177e4SLinus Torvalds {
3861da177e4SLinus Torvalds 	int copied = 0;
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 	while (len > 0) {
3891da177e4SLinus Torvalds 		char buf[128];
3901da177e4SLinus Torvalds 		int this_len, retval;
3911da177e4SLinus Torvalds 
3921da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
3931da177e4SLinus Torvalds 		retval = access_process_vm(tsk, src, buf, this_len, 0);
3941da177e4SLinus Torvalds 		if (!retval) {
3951da177e4SLinus Torvalds 			if (copied)
3961da177e4SLinus Torvalds 				break;
3971da177e4SLinus Torvalds 			return -EIO;
3981da177e4SLinus Torvalds 		}
3991da177e4SLinus Torvalds 		if (copy_to_user(dst, buf, retval))
4001da177e4SLinus Torvalds 			return -EFAULT;
4011da177e4SLinus Torvalds 		copied += retval;
4021da177e4SLinus Torvalds 		src += retval;
4031da177e4SLinus Torvalds 		dst += retval;
4041da177e4SLinus Torvalds 		len -= retval;
4051da177e4SLinus Torvalds 	}
4061da177e4SLinus Torvalds 	return copied;
4071da177e4SLinus Torvalds }
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
4101da177e4SLinus Torvalds {
4111da177e4SLinus Torvalds 	int copied = 0;
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 	while (len > 0) {
4141da177e4SLinus Torvalds 		char buf[128];
4151da177e4SLinus Torvalds 		int this_len, retval;
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
4181da177e4SLinus Torvalds 		if (copy_from_user(buf, src, this_len))
4191da177e4SLinus Torvalds 			return -EFAULT;
4201da177e4SLinus Torvalds 		retval = access_process_vm(tsk, dst, buf, this_len, 1);
4211da177e4SLinus Torvalds 		if (!retval) {
4221da177e4SLinus Torvalds 			if (copied)
4231da177e4SLinus Torvalds 				break;
4241da177e4SLinus Torvalds 			return -EIO;
4251da177e4SLinus Torvalds 		}
4261da177e4SLinus Torvalds 		copied += retval;
4271da177e4SLinus Torvalds 		src += retval;
4281da177e4SLinus Torvalds 		dst += retval;
4291da177e4SLinus Torvalds 		len -= retval;
4301da177e4SLinus Torvalds 	}
4311da177e4SLinus Torvalds 	return copied;
4321da177e4SLinus Torvalds }
4331da177e4SLinus Torvalds 
4344abf9869SNamhyung Kim static int ptrace_setoptions(struct task_struct *child, unsigned long data)
4351da177e4SLinus Torvalds {
4361da177e4SLinus Torvalds 	child->ptrace &= ~PT_TRACE_MASK;
4371da177e4SLinus Torvalds 
4381da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACESYSGOOD)
4391da177e4SLinus Torvalds 		child->ptrace |= PT_TRACESYSGOOD;
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEFORK)
4421da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_FORK;
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORK)
4451da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK;
4461da177e4SLinus Torvalds 
4471da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACECLONE)
4481da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_CLONE;
4491da177e4SLinus Torvalds 
4501da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXEC)
4511da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXEC;
4521da177e4SLinus Torvalds 
4531da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORKDONE)
4541da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK_DONE;
4551da177e4SLinus Torvalds 
4561da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXIT)
4571da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXIT;
4581da177e4SLinus Torvalds 
4591da177e4SLinus Torvalds 	return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
4601da177e4SLinus Torvalds }
4611da177e4SLinus Torvalds 
462e16b2781SRoland McGrath static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info)
4631da177e4SLinus Torvalds {
464e4961254SOleg Nesterov 	unsigned long flags;
4651da177e4SLinus Torvalds 	int error = -ESRCH;
4661da177e4SLinus Torvalds 
467e4961254SOleg Nesterov 	if (lock_task_sighand(child, &flags)) {
4681da177e4SLinus Torvalds 		error = -EINVAL;
4691da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
470e16b2781SRoland McGrath 			*info = *child->last_siginfo;
4711da177e4SLinus Torvalds 			error = 0;
4721da177e4SLinus Torvalds 		}
473e4961254SOleg Nesterov 		unlock_task_sighand(child, &flags);
4741da177e4SLinus Torvalds 	}
4751da177e4SLinus Torvalds 	return error;
4761da177e4SLinus Torvalds }
4771da177e4SLinus Torvalds 
478e16b2781SRoland McGrath static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info)
4791da177e4SLinus Torvalds {
480e4961254SOleg Nesterov 	unsigned long flags;
4811da177e4SLinus Torvalds 	int error = -ESRCH;
4821da177e4SLinus Torvalds 
483e4961254SOleg Nesterov 	if (lock_task_sighand(child, &flags)) {
4841da177e4SLinus Torvalds 		error = -EINVAL;
4851da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
486e16b2781SRoland McGrath 			*child->last_siginfo = *info;
4871da177e4SLinus Torvalds 			error = 0;
4881da177e4SLinus Torvalds 		}
489e4961254SOleg Nesterov 		unlock_task_sighand(child, &flags);
4901da177e4SLinus Torvalds 	}
4911da177e4SLinus Torvalds 	return error;
4921da177e4SLinus Torvalds }
4931da177e4SLinus Torvalds 
49436df29d7SRoland McGrath 
49536df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
49636df29d7SRoland McGrath #define is_singlestep(request)		((request) == PTRACE_SINGLESTEP)
49736df29d7SRoland McGrath #else
49836df29d7SRoland McGrath #define is_singlestep(request)		0
49936df29d7SRoland McGrath #endif
50036df29d7SRoland McGrath 
5015b88abbfSRoland McGrath #ifdef PTRACE_SINGLEBLOCK
5025b88abbfSRoland McGrath #define is_singleblock(request)		((request) == PTRACE_SINGLEBLOCK)
5035b88abbfSRoland McGrath #else
5045b88abbfSRoland McGrath #define is_singleblock(request)		0
5055b88abbfSRoland McGrath #endif
5065b88abbfSRoland McGrath 
50736df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
50836df29d7SRoland McGrath #define is_sysemu_singlestep(request)	((request) == PTRACE_SYSEMU_SINGLESTEP)
50936df29d7SRoland McGrath #else
51036df29d7SRoland McGrath #define is_sysemu_singlestep(request)	0
51136df29d7SRoland McGrath #endif
51236df29d7SRoland McGrath 
5134abf9869SNamhyung Kim static int ptrace_resume(struct task_struct *child, long request,
5144abf9869SNamhyung Kim 			 unsigned long data)
51536df29d7SRoland McGrath {
51636df29d7SRoland McGrath 	if (!valid_signal(data))
51736df29d7SRoland McGrath 		return -EIO;
51836df29d7SRoland McGrath 
51936df29d7SRoland McGrath 	if (request == PTRACE_SYSCALL)
52036df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
52136df29d7SRoland McGrath 	else
52236df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
52336df29d7SRoland McGrath 
52436df29d7SRoland McGrath #ifdef TIF_SYSCALL_EMU
52536df29d7SRoland McGrath 	if (request == PTRACE_SYSEMU || request == PTRACE_SYSEMU_SINGLESTEP)
52636df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_EMU);
52736df29d7SRoland McGrath 	else
52836df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
52936df29d7SRoland McGrath #endif
53036df29d7SRoland McGrath 
5315b88abbfSRoland McGrath 	if (is_singleblock(request)) {
5325b88abbfSRoland McGrath 		if (unlikely(!arch_has_block_step()))
5335b88abbfSRoland McGrath 			return -EIO;
5345b88abbfSRoland McGrath 		user_enable_block_step(child);
5355b88abbfSRoland McGrath 	} else if (is_singlestep(request) || is_sysemu_singlestep(request)) {
53636df29d7SRoland McGrath 		if (unlikely(!arch_has_single_step()))
53736df29d7SRoland McGrath 			return -EIO;
53836df29d7SRoland McGrath 		user_enable_single_step(child);
5393a709703SRoland McGrath 	} else {
54036df29d7SRoland McGrath 		user_disable_single_step(child);
5413a709703SRoland McGrath 	}
54236df29d7SRoland McGrath 
54336df29d7SRoland McGrath 	child->exit_code = data;
54436df29d7SRoland McGrath 	wake_up_process(child);
54536df29d7SRoland McGrath 
54636df29d7SRoland McGrath 	return 0;
54736df29d7SRoland McGrath }
54836df29d7SRoland McGrath 
5492225a122SSuresh Siddha #ifdef CONFIG_HAVE_ARCH_TRACEHOOK
5502225a122SSuresh Siddha 
5512225a122SSuresh Siddha static const struct user_regset *
5522225a122SSuresh Siddha find_regset(const struct user_regset_view *view, unsigned int type)
5532225a122SSuresh Siddha {
5542225a122SSuresh Siddha 	const struct user_regset *regset;
5552225a122SSuresh Siddha 	int n;
5562225a122SSuresh Siddha 
5572225a122SSuresh Siddha 	for (n = 0; n < view->n; ++n) {
5582225a122SSuresh Siddha 		regset = view->regsets + n;
5592225a122SSuresh Siddha 		if (regset->core_note_type == type)
5602225a122SSuresh Siddha 			return regset;
5612225a122SSuresh Siddha 	}
5622225a122SSuresh Siddha 
5632225a122SSuresh Siddha 	return NULL;
5642225a122SSuresh Siddha }
5652225a122SSuresh Siddha 
5662225a122SSuresh Siddha static int ptrace_regset(struct task_struct *task, int req, unsigned int type,
5672225a122SSuresh Siddha 			 struct iovec *kiov)
5682225a122SSuresh Siddha {
5692225a122SSuresh Siddha 	const struct user_regset_view *view = task_user_regset_view(task);
5702225a122SSuresh Siddha 	const struct user_regset *regset = find_regset(view, type);
5712225a122SSuresh Siddha 	int regset_no;
5722225a122SSuresh Siddha 
5732225a122SSuresh Siddha 	if (!regset || (kiov->iov_len % regset->size) != 0)
574c6a0dd7eSSuresh Siddha 		return -EINVAL;
5752225a122SSuresh Siddha 
5762225a122SSuresh Siddha 	regset_no = regset - view->regsets;
5772225a122SSuresh Siddha 	kiov->iov_len = min(kiov->iov_len,
5782225a122SSuresh Siddha 			    (__kernel_size_t) (regset->n * regset->size));
5792225a122SSuresh Siddha 
5802225a122SSuresh Siddha 	if (req == PTRACE_GETREGSET)
5812225a122SSuresh Siddha 		return copy_regset_to_user(task, view, regset_no, 0,
5822225a122SSuresh Siddha 					   kiov->iov_len, kiov->iov_base);
5832225a122SSuresh Siddha 	else
5842225a122SSuresh Siddha 		return copy_regset_from_user(task, view, regset_no, 0,
5852225a122SSuresh Siddha 					     kiov->iov_len, kiov->iov_base);
5862225a122SSuresh Siddha }
5872225a122SSuresh Siddha 
5882225a122SSuresh Siddha #endif
5892225a122SSuresh Siddha 
5901da177e4SLinus Torvalds int ptrace_request(struct task_struct *child, long request,
5914abf9869SNamhyung Kim 		   unsigned long addr, unsigned long data)
5921da177e4SLinus Torvalds {
5931da177e4SLinus Torvalds 	int ret = -EIO;
594e16b2781SRoland McGrath 	siginfo_t siginfo;
5959fed81dcSNamhyung Kim 	void __user *datavp = (void __user *) data;
5969fed81dcSNamhyung Kim 	unsigned long __user *datalp = datavp;
5971da177e4SLinus Torvalds 
5981da177e4SLinus Torvalds 	switch (request) {
59916c3e389SRoland McGrath 	case PTRACE_PEEKTEXT:
60016c3e389SRoland McGrath 	case PTRACE_PEEKDATA:
60116c3e389SRoland McGrath 		return generic_ptrace_peekdata(child, addr, data);
60216c3e389SRoland McGrath 	case PTRACE_POKETEXT:
60316c3e389SRoland McGrath 	case PTRACE_POKEDATA:
60416c3e389SRoland McGrath 		return generic_ptrace_pokedata(child, addr, data);
60516c3e389SRoland McGrath 
6061da177e4SLinus Torvalds #ifdef PTRACE_OLDSETOPTIONS
6071da177e4SLinus Torvalds 	case PTRACE_OLDSETOPTIONS:
6081da177e4SLinus Torvalds #endif
6091da177e4SLinus Torvalds 	case PTRACE_SETOPTIONS:
6101da177e4SLinus Torvalds 		ret = ptrace_setoptions(child, data);
6111da177e4SLinus Torvalds 		break;
6121da177e4SLinus Torvalds 	case PTRACE_GETEVENTMSG:
6139fed81dcSNamhyung Kim 		ret = put_user(child->ptrace_message, datalp);
6141da177e4SLinus Torvalds 		break;
615e16b2781SRoland McGrath 
6161da177e4SLinus Torvalds 	case PTRACE_GETSIGINFO:
617e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
618e16b2781SRoland McGrath 		if (!ret)
6199fed81dcSNamhyung Kim 			ret = copy_siginfo_to_user(datavp, &siginfo);
6201da177e4SLinus Torvalds 		break;
621e16b2781SRoland McGrath 
6221da177e4SLinus Torvalds 	case PTRACE_SETSIGINFO:
6239fed81dcSNamhyung Kim 		if (copy_from_user(&siginfo, datavp, sizeof siginfo))
624e16b2781SRoland McGrath 			ret = -EFAULT;
625e16b2781SRoland McGrath 		else
626e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
6271da177e4SLinus Torvalds 		break;
628e16b2781SRoland McGrath 
6291bcf5482SAlexey Dobriyan 	case PTRACE_DETACH:	 /* detach a process that was attached. */
6301bcf5482SAlexey Dobriyan 		ret = ptrace_detach(child, data);
6311bcf5482SAlexey Dobriyan 		break;
63236df29d7SRoland McGrath 
6339c1a1259SMike Frysinger #ifdef CONFIG_BINFMT_ELF_FDPIC
6349c1a1259SMike Frysinger 	case PTRACE_GETFDPIC: {
635e0129ef9SOleg Nesterov 		struct mm_struct *mm = get_task_mm(child);
6369c1a1259SMike Frysinger 		unsigned long tmp = 0;
6379c1a1259SMike Frysinger 
638e0129ef9SOleg Nesterov 		ret = -ESRCH;
639e0129ef9SOleg Nesterov 		if (!mm)
640e0129ef9SOleg Nesterov 			break;
641e0129ef9SOleg Nesterov 
6429c1a1259SMike Frysinger 		switch (addr) {
6439c1a1259SMike Frysinger 		case PTRACE_GETFDPIC_EXEC:
644e0129ef9SOleg Nesterov 			tmp = mm->context.exec_fdpic_loadmap;
6459c1a1259SMike Frysinger 			break;
6469c1a1259SMike Frysinger 		case PTRACE_GETFDPIC_INTERP:
647e0129ef9SOleg Nesterov 			tmp = mm->context.interp_fdpic_loadmap;
6489c1a1259SMike Frysinger 			break;
6499c1a1259SMike Frysinger 		default:
6509c1a1259SMike Frysinger 			break;
6519c1a1259SMike Frysinger 		}
652e0129ef9SOleg Nesterov 		mmput(mm);
6539c1a1259SMike Frysinger 
6549fed81dcSNamhyung Kim 		ret = put_user(tmp, datalp);
6559c1a1259SMike Frysinger 		break;
6569c1a1259SMike Frysinger 	}
6579c1a1259SMike Frysinger #endif
6589c1a1259SMike Frysinger 
65936df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
66036df29d7SRoland McGrath 	case PTRACE_SINGLESTEP:
66136df29d7SRoland McGrath #endif
6625b88abbfSRoland McGrath #ifdef PTRACE_SINGLEBLOCK
6635b88abbfSRoland McGrath 	case PTRACE_SINGLEBLOCK:
6645b88abbfSRoland McGrath #endif
66536df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
66636df29d7SRoland McGrath 	case PTRACE_SYSEMU:
66736df29d7SRoland McGrath 	case PTRACE_SYSEMU_SINGLESTEP:
66836df29d7SRoland McGrath #endif
66936df29d7SRoland McGrath 	case PTRACE_SYSCALL:
67036df29d7SRoland McGrath 	case PTRACE_CONT:
67136df29d7SRoland McGrath 		return ptrace_resume(child, request, data);
67236df29d7SRoland McGrath 
67336df29d7SRoland McGrath 	case PTRACE_KILL:
67436df29d7SRoland McGrath 		if (child->exit_state)	/* already dead */
67536df29d7SRoland McGrath 			return 0;
67636df29d7SRoland McGrath 		return ptrace_resume(child, request, SIGKILL);
67736df29d7SRoland McGrath 
6782225a122SSuresh Siddha #ifdef CONFIG_HAVE_ARCH_TRACEHOOK
6792225a122SSuresh Siddha 	case PTRACE_GETREGSET:
6802225a122SSuresh Siddha 	case PTRACE_SETREGSET:
6812225a122SSuresh Siddha 	{
6822225a122SSuresh Siddha 		struct iovec kiov;
6839fed81dcSNamhyung Kim 		struct iovec __user *uiov = datavp;
6842225a122SSuresh Siddha 
6852225a122SSuresh Siddha 		if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov)))
6862225a122SSuresh Siddha 			return -EFAULT;
6872225a122SSuresh Siddha 
6882225a122SSuresh Siddha 		if (__get_user(kiov.iov_base, &uiov->iov_base) ||
6892225a122SSuresh Siddha 		    __get_user(kiov.iov_len, &uiov->iov_len))
6902225a122SSuresh Siddha 			return -EFAULT;
6912225a122SSuresh Siddha 
6922225a122SSuresh Siddha 		ret = ptrace_regset(child, request, addr, &kiov);
6932225a122SSuresh Siddha 		if (!ret)
6942225a122SSuresh Siddha 			ret = __put_user(kiov.iov_len, &uiov->iov_len);
6952225a122SSuresh Siddha 		break;
6962225a122SSuresh Siddha 	}
6972225a122SSuresh Siddha #endif
6981da177e4SLinus Torvalds 	default:
6991da177e4SLinus Torvalds 		break;
7001da177e4SLinus Torvalds 	}
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	return ret;
7031da177e4SLinus Torvalds }
704481bed45SChristoph Hellwig 
7058053bdd5SOleg Nesterov static struct task_struct *ptrace_get_task_struct(pid_t pid)
7066b9c7ed8SChristoph Hellwig {
7076b9c7ed8SChristoph Hellwig 	struct task_struct *child;
7086b9c7ed8SChristoph Hellwig 
7098053bdd5SOleg Nesterov 	rcu_read_lock();
710228ebcbeSPavel Emelyanov 	child = find_task_by_vpid(pid);
711481bed45SChristoph Hellwig 	if (child)
712481bed45SChristoph Hellwig 		get_task_struct(child);
7138053bdd5SOleg Nesterov 	rcu_read_unlock();
714f400e198SSukadev Bhattiprolu 
715481bed45SChristoph Hellwig 	if (!child)
7166b9c7ed8SChristoph Hellwig 		return ERR_PTR(-ESRCH);
7176b9c7ed8SChristoph Hellwig 	return child;
718481bed45SChristoph Hellwig }
719481bed45SChristoph Hellwig 
7200ac15559SChristoph Hellwig #ifndef arch_ptrace_attach
7210ac15559SChristoph Hellwig #define arch_ptrace_attach(child)	do { } while (0)
7220ac15559SChristoph Hellwig #endif
7230ac15559SChristoph Hellwig 
7244abf9869SNamhyung Kim SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr,
7254abf9869SNamhyung Kim 		unsigned long, data)
726481bed45SChristoph Hellwig {
727481bed45SChristoph Hellwig 	struct task_struct *child;
728481bed45SChristoph Hellwig 	long ret;
729481bed45SChristoph Hellwig 
7306b9c7ed8SChristoph Hellwig 	if (request == PTRACE_TRACEME) {
7316b9c7ed8SChristoph Hellwig 		ret = ptrace_traceme();
7326ea6dd93SHaavard Skinnemoen 		if (!ret)
7336ea6dd93SHaavard Skinnemoen 			arch_ptrace_attach(current);
734481bed45SChristoph Hellwig 		goto out;
7356b9c7ed8SChristoph Hellwig 	}
7366b9c7ed8SChristoph Hellwig 
7376b9c7ed8SChristoph Hellwig 	child = ptrace_get_task_struct(pid);
7386b9c7ed8SChristoph Hellwig 	if (IS_ERR(child)) {
7396b9c7ed8SChristoph Hellwig 		ret = PTR_ERR(child);
7406b9c7ed8SChristoph Hellwig 		goto out;
7416b9c7ed8SChristoph Hellwig 	}
742481bed45SChristoph Hellwig 
743481bed45SChristoph Hellwig 	if (request == PTRACE_ATTACH) {
744481bed45SChristoph Hellwig 		ret = ptrace_attach(child);
7450ac15559SChristoph Hellwig 		/*
7460ac15559SChristoph Hellwig 		 * Some architectures need to do book-keeping after
7470ac15559SChristoph Hellwig 		 * a ptrace attach.
7480ac15559SChristoph Hellwig 		 */
7490ac15559SChristoph Hellwig 		if (!ret)
7500ac15559SChristoph Hellwig 			arch_ptrace_attach(child);
751005f18dfSChristoph Hellwig 		goto out_put_task_struct;
752481bed45SChristoph Hellwig 	}
753481bed45SChristoph Hellwig 
754481bed45SChristoph Hellwig 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
755481bed45SChristoph Hellwig 	if (ret < 0)
756481bed45SChristoph Hellwig 		goto out_put_task_struct;
757481bed45SChristoph Hellwig 
758481bed45SChristoph Hellwig 	ret = arch_ptrace(child, request, addr, data);
759481bed45SChristoph Hellwig 
760481bed45SChristoph Hellwig  out_put_task_struct:
761481bed45SChristoph Hellwig 	put_task_struct(child);
762481bed45SChristoph Hellwig  out:
763481bed45SChristoph Hellwig 	return ret;
764481bed45SChristoph Hellwig }
76576647323SAlexey Dobriyan 
7664abf9869SNamhyung Kim int generic_ptrace_peekdata(struct task_struct *tsk, unsigned long addr,
7674abf9869SNamhyung Kim 			    unsigned long data)
76876647323SAlexey Dobriyan {
76976647323SAlexey Dobriyan 	unsigned long tmp;
77076647323SAlexey Dobriyan 	int copied;
77176647323SAlexey Dobriyan 
77276647323SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
77376647323SAlexey Dobriyan 	if (copied != sizeof(tmp))
77476647323SAlexey Dobriyan 		return -EIO;
77576647323SAlexey Dobriyan 	return put_user(tmp, (unsigned long __user *)data);
77676647323SAlexey Dobriyan }
777f284ce72SAlexey Dobriyan 
7784abf9869SNamhyung Kim int generic_ptrace_pokedata(struct task_struct *tsk, unsigned long addr,
7794abf9869SNamhyung Kim 			    unsigned long data)
780f284ce72SAlexey Dobriyan {
781f284ce72SAlexey Dobriyan 	int copied;
782f284ce72SAlexey Dobriyan 
783f284ce72SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
784f284ce72SAlexey Dobriyan 	return (copied == sizeof(data)) ? 0 : -EIO;
785f284ce72SAlexey Dobriyan }
786032d82d9SRoland McGrath 
78796b8936aSChristoph Hellwig #if defined CONFIG_COMPAT
788032d82d9SRoland McGrath #include <linux/compat.h>
789032d82d9SRoland McGrath 
790032d82d9SRoland McGrath int compat_ptrace_request(struct task_struct *child, compat_long_t request,
791032d82d9SRoland McGrath 			  compat_ulong_t addr, compat_ulong_t data)
792032d82d9SRoland McGrath {
793032d82d9SRoland McGrath 	compat_ulong_t __user *datap = compat_ptr(data);
794032d82d9SRoland McGrath 	compat_ulong_t word;
795e16b2781SRoland McGrath 	siginfo_t siginfo;
796032d82d9SRoland McGrath 	int ret;
797032d82d9SRoland McGrath 
798032d82d9SRoland McGrath 	switch (request) {
799032d82d9SRoland McGrath 	case PTRACE_PEEKTEXT:
800032d82d9SRoland McGrath 	case PTRACE_PEEKDATA:
801032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &word, sizeof(word), 0);
802032d82d9SRoland McGrath 		if (ret != sizeof(word))
803032d82d9SRoland McGrath 			ret = -EIO;
804032d82d9SRoland McGrath 		else
805032d82d9SRoland McGrath 			ret = put_user(word, datap);
806032d82d9SRoland McGrath 		break;
807032d82d9SRoland McGrath 
808032d82d9SRoland McGrath 	case PTRACE_POKETEXT:
809032d82d9SRoland McGrath 	case PTRACE_POKEDATA:
810032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &data, sizeof(data), 1);
811032d82d9SRoland McGrath 		ret = (ret != sizeof(data) ? -EIO : 0);
812032d82d9SRoland McGrath 		break;
813032d82d9SRoland McGrath 
814032d82d9SRoland McGrath 	case PTRACE_GETEVENTMSG:
815032d82d9SRoland McGrath 		ret = put_user((compat_ulong_t) child->ptrace_message, datap);
816032d82d9SRoland McGrath 		break;
817032d82d9SRoland McGrath 
818e16b2781SRoland McGrath 	case PTRACE_GETSIGINFO:
819e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
820e16b2781SRoland McGrath 		if (!ret)
821e16b2781SRoland McGrath 			ret = copy_siginfo_to_user32(
822e16b2781SRoland McGrath 				(struct compat_siginfo __user *) datap,
823e16b2781SRoland McGrath 				&siginfo);
824e16b2781SRoland McGrath 		break;
825e16b2781SRoland McGrath 
826e16b2781SRoland McGrath 	case PTRACE_SETSIGINFO:
827e16b2781SRoland McGrath 		memset(&siginfo, 0, sizeof siginfo);
828e16b2781SRoland McGrath 		if (copy_siginfo_from_user32(
829e16b2781SRoland McGrath 			    &siginfo, (struct compat_siginfo __user *) datap))
830e16b2781SRoland McGrath 			ret = -EFAULT;
831e16b2781SRoland McGrath 		else
832e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
833e16b2781SRoland McGrath 		break;
8342225a122SSuresh Siddha #ifdef CONFIG_HAVE_ARCH_TRACEHOOK
8352225a122SSuresh Siddha 	case PTRACE_GETREGSET:
8362225a122SSuresh Siddha 	case PTRACE_SETREGSET:
8372225a122SSuresh Siddha 	{
8382225a122SSuresh Siddha 		struct iovec kiov;
8392225a122SSuresh Siddha 		struct compat_iovec __user *uiov =
8402225a122SSuresh Siddha 			(struct compat_iovec __user *) datap;
8412225a122SSuresh Siddha 		compat_uptr_t ptr;
8422225a122SSuresh Siddha 		compat_size_t len;
8432225a122SSuresh Siddha 
8442225a122SSuresh Siddha 		if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov)))
8452225a122SSuresh Siddha 			return -EFAULT;
8462225a122SSuresh Siddha 
8472225a122SSuresh Siddha 		if (__get_user(ptr, &uiov->iov_base) ||
8482225a122SSuresh Siddha 		    __get_user(len, &uiov->iov_len))
8492225a122SSuresh Siddha 			return -EFAULT;
8502225a122SSuresh Siddha 
8512225a122SSuresh Siddha 		kiov.iov_base = compat_ptr(ptr);
8522225a122SSuresh Siddha 		kiov.iov_len = len;
8532225a122SSuresh Siddha 
8542225a122SSuresh Siddha 		ret = ptrace_regset(child, request, addr, &kiov);
8552225a122SSuresh Siddha 		if (!ret)
8562225a122SSuresh Siddha 			ret = __put_user(kiov.iov_len, &uiov->iov_len);
8572225a122SSuresh Siddha 		break;
8582225a122SSuresh Siddha 	}
8592225a122SSuresh Siddha #endif
860e16b2781SRoland McGrath 
861032d82d9SRoland McGrath 	default:
862032d82d9SRoland McGrath 		ret = ptrace_request(child, request, addr, data);
863032d82d9SRoland McGrath 	}
864032d82d9SRoland McGrath 
865032d82d9SRoland McGrath 	return ret;
866032d82d9SRoland McGrath }
867c269f196SRoland McGrath 
868c269f196SRoland McGrath asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
869c269f196SRoland McGrath 				  compat_long_t addr, compat_long_t data)
870c269f196SRoland McGrath {
871c269f196SRoland McGrath 	struct task_struct *child;
872c269f196SRoland McGrath 	long ret;
873c269f196SRoland McGrath 
874c269f196SRoland McGrath 	if (request == PTRACE_TRACEME) {
875c269f196SRoland McGrath 		ret = ptrace_traceme();
876c269f196SRoland McGrath 		goto out;
877c269f196SRoland McGrath 	}
878c269f196SRoland McGrath 
879c269f196SRoland McGrath 	child = ptrace_get_task_struct(pid);
880c269f196SRoland McGrath 	if (IS_ERR(child)) {
881c269f196SRoland McGrath 		ret = PTR_ERR(child);
882c269f196SRoland McGrath 		goto out;
883c269f196SRoland McGrath 	}
884c269f196SRoland McGrath 
885c269f196SRoland McGrath 	if (request == PTRACE_ATTACH) {
886c269f196SRoland McGrath 		ret = ptrace_attach(child);
887c269f196SRoland McGrath 		/*
888c269f196SRoland McGrath 		 * Some architectures need to do book-keeping after
889c269f196SRoland McGrath 		 * a ptrace attach.
890c269f196SRoland McGrath 		 */
891c269f196SRoland McGrath 		if (!ret)
892c269f196SRoland McGrath 			arch_ptrace_attach(child);
893c269f196SRoland McGrath 		goto out_put_task_struct;
894c269f196SRoland McGrath 	}
895c269f196SRoland McGrath 
896c269f196SRoland McGrath 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
897c269f196SRoland McGrath 	if (!ret)
898c269f196SRoland McGrath 		ret = compat_arch_ptrace(child, request, addr, data);
899c269f196SRoland McGrath 
900c269f196SRoland McGrath  out_put_task_struct:
901c269f196SRoland McGrath 	put_task_struct(child);
902c269f196SRoland McGrath  out:
903c269f196SRoland McGrath 	return ret;
904c269f196SRoland McGrath }
90596b8936aSChristoph Hellwig #endif	/* CONFIG_COMPAT */
906