xref: /openbmc/linux/kernel/ptrace.c (revision 3a709703)
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 /*
28bf53de90SMarkus Metzger  * Initialize a new task whose father had been ptraced.
29bf53de90SMarkus Metzger  *
30bf53de90SMarkus Metzger  * Called from copy_process().
31bf53de90SMarkus Metzger  */
32bf53de90SMarkus Metzger void ptrace_fork(struct task_struct *child, unsigned long clone_flags)
33bf53de90SMarkus Metzger {
34bf53de90SMarkus Metzger 	arch_ptrace_fork(child, clone_flags);
35bf53de90SMarkus Metzger }
36bf53de90SMarkus Metzger 
371da177e4SLinus Torvalds /*
381da177e4SLinus Torvalds  * ptrace a task: make the debugger its new parent and
391da177e4SLinus Torvalds  * move it to the ptrace list.
401da177e4SLinus Torvalds  *
411da177e4SLinus Torvalds  * Must be called with the tasklist lock write-held.
421da177e4SLinus Torvalds  */
4336c8b586SIngo Molnar void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
441da177e4SLinus Torvalds {
45f470021aSRoland McGrath 	BUG_ON(!list_empty(&child->ptrace_entry));
46f470021aSRoland McGrath 	list_add(&child->ptrace_entry, &new_parent->ptraced);
471da177e4SLinus Torvalds 	child->parent = new_parent;
481da177e4SLinus Torvalds }
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds /*
511da177e4SLinus Torvalds  * Turn a tracing stop into a normal stop now, since with no tracer there
521da177e4SLinus Torvalds  * would be no way to wake it up with SIGCONT or SIGKILL.  If there was a
531da177e4SLinus Torvalds  * signal sent that would resume the child, but didn't because it was in
541da177e4SLinus Torvalds  * TASK_TRACED, resume it now.
551da177e4SLinus Torvalds  * Requires that irqs be disabled.
561da177e4SLinus Torvalds  */
57b747c8c1SAdrian Bunk static void ptrace_untrace(struct task_struct *child)
581da177e4SLinus Torvalds {
591da177e4SLinus Torvalds 	spin_lock(&child->sighand->siglock);
606618a3e2SMatthew Wilcox 	if (task_is_traced(child)) {
611ee11844SOleg Nesterov 		/*
621ee11844SOleg Nesterov 		 * If the group stop is completed or in progress,
631ee11844SOleg Nesterov 		 * this thread was already counted as stopped.
641ee11844SOleg Nesterov 		 */
651ee11844SOleg Nesterov 		if (child->signal->flags & SIGNAL_STOP_STOPPED ||
661ee11844SOleg Nesterov 		    child->signal->group_stop_count)
67d9ae90acSOleg Nesterov 			__set_task_state(child, TASK_STOPPED);
681ee11844SOleg Nesterov 		else
691da177e4SLinus Torvalds 			signal_wake_up(child, 1);
701da177e4SLinus Torvalds 	}
711da177e4SLinus Torvalds 	spin_unlock(&child->sighand->siglock);
721da177e4SLinus Torvalds }
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds /*
751da177e4SLinus Torvalds  * unptrace a task: move it back to its original parent and
761da177e4SLinus Torvalds  * remove it from the ptrace list.
771da177e4SLinus Torvalds  *
781da177e4SLinus Torvalds  * Must be called with the tasklist lock write-held.
791da177e4SLinus Torvalds  */
8036c8b586SIngo Molnar void __ptrace_unlink(struct task_struct *child)
811da177e4SLinus Torvalds {
825ecfbae0SOleg Nesterov 	BUG_ON(!child->ptrace);
835ecfbae0SOleg Nesterov 
841da177e4SLinus Torvalds 	child->ptrace = 0;
851da177e4SLinus Torvalds 	child->parent = child->real_parent;
86f470021aSRoland McGrath 	list_del_init(&child->ptrace_entry);
871da177e4SLinus Torvalds 
88bf53de90SMarkus Metzger 	arch_ptrace_untrace(child);
896618a3e2SMatthew Wilcox 	if (task_is_traced(child))
901da177e4SLinus Torvalds 		ptrace_untrace(child);
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds /*
941da177e4SLinus Torvalds  * Check that we have indeed attached to the thing..
951da177e4SLinus Torvalds  */
961da177e4SLinus Torvalds int ptrace_check_attach(struct task_struct *child, int kill)
971da177e4SLinus Torvalds {
981da177e4SLinus Torvalds 	int ret = -ESRCH;
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds 	/*
1011da177e4SLinus Torvalds 	 * We take the read lock around doing both checks to close a
1021da177e4SLinus Torvalds 	 * possible race where someone else was tracing our child and
1031da177e4SLinus Torvalds 	 * detached between these two checks.  After this locked check,
1041da177e4SLinus Torvalds 	 * we are sure that this is our traced child and that can only
1051da177e4SLinus Torvalds 	 * be changed by us so it's not changing right after this.
1061da177e4SLinus Torvalds 	 */
1071da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
108c0c0b649SOleg Nesterov 	if ((child->ptrace & PT_PTRACED) && child->parent == current) {
1091da177e4SLinus Torvalds 		ret = 0;
110c0c0b649SOleg Nesterov 		/*
111c0c0b649SOleg Nesterov 		 * child->sighand can't be NULL, release_task()
112c0c0b649SOleg Nesterov 		 * does ptrace_unlink() before __exit_signal().
113c0c0b649SOleg Nesterov 		 */
1141da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
115d9ae90acSOleg Nesterov 		if (task_is_stopped(child))
1161da177e4SLinus Torvalds 			child->state = TASK_TRACED;
117d9ae90acSOleg Nesterov 		else if (!task_is_traced(child) && !kill)
1181da177e4SLinus Torvalds 			ret = -ESRCH;
1191da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
1201da177e4SLinus Torvalds 	}
1211da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
1221da177e4SLinus Torvalds 
123d9ae90acSOleg Nesterov 	if (!ret && !kill)
12485ba2d86SRoland McGrath 		ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH;
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 	/* All systems go.. */
1271da177e4SLinus Torvalds 	return ret;
1281da177e4SLinus Torvalds }
1291da177e4SLinus Torvalds 
130006ebb40SStephen Smalley int __ptrace_may_access(struct task_struct *task, unsigned int mode)
131ab8d11beSMiklos Szeredi {
132c69e8d9cSDavid Howells 	const struct cred *cred = current_cred(), *tcred;
133b6dff3ecSDavid Howells 
134df26c40eSEric W. Biederman 	/* May we inspect the given task?
135df26c40eSEric W. Biederman 	 * This check is used both for attaching with ptrace
136df26c40eSEric W. Biederman 	 * and for allowing access to sensitive information in /proc.
137df26c40eSEric W. Biederman 	 *
138df26c40eSEric W. Biederman 	 * ptrace_attach denies several cases that /proc allows
139df26c40eSEric W. Biederman 	 * because setting up the necessary parent/child relationship
140df26c40eSEric W. Biederman 	 * or halting the specified task is impossible.
141df26c40eSEric W. Biederman 	 */
142df26c40eSEric W. Biederman 	int dumpable = 0;
143df26c40eSEric W. Biederman 	/* Don't let security modules deny introspection */
144df26c40eSEric W. Biederman 	if (task == current)
145df26c40eSEric W. Biederman 		return 0;
146c69e8d9cSDavid Howells 	rcu_read_lock();
147c69e8d9cSDavid Howells 	tcred = __task_cred(task);
148c69e8d9cSDavid Howells 	if ((cred->uid != tcred->euid ||
149c69e8d9cSDavid Howells 	     cred->uid != tcred->suid ||
150c69e8d9cSDavid Howells 	     cred->uid != tcred->uid  ||
151c69e8d9cSDavid Howells 	     cred->gid != tcred->egid ||
152c69e8d9cSDavid Howells 	     cred->gid != tcred->sgid ||
153c69e8d9cSDavid Howells 	     cred->gid != tcred->gid) &&
154c69e8d9cSDavid Howells 	    !capable(CAP_SYS_PTRACE)) {
155c69e8d9cSDavid Howells 		rcu_read_unlock();
156ab8d11beSMiklos Szeredi 		return -EPERM;
157c69e8d9cSDavid Howells 	}
158c69e8d9cSDavid Howells 	rcu_read_unlock();
159ab8d11beSMiklos Szeredi 	smp_rmb();
160df26c40eSEric W. Biederman 	if (task->mm)
1616c5d5238SKawai, Hidehiro 		dumpable = get_dumpable(task->mm);
162df26c40eSEric W. Biederman 	if (!dumpable && !capable(CAP_SYS_PTRACE))
163ab8d11beSMiklos Szeredi 		return -EPERM;
164ab8d11beSMiklos Szeredi 
1655cd9c58fSDavid Howells 	return security_ptrace_may_access(task, mode);
166ab8d11beSMiklos Szeredi }
167ab8d11beSMiklos Szeredi 
168006ebb40SStephen Smalley bool ptrace_may_access(struct task_struct *task, unsigned int mode)
169ab8d11beSMiklos Szeredi {
170ab8d11beSMiklos Szeredi 	int err;
171ab8d11beSMiklos Szeredi 	task_lock(task);
172006ebb40SStephen Smalley 	err = __ptrace_may_access(task, mode);
173ab8d11beSMiklos Szeredi 	task_unlock(task);
1743a709703SRoland McGrath 	return !err;
175ab8d11beSMiklos Szeredi }
176ab8d11beSMiklos Szeredi 
1771da177e4SLinus Torvalds int ptrace_attach(struct task_struct *task)
1781da177e4SLinus Torvalds {
1791da177e4SLinus Torvalds 	int retval;
1806175ecfeSSripathi Kodi 	unsigned long flags;
181f5b40e36SLinus Torvalds 
182a5cb013dSAl Viro 	audit_ptrace(task);
183a5cb013dSAl Viro 
1841da177e4SLinus Torvalds 	retval = -EPERM;
185bac0abd6SPavel Emelyanov 	if (same_thread_group(task, current))
186f5b40e36SLinus Torvalds 		goto out;
187f5b40e36SLinus Torvalds 
188d84f4f99SDavid Howells 	/* Protect exec's credential calculations against our interference;
189d84f4f99SDavid Howells 	 * SUID, SGID and LSM creds get determined differently under ptrace.
190d84f4f99SDavid Howells 	 */
191d84f4f99SDavid Howells 	retval = mutex_lock_interruptible(&current->cred_exec_mutex);
192d84f4f99SDavid Howells 	if (retval  < 0)
193d84f4f99SDavid Howells 		goto out;
194d84f4f99SDavid Howells 
195d84f4f99SDavid Howells 	retval = -EPERM;
196f358166aSLinus Torvalds repeat:
197f358166aSLinus Torvalds 	/*
198f358166aSLinus Torvalds 	 * Nasty, nasty.
199f358166aSLinus Torvalds 	 *
200f358166aSLinus Torvalds 	 * We want to hold both the task-lock and the
201f358166aSLinus Torvalds 	 * tasklist_lock for writing at the same time.
202f358166aSLinus Torvalds 	 * But that's against the rules (tasklist_lock
203f358166aSLinus Torvalds 	 * is taken for reading by interrupts on other
204f358166aSLinus Torvalds 	 * cpu's that may have task_lock).
205f358166aSLinus Torvalds 	 */
206f5b40e36SLinus Torvalds 	task_lock(task);
2076175ecfeSSripathi Kodi 	if (!write_trylock_irqsave(&tasklist_lock, flags)) {
208f358166aSLinus Torvalds 		task_unlock(task);
209f358166aSLinus Torvalds 		do {
210f358166aSLinus Torvalds 			cpu_relax();
211f358166aSLinus Torvalds 		} while (!write_can_lock(&tasklist_lock));
212f358166aSLinus Torvalds 		goto repeat;
213f358166aSLinus Torvalds 	}
214f5b40e36SLinus Torvalds 
215df26c40eSEric W. Biederman 	if (!task->mm)
216df26c40eSEric W. Biederman 		goto bad;
2171da177e4SLinus Torvalds 	/* the same process cannot be attached many times */
2181da177e4SLinus Torvalds 	if (task->ptrace & PT_PTRACED)
2191da177e4SLinus Torvalds 		goto bad;
220006ebb40SStephen Smalley 	retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
2211da177e4SLinus Torvalds 	if (retval)
2221da177e4SLinus Torvalds 		goto bad;
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds 	/* Go */
2256b39c7bfSOleg Nesterov 	task->ptrace |= PT_PTRACED;
2261da177e4SLinus Torvalds 	if (capable(CAP_SYS_PTRACE))
2271da177e4SLinus Torvalds 		task->ptrace |= PT_PTRACE_CAP;
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds 	__ptrace_link(task, current);
2301da177e4SLinus Torvalds 
23133e9fc7dSOleg Nesterov 	send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
2321da177e4SLinus Torvalds bad:
2336175ecfeSSripathi Kodi 	write_unlock_irqrestore(&tasklist_lock, flags);
2341da177e4SLinus Torvalds 	task_unlock(task);
235d84f4f99SDavid Howells 	mutex_unlock(&current->cred_exec_mutex);
236f5b40e36SLinus Torvalds out:
2371da177e4SLinus Torvalds 	return retval;
2381da177e4SLinus Torvalds }
2391da177e4SLinus Torvalds 
24039c626aeSOleg Nesterov /*
24139c626aeSOleg Nesterov  * Called with irqs disabled, returns true if childs should reap themselves.
24239c626aeSOleg Nesterov  */
24339c626aeSOleg Nesterov static int ignoring_children(struct sighand_struct *sigh)
24439c626aeSOleg Nesterov {
24539c626aeSOleg Nesterov 	int ret;
24639c626aeSOleg Nesterov 	spin_lock(&sigh->siglock);
24739c626aeSOleg Nesterov 	ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) ||
24839c626aeSOleg Nesterov 	      (sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT);
24939c626aeSOleg Nesterov 	spin_unlock(&sigh->siglock);
25039c626aeSOleg Nesterov 	return ret;
25139c626aeSOleg Nesterov }
25239c626aeSOleg Nesterov 
25339c626aeSOleg Nesterov /*
25439c626aeSOleg Nesterov  * Called with tasklist_lock held for writing.
25539c626aeSOleg Nesterov  * Unlink a traced task, and clean it up if it was a traced zombie.
25639c626aeSOleg Nesterov  * Return true if it needs to be reaped with release_task().
25739c626aeSOleg Nesterov  * (We can't call release_task() here because we already hold tasklist_lock.)
25839c626aeSOleg Nesterov  *
25939c626aeSOleg Nesterov  * If it's a zombie, our attachedness prevented normal parent notification
26039c626aeSOleg Nesterov  * or self-reaping.  Do notification now if it would have happened earlier.
26139c626aeSOleg Nesterov  * If it should reap itself, return true.
26239c626aeSOleg Nesterov  *
26339c626aeSOleg Nesterov  * If it's our own child, there is no notification to do.
26439c626aeSOleg Nesterov  * But if our normal children self-reap, then this child
26539c626aeSOleg Nesterov  * was prevented by ptrace and we must reap it now.
26639c626aeSOleg Nesterov  */
26739c626aeSOleg Nesterov static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
26839c626aeSOleg Nesterov {
26939c626aeSOleg Nesterov 	__ptrace_unlink(p);
27039c626aeSOleg Nesterov 
27139c626aeSOleg Nesterov 	if (p->exit_state == EXIT_ZOMBIE) {
27239c626aeSOleg Nesterov 		if (!task_detached(p) && thread_group_empty(p)) {
27339c626aeSOleg Nesterov 			if (!same_thread_group(p->real_parent, tracer))
27439c626aeSOleg Nesterov 				do_notify_parent(p, p->exit_signal);
27539c626aeSOleg Nesterov 			else if (ignoring_children(tracer->sighand))
27639c626aeSOleg Nesterov 				p->exit_signal = -1;
27739c626aeSOleg Nesterov 		}
27839c626aeSOleg Nesterov 		if (task_detached(p)) {
27939c626aeSOleg Nesterov 			/* Mark it as in the process of being reaped. */
28039c626aeSOleg Nesterov 			p->exit_state = EXIT_DEAD;
28139c626aeSOleg Nesterov 			return true;
28239c626aeSOleg Nesterov 		}
28339c626aeSOleg Nesterov 	}
28439c626aeSOleg Nesterov 
28539c626aeSOleg Nesterov 	return false;
28639c626aeSOleg Nesterov }
28739c626aeSOleg Nesterov 
2881da177e4SLinus Torvalds int ptrace_detach(struct task_struct *child, unsigned int data)
2891da177e4SLinus Torvalds {
29039c626aeSOleg Nesterov 	bool dead = false;
2914576145cSOleg Nesterov 
2927ed20e1aSJesper Juhl 	if (!valid_signal(data))
2931da177e4SLinus Torvalds 		return -EIO;
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds 	/* Architecture-specific hardware disable .. */
2961da177e4SLinus Torvalds 	ptrace_disable(child);
2977d941432SRoland McGrath 	clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
2981da177e4SLinus Torvalds 
29995c3eb76SOleg Nesterov 	write_lock_irq(&tasklist_lock);
30039c626aeSOleg Nesterov 	/*
30139c626aeSOleg Nesterov 	 * This child can be already killed. Make sure de_thread() or
30239c626aeSOleg Nesterov 	 * our sub-thread doing do_wait() didn't do release_task() yet.
30339c626aeSOleg Nesterov 	 */
30495c3eb76SOleg Nesterov 	if (child->ptrace) {
30595c3eb76SOleg Nesterov 		child->exit_code = data;
3064576145cSOleg Nesterov 		dead = __ptrace_detach(current, child);
30795c3eb76SOleg Nesterov 	}
3081da177e4SLinus Torvalds 	write_unlock_irq(&tasklist_lock);
3091da177e4SLinus Torvalds 
3104576145cSOleg Nesterov 	if (unlikely(dead))
3114576145cSOleg Nesterov 		release_task(child);
3124576145cSOleg Nesterov 
3131da177e4SLinus Torvalds 	return 0;
3141da177e4SLinus Torvalds }
3151da177e4SLinus Torvalds 
31639c626aeSOleg Nesterov /*
31739c626aeSOleg Nesterov  * Detach all tasks we were using ptrace on.
31839c626aeSOleg Nesterov  */
31939c626aeSOleg Nesterov void exit_ptrace(struct task_struct *tracer)
32039c626aeSOleg Nesterov {
32139c626aeSOleg Nesterov 	struct task_struct *p, *n;
32239c626aeSOleg Nesterov 	LIST_HEAD(ptrace_dead);
32339c626aeSOleg Nesterov 
32439c626aeSOleg Nesterov 	write_lock_irq(&tasklist_lock);
32539c626aeSOleg Nesterov 	list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) {
32639c626aeSOleg Nesterov 		if (__ptrace_detach(tracer, p))
32739c626aeSOleg Nesterov 			list_add(&p->ptrace_entry, &ptrace_dead);
32839c626aeSOleg Nesterov 	}
32939c626aeSOleg Nesterov 	write_unlock_irq(&tasklist_lock);
33039c626aeSOleg Nesterov 
33139c626aeSOleg Nesterov 	BUG_ON(!list_empty(&tracer->ptraced));
33239c626aeSOleg Nesterov 
33339c626aeSOleg Nesterov 	list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) {
33439c626aeSOleg Nesterov 		list_del_init(&p->ptrace_entry);
33539c626aeSOleg Nesterov 		release_task(p);
33639c626aeSOleg Nesterov 	}
33739c626aeSOleg Nesterov }
33839c626aeSOleg Nesterov 
3391da177e4SLinus Torvalds int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
3401da177e4SLinus Torvalds {
3411da177e4SLinus Torvalds 	int copied = 0;
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds 	while (len > 0) {
3441da177e4SLinus Torvalds 		char buf[128];
3451da177e4SLinus Torvalds 		int this_len, retval;
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
3481da177e4SLinus Torvalds 		retval = access_process_vm(tsk, src, buf, this_len, 0);
3491da177e4SLinus Torvalds 		if (!retval) {
3501da177e4SLinus Torvalds 			if (copied)
3511da177e4SLinus Torvalds 				break;
3521da177e4SLinus Torvalds 			return -EIO;
3531da177e4SLinus Torvalds 		}
3541da177e4SLinus Torvalds 		if (copy_to_user(dst, buf, retval))
3551da177e4SLinus Torvalds 			return -EFAULT;
3561da177e4SLinus Torvalds 		copied += retval;
3571da177e4SLinus Torvalds 		src += retval;
3581da177e4SLinus Torvalds 		dst += retval;
3591da177e4SLinus Torvalds 		len -= retval;
3601da177e4SLinus Torvalds 	}
3611da177e4SLinus Torvalds 	return copied;
3621da177e4SLinus Torvalds }
3631da177e4SLinus Torvalds 
3641da177e4SLinus Torvalds int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
3651da177e4SLinus Torvalds {
3661da177e4SLinus Torvalds 	int copied = 0;
3671da177e4SLinus Torvalds 
3681da177e4SLinus Torvalds 	while (len > 0) {
3691da177e4SLinus Torvalds 		char buf[128];
3701da177e4SLinus Torvalds 		int this_len, retval;
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
3731da177e4SLinus Torvalds 		if (copy_from_user(buf, src, this_len))
3741da177e4SLinus Torvalds 			return -EFAULT;
3751da177e4SLinus Torvalds 		retval = access_process_vm(tsk, dst, buf, this_len, 1);
3761da177e4SLinus Torvalds 		if (!retval) {
3771da177e4SLinus Torvalds 			if (copied)
3781da177e4SLinus Torvalds 				break;
3791da177e4SLinus Torvalds 			return -EIO;
3801da177e4SLinus Torvalds 		}
3811da177e4SLinus Torvalds 		copied += retval;
3821da177e4SLinus Torvalds 		src += retval;
3831da177e4SLinus Torvalds 		dst += retval;
3841da177e4SLinus Torvalds 		len -= retval;
3851da177e4SLinus Torvalds 	}
3861da177e4SLinus Torvalds 	return copied;
3871da177e4SLinus Torvalds }
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds static int ptrace_setoptions(struct task_struct *child, long data)
3901da177e4SLinus Torvalds {
3911da177e4SLinus Torvalds 	child->ptrace &= ~PT_TRACE_MASK;
3921da177e4SLinus Torvalds 
3931da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACESYSGOOD)
3941da177e4SLinus Torvalds 		child->ptrace |= PT_TRACESYSGOOD;
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEFORK)
3971da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_FORK;
3981da177e4SLinus Torvalds 
3991da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORK)
4001da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK;
4011da177e4SLinus Torvalds 
4021da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACECLONE)
4031da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_CLONE;
4041da177e4SLinus Torvalds 
4051da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXEC)
4061da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXEC;
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORKDONE)
4091da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK_DONE;
4101da177e4SLinus Torvalds 
4111da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXIT)
4121da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXIT;
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds 	return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
4151da177e4SLinus Torvalds }
4161da177e4SLinus Torvalds 
417e16b2781SRoland McGrath static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info)
4181da177e4SLinus Torvalds {
4191da177e4SLinus Torvalds 	int error = -ESRCH;
4201da177e4SLinus Torvalds 
4211da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
4221da177e4SLinus Torvalds 	if (likely(child->sighand != NULL)) {
4231da177e4SLinus Torvalds 		error = -EINVAL;
4241da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
4251da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
426e16b2781SRoland McGrath 			*info = *child->last_siginfo;
4271da177e4SLinus Torvalds 			error = 0;
4281da177e4SLinus Torvalds 		}
4291da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
4301da177e4SLinus Torvalds 	}
4311da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
4321da177e4SLinus Torvalds 	return error;
4331da177e4SLinus Torvalds }
4341da177e4SLinus Torvalds 
435e16b2781SRoland McGrath static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info)
4361da177e4SLinus Torvalds {
4371da177e4SLinus Torvalds 	int error = -ESRCH;
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
4401da177e4SLinus Torvalds 	if (likely(child->sighand != NULL)) {
4411da177e4SLinus Torvalds 		error = -EINVAL;
4421da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
4431da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
444e16b2781SRoland McGrath 			*child->last_siginfo = *info;
4451da177e4SLinus Torvalds 			error = 0;
4461da177e4SLinus Torvalds 		}
4471da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
4481da177e4SLinus Torvalds 	}
4491da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
4501da177e4SLinus Torvalds 	return error;
4511da177e4SLinus Torvalds }
4521da177e4SLinus Torvalds 
45336df29d7SRoland McGrath 
45436df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
45536df29d7SRoland McGrath #define is_singlestep(request)		((request) == PTRACE_SINGLESTEP)
45636df29d7SRoland McGrath #else
45736df29d7SRoland McGrath #define is_singlestep(request)		0
45836df29d7SRoland McGrath #endif
45936df29d7SRoland McGrath 
4605b88abbfSRoland McGrath #ifdef PTRACE_SINGLEBLOCK
4615b88abbfSRoland McGrath #define is_singleblock(request)		((request) == PTRACE_SINGLEBLOCK)
4625b88abbfSRoland McGrath #else
4635b88abbfSRoland McGrath #define is_singleblock(request)		0
4645b88abbfSRoland McGrath #endif
4655b88abbfSRoland McGrath 
46636df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
46736df29d7SRoland McGrath #define is_sysemu_singlestep(request)	((request) == PTRACE_SYSEMU_SINGLESTEP)
46836df29d7SRoland McGrath #else
46936df29d7SRoland McGrath #define is_sysemu_singlestep(request)	0
47036df29d7SRoland McGrath #endif
47136df29d7SRoland McGrath 
47236df29d7SRoland McGrath static int ptrace_resume(struct task_struct *child, long request, long data)
47336df29d7SRoland McGrath {
47436df29d7SRoland McGrath 	if (!valid_signal(data))
47536df29d7SRoland McGrath 		return -EIO;
47636df29d7SRoland McGrath 
47736df29d7SRoland McGrath 	if (request == PTRACE_SYSCALL)
47836df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
47936df29d7SRoland McGrath 	else
48036df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
48136df29d7SRoland McGrath 
48236df29d7SRoland McGrath #ifdef TIF_SYSCALL_EMU
48336df29d7SRoland McGrath 	if (request == PTRACE_SYSEMU || request == PTRACE_SYSEMU_SINGLESTEP)
48436df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_EMU);
48536df29d7SRoland McGrath 	else
48636df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
48736df29d7SRoland McGrath #endif
48836df29d7SRoland McGrath 
4895b88abbfSRoland McGrath 	if (is_singleblock(request)) {
4905b88abbfSRoland McGrath 		if (unlikely(!arch_has_block_step()))
4915b88abbfSRoland McGrath 			return -EIO;
4925b88abbfSRoland McGrath 		user_enable_block_step(child);
4935b88abbfSRoland McGrath 	} else if (is_singlestep(request) || is_sysemu_singlestep(request)) {
49436df29d7SRoland McGrath 		if (unlikely(!arch_has_single_step()))
49536df29d7SRoland McGrath 			return -EIO;
49636df29d7SRoland McGrath 		user_enable_single_step(child);
4973a709703SRoland McGrath 	} else {
49836df29d7SRoland McGrath 		user_disable_single_step(child);
4993a709703SRoland McGrath 	}
50036df29d7SRoland McGrath 
50136df29d7SRoland McGrath 	child->exit_code = data;
50236df29d7SRoland McGrath 	wake_up_process(child);
50336df29d7SRoland McGrath 
50436df29d7SRoland McGrath 	return 0;
50536df29d7SRoland McGrath }
50636df29d7SRoland McGrath 
5071da177e4SLinus Torvalds int ptrace_request(struct task_struct *child, long request,
5081da177e4SLinus Torvalds 		   long addr, long data)
5091da177e4SLinus Torvalds {
5101da177e4SLinus Torvalds 	int ret = -EIO;
511e16b2781SRoland McGrath 	siginfo_t siginfo;
5121da177e4SLinus Torvalds 
5131da177e4SLinus Torvalds 	switch (request) {
51416c3e389SRoland McGrath 	case PTRACE_PEEKTEXT:
51516c3e389SRoland McGrath 	case PTRACE_PEEKDATA:
51616c3e389SRoland McGrath 		return generic_ptrace_peekdata(child, addr, data);
51716c3e389SRoland McGrath 	case PTRACE_POKETEXT:
51816c3e389SRoland McGrath 	case PTRACE_POKEDATA:
51916c3e389SRoland McGrath 		return generic_ptrace_pokedata(child, addr, data);
52016c3e389SRoland McGrath 
5211da177e4SLinus Torvalds #ifdef PTRACE_OLDSETOPTIONS
5221da177e4SLinus Torvalds 	case PTRACE_OLDSETOPTIONS:
5231da177e4SLinus Torvalds #endif
5241da177e4SLinus Torvalds 	case PTRACE_SETOPTIONS:
5251da177e4SLinus Torvalds 		ret = ptrace_setoptions(child, data);
5261da177e4SLinus Torvalds 		break;
5271da177e4SLinus Torvalds 	case PTRACE_GETEVENTMSG:
5281da177e4SLinus Torvalds 		ret = put_user(child->ptrace_message, (unsigned long __user *) data);
5291da177e4SLinus Torvalds 		break;
530e16b2781SRoland McGrath 
5311da177e4SLinus Torvalds 	case PTRACE_GETSIGINFO:
532e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
533e16b2781SRoland McGrath 		if (!ret)
534e16b2781SRoland McGrath 			ret = copy_siginfo_to_user((siginfo_t __user *) data,
535e16b2781SRoland McGrath 						   &siginfo);
5361da177e4SLinus Torvalds 		break;
537e16b2781SRoland McGrath 
5381da177e4SLinus Torvalds 	case PTRACE_SETSIGINFO:
539e16b2781SRoland McGrath 		if (copy_from_user(&siginfo, (siginfo_t __user *) data,
540e16b2781SRoland McGrath 				   sizeof siginfo))
541e16b2781SRoland McGrath 			ret = -EFAULT;
542e16b2781SRoland McGrath 		else
543e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
5441da177e4SLinus Torvalds 		break;
545e16b2781SRoland McGrath 
5461bcf5482SAlexey Dobriyan 	case PTRACE_DETACH:	 /* detach a process that was attached. */
5471bcf5482SAlexey Dobriyan 		ret = ptrace_detach(child, data);
5481bcf5482SAlexey Dobriyan 		break;
54936df29d7SRoland McGrath 
55036df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
55136df29d7SRoland McGrath 	case PTRACE_SINGLESTEP:
55236df29d7SRoland McGrath #endif
5535b88abbfSRoland McGrath #ifdef PTRACE_SINGLEBLOCK
5545b88abbfSRoland McGrath 	case PTRACE_SINGLEBLOCK:
5555b88abbfSRoland McGrath #endif
55636df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
55736df29d7SRoland McGrath 	case PTRACE_SYSEMU:
55836df29d7SRoland McGrath 	case PTRACE_SYSEMU_SINGLESTEP:
55936df29d7SRoland McGrath #endif
56036df29d7SRoland McGrath 	case PTRACE_SYSCALL:
56136df29d7SRoland McGrath 	case PTRACE_CONT:
56236df29d7SRoland McGrath 		return ptrace_resume(child, request, data);
56336df29d7SRoland McGrath 
56436df29d7SRoland McGrath 	case PTRACE_KILL:
56536df29d7SRoland McGrath 		if (child->exit_state)	/* already dead */
56636df29d7SRoland McGrath 			return 0;
56736df29d7SRoland McGrath 		return ptrace_resume(child, request, SIGKILL);
56836df29d7SRoland McGrath 
5691da177e4SLinus Torvalds 	default:
5701da177e4SLinus Torvalds 		break;
5711da177e4SLinus Torvalds 	}
5721da177e4SLinus Torvalds 
5731da177e4SLinus Torvalds 	return ret;
5741da177e4SLinus Torvalds }
575481bed45SChristoph Hellwig 
5766b9c7ed8SChristoph Hellwig /**
5776b9c7ed8SChristoph Hellwig  * ptrace_traceme  --  helper for PTRACE_TRACEME
5786b9c7ed8SChristoph Hellwig  *
5796b9c7ed8SChristoph Hellwig  * Performs checks and sets PT_PTRACED.
5806b9c7ed8SChristoph Hellwig  * Should be used by all ptrace implementations for PTRACE_TRACEME.
5816b9c7ed8SChristoph Hellwig  */
5826b9c7ed8SChristoph Hellwig int ptrace_traceme(void)
583481bed45SChristoph Hellwig {
584f5b40e36SLinus Torvalds 	int ret = -EPERM;
585481bed45SChristoph Hellwig 
586481bed45SChristoph Hellwig 	/*
587481bed45SChristoph Hellwig 	 * Are we already being traced?
588481bed45SChristoph Hellwig 	 */
589f470021aSRoland McGrath repeat:
590f5b40e36SLinus Torvalds 	task_lock(current);
591f5b40e36SLinus Torvalds 	if (!(current->ptrace & PT_PTRACED)) {
592f470021aSRoland McGrath 		/*
593f470021aSRoland McGrath 		 * See ptrace_attach() comments about the locking here.
594f470021aSRoland McGrath 		 */
595f470021aSRoland McGrath 		unsigned long flags;
596f470021aSRoland McGrath 		if (!write_trylock_irqsave(&tasklist_lock, flags)) {
597f470021aSRoland McGrath 			task_unlock(current);
598f470021aSRoland McGrath 			do {
599f470021aSRoland McGrath 				cpu_relax();
600f470021aSRoland McGrath 			} while (!write_can_lock(&tasklist_lock));
601f470021aSRoland McGrath 			goto repeat;
602f470021aSRoland McGrath 		}
603f470021aSRoland McGrath 
6045cd9c58fSDavid Howells 		ret = security_ptrace_traceme(current->parent);
605f470021aSRoland McGrath 
606481bed45SChristoph Hellwig 		/*
607481bed45SChristoph Hellwig 		 * Set the ptrace bit in the process ptrace flags.
608f470021aSRoland McGrath 		 * Then link us on our parent's ptraced list.
609481bed45SChristoph Hellwig 		 */
610f470021aSRoland McGrath 		if (!ret) {
611481bed45SChristoph Hellwig 			current->ptrace |= PT_PTRACED;
612f470021aSRoland McGrath 			__ptrace_link(current, current->real_parent);
613f470021aSRoland McGrath 		}
614f470021aSRoland McGrath 
615f470021aSRoland McGrath 		write_unlock_irqrestore(&tasklist_lock, flags);
616f5b40e36SLinus Torvalds 	}
617f5b40e36SLinus Torvalds 	task_unlock(current);
618f5b40e36SLinus Torvalds 	return ret;
619481bed45SChristoph Hellwig }
620481bed45SChristoph Hellwig 
6216b9c7ed8SChristoph Hellwig /**
6226b9c7ed8SChristoph Hellwig  * ptrace_get_task_struct  --  grab a task struct reference for ptrace
6236b9c7ed8SChristoph Hellwig  * @pid:       process id to grab a task_struct reference of
6246b9c7ed8SChristoph Hellwig  *
6256b9c7ed8SChristoph Hellwig  * This function is a helper for ptrace implementations.  It checks
6266b9c7ed8SChristoph Hellwig  * permissions and then grabs a task struct for use of the actual
6276b9c7ed8SChristoph Hellwig  * ptrace implementation.
6286b9c7ed8SChristoph Hellwig  *
6296b9c7ed8SChristoph Hellwig  * Returns the task_struct for @pid or an ERR_PTR() on failure.
6306b9c7ed8SChristoph Hellwig  */
6316b9c7ed8SChristoph Hellwig struct task_struct *ptrace_get_task_struct(pid_t pid)
6326b9c7ed8SChristoph Hellwig {
6336b9c7ed8SChristoph Hellwig 	struct task_struct *child;
6346b9c7ed8SChristoph Hellwig 
635481bed45SChristoph Hellwig 	read_lock(&tasklist_lock);
636228ebcbeSPavel Emelyanov 	child = find_task_by_vpid(pid);
637481bed45SChristoph Hellwig 	if (child)
638481bed45SChristoph Hellwig 		get_task_struct(child);
639f400e198SSukadev Bhattiprolu 
640481bed45SChristoph Hellwig 	read_unlock(&tasklist_lock);
641481bed45SChristoph Hellwig 	if (!child)
6426b9c7ed8SChristoph Hellwig 		return ERR_PTR(-ESRCH);
6436b9c7ed8SChristoph Hellwig 	return child;
644481bed45SChristoph Hellwig }
645481bed45SChristoph Hellwig 
6460ac15559SChristoph Hellwig #ifndef arch_ptrace_attach
6470ac15559SChristoph Hellwig #define arch_ptrace_attach(child)	do { } while (0)
6480ac15559SChristoph Hellwig #endif
6490ac15559SChristoph Hellwig 
6501e7bfb21SHeiko Carstens SYSCALL_DEFINE4(ptrace, long, request, long, pid, long, addr, long, data)
651481bed45SChristoph Hellwig {
652481bed45SChristoph Hellwig 	struct task_struct *child;
653481bed45SChristoph Hellwig 	long ret;
654481bed45SChristoph Hellwig 
655481bed45SChristoph Hellwig 	/*
656481bed45SChristoph Hellwig 	 * This lock_kernel fixes a subtle race with suid exec
657481bed45SChristoph Hellwig 	 */
658481bed45SChristoph Hellwig 	lock_kernel();
6596b9c7ed8SChristoph Hellwig 	if (request == PTRACE_TRACEME) {
6606b9c7ed8SChristoph Hellwig 		ret = ptrace_traceme();
6616ea6dd93SHaavard Skinnemoen 		if (!ret)
6626ea6dd93SHaavard Skinnemoen 			arch_ptrace_attach(current);
663481bed45SChristoph Hellwig 		goto out;
6646b9c7ed8SChristoph Hellwig 	}
6656b9c7ed8SChristoph Hellwig 
6666b9c7ed8SChristoph Hellwig 	child = ptrace_get_task_struct(pid);
6676b9c7ed8SChristoph Hellwig 	if (IS_ERR(child)) {
6686b9c7ed8SChristoph Hellwig 		ret = PTR_ERR(child);
6696b9c7ed8SChristoph Hellwig 		goto out;
6706b9c7ed8SChristoph Hellwig 	}
671481bed45SChristoph Hellwig 
672481bed45SChristoph Hellwig 	if (request == PTRACE_ATTACH) {
673481bed45SChristoph Hellwig 		ret = ptrace_attach(child);
6740ac15559SChristoph Hellwig 		/*
6750ac15559SChristoph Hellwig 		 * Some architectures need to do book-keeping after
6760ac15559SChristoph Hellwig 		 * a ptrace attach.
6770ac15559SChristoph Hellwig 		 */
6780ac15559SChristoph Hellwig 		if (!ret)
6790ac15559SChristoph Hellwig 			arch_ptrace_attach(child);
680005f18dfSChristoph Hellwig 		goto out_put_task_struct;
681481bed45SChristoph Hellwig 	}
682481bed45SChristoph Hellwig 
683481bed45SChristoph Hellwig 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
684481bed45SChristoph Hellwig 	if (ret < 0)
685481bed45SChristoph Hellwig 		goto out_put_task_struct;
686481bed45SChristoph Hellwig 
687481bed45SChristoph Hellwig 	ret = arch_ptrace(child, request, addr, data);
688481bed45SChristoph Hellwig 
689481bed45SChristoph Hellwig  out_put_task_struct:
690481bed45SChristoph Hellwig 	put_task_struct(child);
691481bed45SChristoph Hellwig  out:
692481bed45SChristoph Hellwig 	unlock_kernel();
693481bed45SChristoph Hellwig 	return ret;
694481bed45SChristoph Hellwig }
69576647323SAlexey Dobriyan 
69676647323SAlexey Dobriyan int generic_ptrace_peekdata(struct task_struct *tsk, long addr, long data)
69776647323SAlexey Dobriyan {
69876647323SAlexey Dobriyan 	unsigned long tmp;
69976647323SAlexey Dobriyan 	int copied;
70076647323SAlexey Dobriyan 
70176647323SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
70276647323SAlexey Dobriyan 	if (copied != sizeof(tmp))
70376647323SAlexey Dobriyan 		return -EIO;
70476647323SAlexey Dobriyan 	return put_user(tmp, (unsigned long __user *)data);
70576647323SAlexey Dobriyan }
706f284ce72SAlexey Dobriyan 
707f284ce72SAlexey Dobriyan int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data)
708f284ce72SAlexey Dobriyan {
709f284ce72SAlexey Dobriyan 	int copied;
710f284ce72SAlexey Dobriyan 
711f284ce72SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
712f284ce72SAlexey Dobriyan 	return (copied == sizeof(data)) ? 0 : -EIO;
713f284ce72SAlexey Dobriyan }
714032d82d9SRoland McGrath 
71596b8936aSChristoph Hellwig #if defined CONFIG_COMPAT
716032d82d9SRoland McGrath #include <linux/compat.h>
717032d82d9SRoland McGrath 
718032d82d9SRoland McGrath int compat_ptrace_request(struct task_struct *child, compat_long_t request,
719032d82d9SRoland McGrath 			  compat_ulong_t addr, compat_ulong_t data)
720032d82d9SRoland McGrath {
721032d82d9SRoland McGrath 	compat_ulong_t __user *datap = compat_ptr(data);
722032d82d9SRoland McGrath 	compat_ulong_t word;
723e16b2781SRoland McGrath 	siginfo_t siginfo;
724032d82d9SRoland McGrath 	int ret;
725032d82d9SRoland McGrath 
726032d82d9SRoland McGrath 	switch (request) {
727032d82d9SRoland McGrath 	case PTRACE_PEEKTEXT:
728032d82d9SRoland McGrath 	case PTRACE_PEEKDATA:
729032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &word, sizeof(word), 0);
730032d82d9SRoland McGrath 		if (ret != sizeof(word))
731032d82d9SRoland McGrath 			ret = -EIO;
732032d82d9SRoland McGrath 		else
733032d82d9SRoland McGrath 			ret = put_user(word, datap);
734032d82d9SRoland McGrath 		break;
735032d82d9SRoland McGrath 
736032d82d9SRoland McGrath 	case PTRACE_POKETEXT:
737032d82d9SRoland McGrath 	case PTRACE_POKEDATA:
738032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &data, sizeof(data), 1);
739032d82d9SRoland McGrath 		ret = (ret != sizeof(data) ? -EIO : 0);
740032d82d9SRoland McGrath 		break;
741032d82d9SRoland McGrath 
742032d82d9SRoland McGrath 	case PTRACE_GETEVENTMSG:
743032d82d9SRoland McGrath 		ret = put_user((compat_ulong_t) child->ptrace_message, datap);
744032d82d9SRoland McGrath 		break;
745032d82d9SRoland McGrath 
746e16b2781SRoland McGrath 	case PTRACE_GETSIGINFO:
747e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
748e16b2781SRoland McGrath 		if (!ret)
749e16b2781SRoland McGrath 			ret = copy_siginfo_to_user32(
750e16b2781SRoland McGrath 				(struct compat_siginfo __user *) datap,
751e16b2781SRoland McGrath 				&siginfo);
752e16b2781SRoland McGrath 		break;
753e16b2781SRoland McGrath 
754e16b2781SRoland McGrath 	case PTRACE_SETSIGINFO:
755e16b2781SRoland McGrath 		memset(&siginfo, 0, sizeof siginfo);
756e16b2781SRoland McGrath 		if (copy_siginfo_from_user32(
757e16b2781SRoland McGrath 			    &siginfo, (struct compat_siginfo __user *) datap))
758e16b2781SRoland McGrath 			ret = -EFAULT;
759e16b2781SRoland McGrath 		else
760e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
761e16b2781SRoland McGrath 		break;
762e16b2781SRoland McGrath 
763032d82d9SRoland McGrath 	default:
764032d82d9SRoland McGrath 		ret = ptrace_request(child, request, addr, data);
765032d82d9SRoland McGrath 	}
766032d82d9SRoland McGrath 
767032d82d9SRoland McGrath 	return ret;
768032d82d9SRoland McGrath }
769c269f196SRoland McGrath 
770c269f196SRoland McGrath asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
771c269f196SRoland McGrath 				  compat_long_t addr, compat_long_t data)
772c269f196SRoland McGrath {
773c269f196SRoland McGrath 	struct task_struct *child;
774c269f196SRoland McGrath 	long ret;
775c269f196SRoland McGrath 
776c269f196SRoland McGrath 	/*
777c269f196SRoland McGrath 	 * This lock_kernel fixes a subtle race with suid exec
778c269f196SRoland McGrath 	 */
779c269f196SRoland McGrath 	lock_kernel();
780c269f196SRoland McGrath 	if (request == PTRACE_TRACEME) {
781c269f196SRoland McGrath 		ret = ptrace_traceme();
782c269f196SRoland McGrath 		goto out;
783c269f196SRoland McGrath 	}
784c269f196SRoland McGrath 
785c269f196SRoland McGrath 	child = ptrace_get_task_struct(pid);
786c269f196SRoland McGrath 	if (IS_ERR(child)) {
787c269f196SRoland McGrath 		ret = PTR_ERR(child);
788c269f196SRoland McGrath 		goto out;
789c269f196SRoland McGrath 	}
790c269f196SRoland McGrath 
791c269f196SRoland McGrath 	if (request == PTRACE_ATTACH) {
792c269f196SRoland McGrath 		ret = ptrace_attach(child);
793c269f196SRoland McGrath 		/*
794c269f196SRoland McGrath 		 * Some architectures need to do book-keeping after
795c269f196SRoland McGrath 		 * a ptrace attach.
796c269f196SRoland McGrath 		 */
797c269f196SRoland McGrath 		if (!ret)
798c269f196SRoland McGrath 			arch_ptrace_attach(child);
799c269f196SRoland McGrath 		goto out_put_task_struct;
800c269f196SRoland McGrath 	}
801c269f196SRoland McGrath 
802c269f196SRoland McGrath 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
803c269f196SRoland McGrath 	if (!ret)
804c269f196SRoland McGrath 		ret = compat_arch_ptrace(child, request, addr, data);
805c269f196SRoland McGrath 
806c269f196SRoland McGrath  out_put_task_struct:
807c269f196SRoland McGrath 	put_task_struct(child);
808c269f196SRoland McGrath  out:
809c269f196SRoland McGrath 	unlock_kernel();
810c269f196SRoland McGrath 	return ret;
811c269f196SRoland McGrath }
81296b8936aSChristoph Hellwig #endif	/* CONFIG_COMPAT */
813