xref: /openbmc/linux/kernel/ptrace.c (revision 39c626ae)
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>
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds #include <asm/pgtable.h>
261da177e4SLinus Torvalds #include <asm/uaccess.h>
271da177e4SLinus Torvalds 
28bf53de90SMarkus Metzger 
29bf53de90SMarkus Metzger /*
30bf53de90SMarkus Metzger  * Initialize a new task whose father had been ptraced.
31bf53de90SMarkus Metzger  *
32bf53de90SMarkus Metzger  * Called from copy_process().
33bf53de90SMarkus Metzger  */
34bf53de90SMarkus Metzger void ptrace_fork(struct task_struct *child, unsigned long clone_flags)
35bf53de90SMarkus Metzger {
36bf53de90SMarkus Metzger 	arch_ptrace_fork(child, clone_flags);
37bf53de90SMarkus Metzger }
38bf53de90SMarkus Metzger 
391da177e4SLinus Torvalds /*
401da177e4SLinus Torvalds  * ptrace a task: make the debugger its new parent and
411da177e4SLinus Torvalds  * move it to the ptrace list.
421da177e4SLinus Torvalds  *
431da177e4SLinus Torvalds  * Must be called with the tasklist lock write-held.
441da177e4SLinus Torvalds  */
4536c8b586SIngo Molnar void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
461da177e4SLinus Torvalds {
47f470021aSRoland McGrath 	BUG_ON(!list_empty(&child->ptrace_entry));
48f470021aSRoland McGrath 	list_add(&child->ptrace_entry, &new_parent->ptraced);
491da177e4SLinus Torvalds 	child->parent = new_parent;
501da177e4SLinus Torvalds }
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds /*
531da177e4SLinus Torvalds  * Turn a tracing stop into a normal stop now, since with no tracer there
541da177e4SLinus Torvalds  * would be no way to wake it up with SIGCONT or SIGKILL.  If there was a
551da177e4SLinus Torvalds  * signal sent that would resume the child, but didn't because it was in
561da177e4SLinus Torvalds  * TASK_TRACED, resume it now.
571da177e4SLinus Torvalds  * Requires that irqs be disabled.
581da177e4SLinus Torvalds  */
59b747c8c1SAdrian Bunk static void ptrace_untrace(struct task_struct *child)
601da177e4SLinus Torvalds {
611da177e4SLinus Torvalds 	spin_lock(&child->sighand->siglock);
626618a3e2SMatthew Wilcox 	if (task_is_traced(child)) {
631da177e4SLinus Torvalds 		if (child->signal->flags & SIGNAL_STOP_STOPPED) {
64d9ae90acSOleg Nesterov 			__set_task_state(child, TASK_STOPPED);
651da177e4SLinus Torvalds 		} else {
661da177e4SLinus Torvalds 			signal_wake_up(child, 1);
671da177e4SLinus Torvalds 		}
681da177e4SLinus Torvalds 	}
691da177e4SLinus Torvalds 	spin_unlock(&child->sighand->siglock);
701da177e4SLinus Torvalds }
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds /*
731da177e4SLinus Torvalds  * unptrace a task: move it back to its original parent and
741da177e4SLinus Torvalds  * remove it from the ptrace list.
751da177e4SLinus Torvalds  *
761da177e4SLinus Torvalds  * Must be called with the tasklist lock write-held.
771da177e4SLinus Torvalds  */
7836c8b586SIngo Molnar void __ptrace_unlink(struct task_struct *child)
791da177e4SLinus Torvalds {
805ecfbae0SOleg Nesterov 	BUG_ON(!child->ptrace);
815ecfbae0SOleg Nesterov 
821da177e4SLinus Torvalds 	child->ptrace = 0;
831da177e4SLinus Torvalds 	child->parent = child->real_parent;
84f470021aSRoland McGrath 	list_del_init(&child->ptrace_entry);
851da177e4SLinus Torvalds 
86bf53de90SMarkus Metzger 	arch_ptrace_untrace(child);
876618a3e2SMatthew Wilcox 	if (task_is_traced(child))
881da177e4SLinus Torvalds 		ptrace_untrace(child);
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds /*
921da177e4SLinus Torvalds  * Check that we have indeed attached to the thing..
931da177e4SLinus Torvalds  */
941da177e4SLinus Torvalds int ptrace_check_attach(struct task_struct *child, int kill)
951da177e4SLinus Torvalds {
961da177e4SLinus Torvalds 	int ret = -ESRCH;
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds 	/*
991da177e4SLinus Torvalds 	 * We take the read lock around doing both checks to close a
1001da177e4SLinus Torvalds 	 * possible race where someone else was tracing our child and
1011da177e4SLinus Torvalds 	 * detached between these two checks.  After this locked check,
1021da177e4SLinus Torvalds 	 * we are sure that this is our traced child and that can only
1031da177e4SLinus Torvalds 	 * be changed by us so it's not changing right after this.
1041da177e4SLinus Torvalds 	 */
1051da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
106c0c0b649SOleg Nesterov 	if ((child->ptrace & PT_PTRACED) && child->parent == current) {
1071da177e4SLinus Torvalds 		ret = 0;
108c0c0b649SOleg Nesterov 		/*
109c0c0b649SOleg Nesterov 		 * child->sighand can't be NULL, release_task()
110c0c0b649SOleg Nesterov 		 * does ptrace_unlink() before __exit_signal().
111c0c0b649SOleg Nesterov 		 */
1121da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
113d9ae90acSOleg Nesterov 		if (task_is_stopped(child))
1141da177e4SLinus Torvalds 			child->state = TASK_TRACED;
115d9ae90acSOleg Nesterov 		else if (!task_is_traced(child) && !kill)
1161da177e4SLinus Torvalds 			ret = -ESRCH;
1171da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
1181da177e4SLinus Torvalds 	}
1191da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
1201da177e4SLinus Torvalds 
121d9ae90acSOleg Nesterov 	if (!ret && !kill)
12285ba2d86SRoland McGrath 		ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH;
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds 	/* All systems go.. */
1251da177e4SLinus Torvalds 	return ret;
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds 
128006ebb40SStephen Smalley int __ptrace_may_access(struct task_struct *task, unsigned int mode)
129ab8d11beSMiklos Szeredi {
130c69e8d9cSDavid Howells 	const struct cred *cred = current_cred(), *tcred;
131b6dff3ecSDavid Howells 
132df26c40eSEric W. Biederman 	/* May we inspect the given task?
133df26c40eSEric W. Biederman 	 * This check is used both for attaching with ptrace
134df26c40eSEric W. Biederman 	 * and for allowing access to sensitive information in /proc.
135df26c40eSEric W. Biederman 	 *
136df26c40eSEric W. Biederman 	 * ptrace_attach denies several cases that /proc allows
137df26c40eSEric W. Biederman 	 * because setting up the necessary parent/child relationship
138df26c40eSEric W. Biederman 	 * or halting the specified task is impossible.
139df26c40eSEric W. Biederman 	 */
140df26c40eSEric W. Biederman 	int dumpable = 0;
141df26c40eSEric W. Biederman 	/* Don't let security modules deny introspection */
142df26c40eSEric W. Biederman 	if (task == current)
143df26c40eSEric W. Biederman 		return 0;
144c69e8d9cSDavid Howells 	rcu_read_lock();
145c69e8d9cSDavid Howells 	tcred = __task_cred(task);
146c69e8d9cSDavid Howells 	if ((cred->uid != tcred->euid ||
147c69e8d9cSDavid Howells 	     cred->uid != tcred->suid ||
148c69e8d9cSDavid Howells 	     cred->uid != tcred->uid  ||
149c69e8d9cSDavid Howells 	     cred->gid != tcred->egid ||
150c69e8d9cSDavid Howells 	     cred->gid != tcred->sgid ||
151c69e8d9cSDavid Howells 	     cred->gid != tcred->gid) &&
152c69e8d9cSDavid Howells 	    !capable(CAP_SYS_PTRACE)) {
153c69e8d9cSDavid Howells 		rcu_read_unlock();
154ab8d11beSMiklos Szeredi 		return -EPERM;
155c69e8d9cSDavid Howells 	}
156c69e8d9cSDavid Howells 	rcu_read_unlock();
157ab8d11beSMiklos Szeredi 	smp_rmb();
158df26c40eSEric W. Biederman 	if (task->mm)
1596c5d5238SKawai, Hidehiro 		dumpable = get_dumpable(task->mm);
160df26c40eSEric W. Biederman 	if (!dumpable && !capable(CAP_SYS_PTRACE))
161ab8d11beSMiklos Szeredi 		return -EPERM;
162ab8d11beSMiklos Szeredi 
1635cd9c58fSDavid Howells 	return security_ptrace_may_access(task, mode);
164ab8d11beSMiklos Szeredi }
165ab8d11beSMiklos Szeredi 
166006ebb40SStephen Smalley bool ptrace_may_access(struct task_struct *task, unsigned int mode)
167ab8d11beSMiklos Szeredi {
168ab8d11beSMiklos Szeredi 	int err;
169ab8d11beSMiklos Szeredi 	task_lock(task);
170006ebb40SStephen Smalley 	err = __ptrace_may_access(task, mode);
171ab8d11beSMiklos Szeredi 	task_unlock(task);
172006ebb40SStephen Smalley 	return (!err ? true : false);
173ab8d11beSMiklos Szeredi }
174ab8d11beSMiklos Szeredi 
1751da177e4SLinus Torvalds int ptrace_attach(struct task_struct *task)
1761da177e4SLinus Torvalds {
1771da177e4SLinus Torvalds 	int retval;
1786175ecfeSSripathi Kodi 	unsigned long flags;
179f5b40e36SLinus Torvalds 
180a5cb013dSAl Viro 	audit_ptrace(task);
181a5cb013dSAl Viro 
1821da177e4SLinus Torvalds 	retval = -EPERM;
183bac0abd6SPavel Emelyanov 	if (same_thread_group(task, current))
184f5b40e36SLinus Torvalds 		goto out;
185f5b40e36SLinus Torvalds 
186d84f4f99SDavid Howells 	/* Protect exec's credential calculations against our interference;
187d84f4f99SDavid Howells 	 * SUID, SGID and LSM creds get determined differently under ptrace.
188d84f4f99SDavid Howells 	 */
189d84f4f99SDavid Howells 	retval = mutex_lock_interruptible(&current->cred_exec_mutex);
190d84f4f99SDavid Howells 	if (retval  < 0)
191d84f4f99SDavid Howells 		goto out;
192d84f4f99SDavid Howells 
193d84f4f99SDavid Howells 	retval = -EPERM;
194f358166aSLinus Torvalds repeat:
195f358166aSLinus Torvalds 	/*
196f358166aSLinus Torvalds 	 * Nasty, nasty.
197f358166aSLinus Torvalds 	 *
198f358166aSLinus Torvalds 	 * We want to hold both the task-lock and the
199f358166aSLinus Torvalds 	 * tasklist_lock for writing at the same time.
200f358166aSLinus Torvalds 	 * But that's against the rules (tasklist_lock
201f358166aSLinus Torvalds 	 * is taken for reading by interrupts on other
202f358166aSLinus Torvalds 	 * cpu's that may have task_lock).
203f358166aSLinus Torvalds 	 */
204f5b40e36SLinus Torvalds 	task_lock(task);
2056175ecfeSSripathi Kodi 	if (!write_trylock_irqsave(&tasklist_lock, flags)) {
206f358166aSLinus Torvalds 		task_unlock(task);
207f358166aSLinus Torvalds 		do {
208f358166aSLinus Torvalds 			cpu_relax();
209f358166aSLinus Torvalds 		} while (!write_can_lock(&tasklist_lock));
210f358166aSLinus Torvalds 		goto repeat;
211f358166aSLinus Torvalds 	}
212f5b40e36SLinus Torvalds 
213df26c40eSEric W. Biederman 	if (!task->mm)
214df26c40eSEric W. Biederman 		goto bad;
2151da177e4SLinus Torvalds 	/* the same process cannot be attached many times */
2161da177e4SLinus Torvalds 	if (task->ptrace & PT_PTRACED)
2171da177e4SLinus Torvalds 		goto bad;
218006ebb40SStephen Smalley 	retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
2191da177e4SLinus Torvalds 	if (retval)
2201da177e4SLinus Torvalds 		goto bad;
2211da177e4SLinus Torvalds 
2221da177e4SLinus Torvalds 	/* Go */
2236b39c7bfSOleg Nesterov 	task->ptrace |= PT_PTRACED;
2241da177e4SLinus Torvalds 	if (capable(CAP_SYS_PTRACE))
2251da177e4SLinus Torvalds 		task->ptrace |= PT_PTRACE_CAP;
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds 	__ptrace_link(task, current);
2281da177e4SLinus Torvalds 
22933e9fc7dSOleg Nesterov 	send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
2301da177e4SLinus Torvalds bad:
2316175ecfeSSripathi Kodi 	write_unlock_irqrestore(&tasklist_lock, flags);
2321da177e4SLinus Torvalds 	task_unlock(task);
233d84f4f99SDavid Howells 	mutex_unlock(&current->cred_exec_mutex);
234f5b40e36SLinus Torvalds out:
2351da177e4SLinus Torvalds 	return retval;
2361da177e4SLinus Torvalds }
2371da177e4SLinus Torvalds 
23839c626aeSOleg Nesterov /*
23939c626aeSOleg Nesterov  * Called with irqs disabled, returns true if childs should reap themselves.
24039c626aeSOleg Nesterov  */
24139c626aeSOleg Nesterov static int ignoring_children(struct sighand_struct *sigh)
24239c626aeSOleg Nesterov {
24339c626aeSOleg Nesterov 	int ret;
24439c626aeSOleg Nesterov 	spin_lock(&sigh->siglock);
24539c626aeSOleg Nesterov 	ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) ||
24639c626aeSOleg Nesterov 	      (sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT);
24739c626aeSOleg Nesterov 	spin_unlock(&sigh->siglock);
24839c626aeSOleg Nesterov 	return ret;
24939c626aeSOleg Nesterov }
25039c626aeSOleg Nesterov 
25139c626aeSOleg Nesterov /*
25239c626aeSOleg Nesterov  * Called with tasklist_lock held for writing.
25339c626aeSOleg Nesterov  * Unlink a traced task, and clean it up if it was a traced zombie.
25439c626aeSOleg Nesterov  * Return true if it needs to be reaped with release_task().
25539c626aeSOleg Nesterov  * (We can't call release_task() here because we already hold tasklist_lock.)
25639c626aeSOleg Nesterov  *
25739c626aeSOleg Nesterov  * If it's a zombie, our attachedness prevented normal parent notification
25839c626aeSOleg Nesterov  * or self-reaping.  Do notification now if it would have happened earlier.
25939c626aeSOleg Nesterov  * If it should reap itself, return true.
26039c626aeSOleg Nesterov  *
26139c626aeSOleg Nesterov  * If it's our own child, there is no notification to do.
26239c626aeSOleg Nesterov  * But if our normal children self-reap, then this child
26339c626aeSOleg Nesterov  * was prevented by ptrace and we must reap it now.
26439c626aeSOleg Nesterov  */
26539c626aeSOleg Nesterov static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
26639c626aeSOleg Nesterov {
26739c626aeSOleg Nesterov 	__ptrace_unlink(p);
26839c626aeSOleg Nesterov 
26939c626aeSOleg Nesterov 	if (p->exit_state == EXIT_ZOMBIE) {
27039c626aeSOleg Nesterov 		if (!task_detached(p) && thread_group_empty(p)) {
27139c626aeSOleg Nesterov 			if (!same_thread_group(p->real_parent, tracer))
27239c626aeSOleg Nesterov 				do_notify_parent(p, p->exit_signal);
27339c626aeSOleg Nesterov 			else if (ignoring_children(tracer->sighand))
27439c626aeSOleg Nesterov 				p->exit_signal = -1;
27539c626aeSOleg Nesterov 		}
27639c626aeSOleg Nesterov 		if (task_detached(p)) {
27739c626aeSOleg Nesterov 			/* Mark it as in the process of being reaped. */
27839c626aeSOleg Nesterov 			p->exit_state = EXIT_DEAD;
27939c626aeSOleg Nesterov 			return true;
28039c626aeSOleg Nesterov 		}
28139c626aeSOleg Nesterov 	}
28239c626aeSOleg Nesterov 
28339c626aeSOleg Nesterov 	return false;
28439c626aeSOleg Nesterov }
28539c626aeSOleg Nesterov 
2861da177e4SLinus Torvalds int ptrace_detach(struct task_struct *child, unsigned int data)
2871da177e4SLinus Torvalds {
28839c626aeSOleg Nesterov 	bool dead = false;
2894576145cSOleg Nesterov 
2907ed20e1aSJesper Juhl 	if (!valid_signal(data))
2911da177e4SLinus Torvalds 		return -EIO;
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds 	/* Architecture-specific hardware disable .. */
2941da177e4SLinus Torvalds 	ptrace_disable(child);
2957d941432SRoland McGrath 	clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
2961da177e4SLinus Torvalds 
29795c3eb76SOleg Nesterov 	write_lock_irq(&tasklist_lock);
29839c626aeSOleg Nesterov 	/*
29939c626aeSOleg Nesterov 	 * This child can be already killed. Make sure de_thread() or
30039c626aeSOleg Nesterov 	 * our sub-thread doing do_wait() didn't do release_task() yet.
30139c626aeSOleg Nesterov 	 */
30295c3eb76SOleg Nesterov 	if (child->ptrace) {
30395c3eb76SOleg Nesterov 		child->exit_code = data;
30495c3eb76SOleg Nesterov 
3054576145cSOleg Nesterov 		dead = __ptrace_detach(current, child);
30695c3eb76SOleg Nesterov 
30795c3eb76SOleg Nesterov 		if (!child->exit_state)
30895c3eb76SOleg Nesterov 			wake_up_process(child);
30995c3eb76SOleg Nesterov 	}
3101da177e4SLinus Torvalds 	write_unlock_irq(&tasklist_lock);
3111da177e4SLinus Torvalds 
3124576145cSOleg Nesterov 	if (unlikely(dead))
3134576145cSOleg Nesterov 		release_task(child);
3144576145cSOleg Nesterov 
3151da177e4SLinus Torvalds 	return 0;
3161da177e4SLinus Torvalds }
3171da177e4SLinus Torvalds 
31839c626aeSOleg Nesterov /*
31939c626aeSOleg Nesterov  * Detach all tasks we were using ptrace on.
32039c626aeSOleg Nesterov  */
32139c626aeSOleg Nesterov void exit_ptrace(struct task_struct *tracer)
32239c626aeSOleg Nesterov {
32339c626aeSOleg Nesterov 	struct task_struct *p, *n;
32439c626aeSOleg Nesterov 	LIST_HEAD(ptrace_dead);
32539c626aeSOleg Nesterov 
32639c626aeSOleg Nesterov 	write_lock_irq(&tasklist_lock);
32739c626aeSOleg Nesterov 	list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) {
32839c626aeSOleg Nesterov 		if (__ptrace_detach(tracer, p))
32939c626aeSOleg Nesterov 			list_add(&p->ptrace_entry, &ptrace_dead);
33039c626aeSOleg Nesterov 	}
33139c626aeSOleg Nesterov 	write_unlock_irq(&tasklist_lock);
33239c626aeSOleg Nesterov 
33339c626aeSOleg Nesterov 	BUG_ON(!list_empty(&tracer->ptraced));
33439c626aeSOleg Nesterov 
33539c626aeSOleg Nesterov 	list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) {
33639c626aeSOleg Nesterov 		list_del_init(&p->ptrace_entry);
33739c626aeSOleg Nesterov 		release_task(p);
33839c626aeSOleg Nesterov 	}
33939c626aeSOleg Nesterov }
34039c626aeSOleg Nesterov 
3411da177e4SLinus Torvalds int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
3421da177e4SLinus Torvalds {
3431da177e4SLinus Torvalds 	int copied = 0;
3441da177e4SLinus Torvalds 
3451da177e4SLinus Torvalds 	while (len > 0) {
3461da177e4SLinus Torvalds 		char buf[128];
3471da177e4SLinus Torvalds 		int this_len, retval;
3481da177e4SLinus Torvalds 
3491da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
3501da177e4SLinus Torvalds 		retval = access_process_vm(tsk, src, buf, this_len, 0);
3511da177e4SLinus Torvalds 		if (!retval) {
3521da177e4SLinus Torvalds 			if (copied)
3531da177e4SLinus Torvalds 				break;
3541da177e4SLinus Torvalds 			return -EIO;
3551da177e4SLinus Torvalds 		}
3561da177e4SLinus Torvalds 		if (copy_to_user(dst, buf, retval))
3571da177e4SLinus Torvalds 			return -EFAULT;
3581da177e4SLinus Torvalds 		copied += retval;
3591da177e4SLinus Torvalds 		src += retval;
3601da177e4SLinus Torvalds 		dst += retval;
3611da177e4SLinus Torvalds 		len -= retval;
3621da177e4SLinus Torvalds 	}
3631da177e4SLinus Torvalds 	return copied;
3641da177e4SLinus Torvalds }
3651da177e4SLinus Torvalds 
3661da177e4SLinus Torvalds int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
3671da177e4SLinus Torvalds {
3681da177e4SLinus Torvalds 	int copied = 0;
3691da177e4SLinus Torvalds 
3701da177e4SLinus Torvalds 	while (len > 0) {
3711da177e4SLinus Torvalds 		char buf[128];
3721da177e4SLinus Torvalds 		int this_len, retval;
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
3751da177e4SLinus Torvalds 		if (copy_from_user(buf, src, this_len))
3761da177e4SLinus Torvalds 			return -EFAULT;
3771da177e4SLinus Torvalds 		retval = access_process_vm(tsk, dst, buf, this_len, 1);
3781da177e4SLinus Torvalds 		if (!retval) {
3791da177e4SLinus Torvalds 			if (copied)
3801da177e4SLinus Torvalds 				break;
3811da177e4SLinus Torvalds 			return -EIO;
3821da177e4SLinus Torvalds 		}
3831da177e4SLinus Torvalds 		copied += retval;
3841da177e4SLinus Torvalds 		src += retval;
3851da177e4SLinus Torvalds 		dst += retval;
3861da177e4SLinus Torvalds 		len -= retval;
3871da177e4SLinus Torvalds 	}
3881da177e4SLinus Torvalds 	return copied;
3891da177e4SLinus Torvalds }
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds static int ptrace_setoptions(struct task_struct *child, long data)
3921da177e4SLinus Torvalds {
3931da177e4SLinus Torvalds 	child->ptrace &= ~PT_TRACE_MASK;
3941da177e4SLinus Torvalds 
3951da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACESYSGOOD)
3961da177e4SLinus Torvalds 		child->ptrace |= PT_TRACESYSGOOD;
3971da177e4SLinus Torvalds 
3981da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEFORK)
3991da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_FORK;
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORK)
4021da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK;
4031da177e4SLinus Torvalds 
4041da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACECLONE)
4051da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_CLONE;
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXEC)
4081da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXEC;
4091da177e4SLinus Torvalds 
4101da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORKDONE)
4111da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK_DONE;
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXIT)
4141da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXIT;
4151da177e4SLinus Torvalds 
4161da177e4SLinus Torvalds 	return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
4171da177e4SLinus Torvalds }
4181da177e4SLinus Torvalds 
419e16b2781SRoland McGrath static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info)
4201da177e4SLinus Torvalds {
4211da177e4SLinus Torvalds 	int error = -ESRCH;
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
4241da177e4SLinus Torvalds 	if (likely(child->sighand != NULL)) {
4251da177e4SLinus Torvalds 		error = -EINVAL;
4261da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
4271da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
428e16b2781SRoland McGrath 			*info = *child->last_siginfo;
4291da177e4SLinus Torvalds 			error = 0;
4301da177e4SLinus Torvalds 		}
4311da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
4321da177e4SLinus Torvalds 	}
4331da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
4341da177e4SLinus Torvalds 	return error;
4351da177e4SLinus Torvalds }
4361da177e4SLinus Torvalds 
437e16b2781SRoland McGrath static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info)
4381da177e4SLinus Torvalds {
4391da177e4SLinus Torvalds 	int error = -ESRCH;
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
4421da177e4SLinus Torvalds 	if (likely(child->sighand != NULL)) {
4431da177e4SLinus Torvalds 		error = -EINVAL;
4441da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
4451da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
446e16b2781SRoland McGrath 			*child->last_siginfo = *info;
4471da177e4SLinus Torvalds 			error = 0;
4481da177e4SLinus Torvalds 		}
4491da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
4501da177e4SLinus Torvalds 	}
4511da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
4521da177e4SLinus Torvalds 	return error;
4531da177e4SLinus Torvalds }
4541da177e4SLinus Torvalds 
45536df29d7SRoland McGrath 
45636df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
45736df29d7SRoland McGrath #define is_singlestep(request)		((request) == PTRACE_SINGLESTEP)
45836df29d7SRoland McGrath #else
45936df29d7SRoland McGrath #define is_singlestep(request)		0
46036df29d7SRoland McGrath #endif
46136df29d7SRoland McGrath 
4625b88abbfSRoland McGrath #ifdef PTRACE_SINGLEBLOCK
4635b88abbfSRoland McGrath #define is_singleblock(request)		((request) == PTRACE_SINGLEBLOCK)
4645b88abbfSRoland McGrath #else
4655b88abbfSRoland McGrath #define is_singleblock(request)		0
4665b88abbfSRoland McGrath #endif
4675b88abbfSRoland McGrath 
46836df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
46936df29d7SRoland McGrath #define is_sysemu_singlestep(request)	((request) == PTRACE_SYSEMU_SINGLESTEP)
47036df29d7SRoland McGrath #else
47136df29d7SRoland McGrath #define is_sysemu_singlestep(request)	0
47236df29d7SRoland McGrath #endif
47336df29d7SRoland McGrath 
47436df29d7SRoland McGrath static int ptrace_resume(struct task_struct *child, long request, long data)
47536df29d7SRoland McGrath {
47636df29d7SRoland McGrath 	if (!valid_signal(data))
47736df29d7SRoland McGrath 		return -EIO;
47836df29d7SRoland McGrath 
47936df29d7SRoland McGrath 	if (request == PTRACE_SYSCALL)
48036df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
48136df29d7SRoland McGrath 	else
48236df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
48336df29d7SRoland McGrath 
48436df29d7SRoland McGrath #ifdef TIF_SYSCALL_EMU
48536df29d7SRoland McGrath 	if (request == PTRACE_SYSEMU || request == PTRACE_SYSEMU_SINGLESTEP)
48636df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_EMU);
48736df29d7SRoland McGrath 	else
48836df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
48936df29d7SRoland McGrath #endif
49036df29d7SRoland McGrath 
4915b88abbfSRoland McGrath 	if (is_singleblock(request)) {
4925b88abbfSRoland McGrath 		if (unlikely(!arch_has_block_step()))
4935b88abbfSRoland McGrath 			return -EIO;
4945b88abbfSRoland McGrath 		user_enable_block_step(child);
4955b88abbfSRoland McGrath 	} else if (is_singlestep(request) || is_sysemu_singlestep(request)) {
49636df29d7SRoland McGrath 		if (unlikely(!arch_has_single_step()))
49736df29d7SRoland McGrath 			return -EIO;
49836df29d7SRoland McGrath 		user_enable_single_step(child);
49936df29d7SRoland McGrath 	}
50036df29d7SRoland McGrath 	else
50136df29d7SRoland McGrath 		user_disable_single_step(child);
50236df29d7SRoland McGrath 
50336df29d7SRoland McGrath 	child->exit_code = data;
50436df29d7SRoland McGrath 	wake_up_process(child);
50536df29d7SRoland McGrath 
50636df29d7SRoland McGrath 	return 0;
50736df29d7SRoland McGrath }
50836df29d7SRoland McGrath 
5091da177e4SLinus Torvalds int ptrace_request(struct task_struct *child, long request,
5101da177e4SLinus Torvalds 		   long addr, long data)
5111da177e4SLinus Torvalds {
5121da177e4SLinus Torvalds 	int ret = -EIO;
513e16b2781SRoland McGrath 	siginfo_t siginfo;
5141da177e4SLinus Torvalds 
5151da177e4SLinus Torvalds 	switch (request) {
51616c3e389SRoland McGrath 	case PTRACE_PEEKTEXT:
51716c3e389SRoland McGrath 	case PTRACE_PEEKDATA:
51816c3e389SRoland McGrath 		return generic_ptrace_peekdata(child, addr, data);
51916c3e389SRoland McGrath 	case PTRACE_POKETEXT:
52016c3e389SRoland McGrath 	case PTRACE_POKEDATA:
52116c3e389SRoland McGrath 		return generic_ptrace_pokedata(child, addr, data);
52216c3e389SRoland McGrath 
5231da177e4SLinus Torvalds #ifdef PTRACE_OLDSETOPTIONS
5241da177e4SLinus Torvalds 	case PTRACE_OLDSETOPTIONS:
5251da177e4SLinus Torvalds #endif
5261da177e4SLinus Torvalds 	case PTRACE_SETOPTIONS:
5271da177e4SLinus Torvalds 		ret = ptrace_setoptions(child, data);
5281da177e4SLinus Torvalds 		break;
5291da177e4SLinus Torvalds 	case PTRACE_GETEVENTMSG:
5301da177e4SLinus Torvalds 		ret = put_user(child->ptrace_message, (unsigned long __user *) data);
5311da177e4SLinus Torvalds 		break;
532e16b2781SRoland McGrath 
5331da177e4SLinus Torvalds 	case PTRACE_GETSIGINFO:
534e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
535e16b2781SRoland McGrath 		if (!ret)
536e16b2781SRoland McGrath 			ret = copy_siginfo_to_user((siginfo_t __user *) data,
537e16b2781SRoland McGrath 						   &siginfo);
5381da177e4SLinus Torvalds 		break;
539e16b2781SRoland McGrath 
5401da177e4SLinus Torvalds 	case PTRACE_SETSIGINFO:
541e16b2781SRoland McGrath 		if (copy_from_user(&siginfo, (siginfo_t __user *) data,
542e16b2781SRoland McGrath 				   sizeof siginfo))
543e16b2781SRoland McGrath 			ret = -EFAULT;
544e16b2781SRoland McGrath 		else
545e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
5461da177e4SLinus Torvalds 		break;
547e16b2781SRoland McGrath 
5481bcf5482SAlexey Dobriyan 	case PTRACE_DETACH:	 /* detach a process that was attached. */
5491bcf5482SAlexey Dobriyan 		ret = ptrace_detach(child, data);
5501bcf5482SAlexey Dobriyan 		break;
55136df29d7SRoland McGrath 
55236df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
55336df29d7SRoland McGrath 	case PTRACE_SINGLESTEP:
55436df29d7SRoland McGrath #endif
5555b88abbfSRoland McGrath #ifdef PTRACE_SINGLEBLOCK
5565b88abbfSRoland McGrath 	case PTRACE_SINGLEBLOCK:
5575b88abbfSRoland McGrath #endif
55836df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
55936df29d7SRoland McGrath 	case PTRACE_SYSEMU:
56036df29d7SRoland McGrath 	case PTRACE_SYSEMU_SINGLESTEP:
56136df29d7SRoland McGrath #endif
56236df29d7SRoland McGrath 	case PTRACE_SYSCALL:
56336df29d7SRoland McGrath 	case PTRACE_CONT:
56436df29d7SRoland McGrath 		return ptrace_resume(child, request, data);
56536df29d7SRoland McGrath 
56636df29d7SRoland McGrath 	case PTRACE_KILL:
56736df29d7SRoland McGrath 		if (child->exit_state)	/* already dead */
56836df29d7SRoland McGrath 			return 0;
56936df29d7SRoland McGrath 		return ptrace_resume(child, request, SIGKILL);
57036df29d7SRoland McGrath 
5711da177e4SLinus Torvalds 	default:
5721da177e4SLinus Torvalds 		break;
5731da177e4SLinus Torvalds 	}
5741da177e4SLinus Torvalds 
5751da177e4SLinus Torvalds 	return ret;
5761da177e4SLinus Torvalds }
577481bed45SChristoph Hellwig 
5786b9c7ed8SChristoph Hellwig /**
5796b9c7ed8SChristoph Hellwig  * ptrace_traceme  --  helper for PTRACE_TRACEME
5806b9c7ed8SChristoph Hellwig  *
5816b9c7ed8SChristoph Hellwig  * Performs checks and sets PT_PTRACED.
5826b9c7ed8SChristoph Hellwig  * Should be used by all ptrace implementations for PTRACE_TRACEME.
5836b9c7ed8SChristoph Hellwig  */
5846b9c7ed8SChristoph Hellwig int ptrace_traceme(void)
585481bed45SChristoph Hellwig {
586f5b40e36SLinus Torvalds 	int ret = -EPERM;
587481bed45SChristoph Hellwig 
588481bed45SChristoph Hellwig 	/*
589481bed45SChristoph Hellwig 	 * Are we already being traced?
590481bed45SChristoph Hellwig 	 */
591f470021aSRoland McGrath repeat:
592f5b40e36SLinus Torvalds 	task_lock(current);
593f5b40e36SLinus Torvalds 	if (!(current->ptrace & PT_PTRACED)) {
594f470021aSRoland McGrath 		/*
595f470021aSRoland McGrath 		 * See ptrace_attach() comments about the locking here.
596f470021aSRoland McGrath 		 */
597f470021aSRoland McGrath 		unsigned long flags;
598f470021aSRoland McGrath 		if (!write_trylock_irqsave(&tasklist_lock, flags)) {
599f470021aSRoland McGrath 			task_unlock(current);
600f470021aSRoland McGrath 			do {
601f470021aSRoland McGrath 				cpu_relax();
602f470021aSRoland McGrath 			} while (!write_can_lock(&tasklist_lock));
603f470021aSRoland McGrath 			goto repeat;
604f470021aSRoland McGrath 		}
605f470021aSRoland McGrath 
6065cd9c58fSDavid Howells 		ret = security_ptrace_traceme(current->parent);
607f470021aSRoland McGrath 
608481bed45SChristoph Hellwig 		/*
609481bed45SChristoph Hellwig 		 * Set the ptrace bit in the process ptrace flags.
610f470021aSRoland McGrath 		 * Then link us on our parent's ptraced list.
611481bed45SChristoph Hellwig 		 */
612f470021aSRoland McGrath 		if (!ret) {
613481bed45SChristoph Hellwig 			current->ptrace |= PT_PTRACED;
614f470021aSRoland McGrath 			__ptrace_link(current, current->real_parent);
615f470021aSRoland McGrath 		}
616f470021aSRoland McGrath 
617f470021aSRoland McGrath 		write_unlock_irqrestore(&tasklist_lock, flags);
618f5b40e36SLinus Torvalds 	}
619f5b40e36SLinus Torvalds 	task_unlock(current);
620f5b40e36SLinus Torvalds 	return ret;
621481bed45SChristoph Hellwig }
622481bed45SChristoph Hellwig 
6236b9c7ed8SChristoph Hellwig /**
6246b9c7ed8SChristoph Hellwig  * ptrace_get_task_struct  --  grab a task struct reference for ptrace
6256b9c7ed8SChristoph Hellwig  * @pid:       process id to grab a task_struct reference of
6266b9c7ed8SChristoph Hellwig  *
6276b9c7ed8SChristoph Hellwig  * This function is a helper for ptrace implementations.  It checks
6286b9c7ed8SChristoph Hellwig  * permissions and then grabs a task struct for use of the actual
6296b9c7ed8SChristoph Hellwig  * ptrace implementation.
6306b9c7ed8SChristoph Hellwig  *
6316b9c7ed8SChristoph Hellwig  * Returns the task_struct for @pid or an ERR_PTR() on failure.
6326b9c7ed8SChristoph Hellwig  */
6336b9c7ed8SChristoph Hellwig struct task_struct *ptrace_get_task_struct(pid_t pid)
6346b9c7ed8SChristoph Hellwig {
6356b9c7ed8SChristoph Hellwig 	struct task_struct *child;
6366b9c7ed8SChristoph Hellwig 
637481bed45SChristoph Hellwig 	read_lock(&tasklist_lock);
638228ebcbeSPavel Emelyanov 	child = find_task_by_vpid(pid);
639481bed45SChristoph Hellwig 	if (child)
640481bed45SChristoph Hellwig 		get_task_struct(child);
641f400e198SSukadev Bhattiprolu 
642481bed45SChristoph Hellwig 	read_unlock(&tasklist_lock);
643481bed45SChristoph Hellwig 	if (!child)
6446b9c7ed8SChristoph Hellwig 		return ERR_PTR(-ESRCH);
6456b9c7ed8SChristoph Hellwig 	return child;
646481bed45SChristoph Hellwig }
647481bed45SChristoph Hellwig 
6480ac15559SChristoph Hellwig #ifndef arch_ptrace_attach
6490ac15559SChristoph Hellwig #define arch_ptrace_attach(child)	do { } while (0)
6500ac15559SChristoph Hellwig #endif
6510ac15559SChristoph Hellwig 
6521e7bfb21SHeiko Carstens SYSCALL_DEFINE4(ptrace, long, request, long, pid, long, addr, long, data)
653481bed45SChristoph Hellwig {
654481bed45SChristoph Hellwig 	struct task_struct *child;
655481bed45SChristoph Hellwig 	long ret;
656481bed45SChristoph Hellwig 
657481bed45SChristoph Hellwig 	/*
658481bed45SChristoph Hellwig 	 * This lock_kernel fixes a subtle race with suid exec
659481bed45SChristoph Hellwig 	 */
660481bed45SChristoph Hellwig 	lock_kernel();
6616b9c7ed8SChristoph Hellwig 	if (request == PTRACE_TRACEME) {
6626b9c7ed8SChristoph Hellwig 		ret = ptrace_traceme();
6636ea6dd93SHaavard Skinnemoen 		if (!ret)
6646ea6dd93SHaavard Skinnemoen 			arch_ptrace_attach(current);
665481bed45SChristoph Hellwig 		goto out;
6666b9c7ed8SChristoph Hellwig 	}
6676b9c7ed8SChristoph Hellwig 
6686b9c7ed8SChristoph Hellwig 	child = ptrace_get_task_struct(pid);
6696b9c7ed8SChristoph Hellwig 	if (IS_ERR(child)) {
6706b9c7ed8SChristoph Hellwig 		ret = PTR_ERR(child);
6716b9c7ed8SChristoph Hellwig 		goto out;
6726b9c7ed8SChristoph Hellwig 	}
673481bed45SChristoph Hellwig 
674481bed45SChristoph Hellwig 	if (request == PTRACE_ATTACH) {
675481bed45SChristoph Hellwig 		ret = ptrace_attach(child);
6760ac15559SChristoph Hellwig 		/*
6770ac15559SChristoph Hellwig 		 * Some architectures need to do book-keeping after
6780ac15559SChristoph Hellwig 		 * a ptrace attach.
6790ac15559SChristoph Hellwig 		 */
6800ac15559SChristoph Hellwig 		if (!ret)
6810ac15559SChristoph Hellwig 			arch_ptrace_attach(child);
682005f18dfSChristoph Hellwig 		goto out_put_task_struct;
683481bed45SChristoph Hellwig 	}
684481bed45SChristoph Hellwig 
685481bed45SChristoph Hellwig 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
686481bed45SChristoph Hellwig 	if (ret < 0)
687481bed45SChristoph Hellwig 		goto out_put_task_struct;
688481bed45SChristoph Hellwig 
689481bed45SChristoph Hellwig 	ret = arch_ptrace(child, request, addr, data);
690481bed45SChristoph Hellwig 	if (ret < 0)
691481bed45SChristoph Hellwig 		goto out_put_task_struct;
692481bed45SChristoph Hellwig 
693481bed45SChristoph Hellwig  out_put_task_struct:
694481bed45SChristoph Hellwig 	put_task_struct(child);
695481bed45SChristoph Hellwig  out:
696481bed45SChristoph Hellwig 	unlock_kernel();
697481bed45SChristoph Hellwig 	return ret;
698481bed45SChristoph Hellwig }
69976647323SAlexey Dobriyan 
70076647323SAlexey Dobriyan int generic_ptrace_peekdata(struct task_struct *tsk, long addr, long data)
70176647323SAlexey Dobriyan {
70276647323SAlexey Dobriyan 	unsigned long tmp;
70376647323SAlexey Dobriyan 	int copied;
70476647323SAlexey Dobriyan 
70576647323SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
70676647323SAlexey Dobriyan 	if (copied != sizeof(tmp))
70776647323SAlexey Dobriyan 		return -EIO;
70876647323SAlexey Dobriyan 	return put_user(tmp, (unsigned long __user *)data);
70976647323SAlexey Dobriyan }
710f284ce72SAlexey Dobriyan 
711f284ce72SAlexey Dobriyan int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data)
712f284ce72SAlexey Dobriyan {
713f284ce72SAlexey Dobriyan 	int copied;
714f284ce72SAlexey Dobriyan 
715f284ce72SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
716f284ce72SAlexey Dobriyan 	return (copied == sizeof(data)) ? 0 : -EIO;
717f284ce72SAlexey Dobriyan }
718032d82d9SRoland McGrath 
71996b8936aSChristoph Hellwig #if defined CONFIG_COMPAT
720032d82d9SRoland McGrath #include <linux/compat.h>
721032d82d9SRoland McGrath 
722032d82d9SRoland McGrath int compat_ptrace_request(struct task_struct *child, compat_long_t request,
723032d82d9SRoland McGrath 			  compat_ulong_t addr, compat_ulong_t data)
724032d82d9SRoland McGrath {
725032d82d9SRoland McGrath 	compat_ulong_t __user *datap = compat_ptr(data);
726032d82d9SRoland McGrath 	compat_ulong_t word;
727e16b2781SRoland McGrath 	siginfo_t siginfo;
728032d82d9SRoland McGrath 	int ret;
729032d82d9SRoland McGrath 
730032d82d9SRoland McGrath 	switch (request) {
731032d82d9SRoland McGrath 	case PTRACE_PEEKTEXT:
732032d82d9SRoland McGrath 	case PTRACE_PEEKDATA:
733032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &word, sizeof(word), 0);
734032d82d9SRoland McGrath 		if (ret != sizeof(word))
735032d82d9SRoland McGrath 			ret = -EIO;
736032d82d9SRoland McGrath 		else
737032d82d9SRoland McGrath 			ret = put_user(word, datap);
738032d82d9SRoland McGrath 		break;
739032d82d9SRoland McGrath 
740032d82d9SRoland McGrath 	case PTRACE_POKETEXT:
741032d82d9SRoland McGrath 	case PTRACE_POKEDATA:
742032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &data, sizeof(data), 1);
743032d82d9SRoland McGrath 		ret = (ret != sizeof(data) ? -EIO : 0);
744032d82d9SRoland McGrath 		break;
745032d82d9SRoland McGrath 
746032d82d9SRoland McGrath 	case PTRACE_GETEVENTMSG:
747032d82d9SRoland McGrath 		ret = put_user((compat_ulong_t) child->ptrace_message, datap);
748032d82d9SRoland McGrath 		break;
749032d82d9SRoland McGrath 
750e16b2781SRoland McGrath 	case PTRACE_GETSIGINFO:
751e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
752e16b2781SRoland McGrath 		if (!ret)
753e16b2781SRoland McGrath 			ret = copy_siginfo_to_user32(
754e16b2781SRoland McGrath 				(struct compat_siginfo __user *) datap,
755e16b2781SRoland McGrath 				&siginfo);
756e16b2781SRoland McGrath 		break;
757e16b2781SRoland McGrath 
758e16b2781SRoland McGrath 	case PTRACE_SETSIGINFO:
759e16b2781SRoland McGrath 		memset(&siginfo, 0, sizeof siginfo);
760e16b2781SRoland McGrath 		if (copy_siginfo_from_user32(
761e16b2781SRoland McGrath 			    &siginfo, (struct compat_siginfo __user *) datap))
762e16b2781SRoland McGrath 			ret = -EFAULT;
763e16b2781SRoland McGrath 		else
764e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
765e16b2781SRoland McGrath 		break;
766e16b2781SRoland McGrath 
767032d82d9SRoland McGrath 	default:
768032d82d9SRoland McGrath 		ret = ptrace_request(child, request, addr, data);
769032d82d9SRoland McGrath 	}
770032d82d9SRoland McGrath 
771032d82d9SRoland McGrath 	return ret;
772032d82d9SRoland McGrath }
773c269f196SRoland McGrath 
774c269f196SRoland McGrath asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
775c269f196SRoland McGrath 				  compat_long_t addr, compat_long_t data)
776c269f196SRoland McGrath {
777c269f196SRoland McGrath 	struct task_struct *child;
778c269f196SRoland McGrath 	long ret;
779c269f196SRoland McGrath 
780c269f196SRoland McGrath 	/*
781c269f196SRoland McGrath 	 * This lock_kernel fixes a subtle race with suid exec
782c269f196SRoland McGrath 	 */
783c269f196SRoland McGrath 	lock_kernel();
784c269f196SRoland McGrath 	if (request == PTRACE_TRACEME) {
785c269f196SRoland McGrath 		ret = ptrace_traceme();
786c269f196SRoland McGrath 		goto out;
787c269f196SRoland McGrath 	}
788c269f196SRoland McGrath 
789c269f196SRoland McGrath 	child = ptrace_get_task_struct(pid);
790c269f196SRoland McGrath 	if (IS_ERR(child)) {
791c269f196SRoland McGrath 		ret = PTR_ERR(child);
792c269f196SRoland McGrath 		goto out;
793c269f196SRoland McGrath 	}
794c269f196SRoland McGrath 
795c269f196SRoland McGrath 	if (request == PTRACE_ATTACH) {
796c269f196SRoland McGrath 		ret = ptrace_attach(child);
797c269f196SRoland McGrath 		/*
798c269f196SRoland McGrath 		 * Some architectures need to do book-keeping after
799c269f196SRoland McGrath 		 * a ptrace attach.
800c269f196SRoland McGrath 		 */
801c269f196SRoland McGrath 		if (!ret)
802c269f196SRoland McGrath 			arch_ptrace_attach(child);
803c269f196SRoland McGrath 		goto out_put_task_struct;
804c269f196SRoland McGrath 	}
805c269f196SRoland McGrath 
806c269f196SRoland McGrath 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
807c269f196SRoland McGrath 	if (!ret)
808c269f196SRoland McGrath 		ret = compat_arch_ptrace(child, request, addr, data);
809c269f196SRoland McGrath 
810c269f196SRoland McGrath  out_put_task_struct:
811c269f196SRoland McGrath 	put_task_struct(child);
812c269f196SRoland McGrath  out:
813c269f196SRoland McGrath 	unlock_kernel();
814c269f196SRoland McGrath 	return ret;
815c269f196SRoland McGrath }
81696b8936aSChristoph Hellwig #endif	/* CONFIG_COMPAT */
817