xref: /openbmc/linux/kernel/ptrace.c (revision f2f0b00a)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/kernel/ptrace.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * (C) Copyright 1999 Linus Torvalds
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Common interfaces for "ptrace()" which we do not want
71da177e4SLinus Torvalds  * to continually duplicate across every architecture.
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
10c59ede7bSRandy.Dunlap #include <linux/capability.h>
111da177e4SLinus Torvalds #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/sched.h>
131da177e4SLinus Torvalds #include <linux/errno.h>
141da177e4SLinus Torvalds #include <linux/mm.h>
151da177e4SLinus Torvalds #include <linux/highmem.h>
161da177e4SLinus Torvalds #include <linux/pagemap.h>
171da177e4SLinus Torvalds #include <linux/smp_lock.h>
181da177e4SLinus Torvalds #include <linux/ptrace.h>
191da177e4SLinus Torvalds #include <linux/security.h>
207ed20e1aSJesper Juhl #include <linux/signal.h>
21a5cb013dSAl Viro #include <linux/audit.h>
22b488893aSPavel Emelyanov #include <linux/pid_namespace.h>
23f17d30a8SAdrian Bunk #include <linux/syscalls.h>
243a709703SRoland McGrath #include <linux/uaccess.h>
251da177e4SLinus Torvalds 
26bf53de90SMarkus Metzger 
27bf53de90SMarkus Metzger /*
281da177e4SLinus Torvalds  * ptrace a task: make the debugger its new parent and
291da177e4SLinus Torvalds  * move it to the ptrace list.
301da177e4SLinus Torvalds  *
311da177e4SLinus Torvalds  * Must be called with the tasklist lock write-held.
321da177e4SLinus Torvalds  */
3336c8b586SIngo Molnar void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
341da177e4SLinus Torvalds {
35f470021aSRoland McGrath 	BUG_ON(!list_empty(&child->ptrace_entry));
36f470021aSRoland McGrath 	list_add(&child->ptrace_entry, &new_parent->ptraced);
371da177e4SLinus Torvalds 	child->parent = new_parent;
381da177e4SLinus Torvalds }
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds /*
411da177e4SLinus Torvalds  * Turn a tracing stop into a normal stop now, since with no tracer there
421da177e4SLinus Torvalds  * would be no way to wake it up with SIGCONT or SIGKILL.  If there was a
431da177e4SLinus Torvalds  * signal sent that would resume the child, but didn't because it was in
441da177e4SLinus Torvalds  * TASK_TRACED, resume it now.
451da177e4SLinus Torvalds  * Requires that irqs be disabled.
461da177e4SLinus Torvalds  */
47b747c8c1SAdrian Bunk static void ptrace_untrace(struct task_struct *child)
481da177e4SLinus Torvalds {
491da177e4SLinus Torvalds 	spin_lock(&child->sighand->siglock);
506618a3e2SMatthew Wilcox 	if (task_is_traced(child)) {
511ee11844SOleg Nesterov 		/*
521ee11844SOleg Nesterov 		 * If the group stop is completed or in progress,
531ee11844SOleg Nesterov 		 * this thread was already counted as stopped.
541ee11844SOleg Nesterov 		 */
551ee11844SOleg Nesterov 		if (child->signal->flags & SIGNAL_STOP_STOPPED ||
561ee11844SOleg Nesterov 		    child->signal->group_stop_count)
57d9ae90acSOleg Nesterov 			__set_task_state(child, TASK_STOPPED);
581ee11844SOleg Nesterov 		else
591da177e4SLinus Torvalds 			signal_wake_up(child, 1);
601da177e4SLinus Torvalds 	}
611da177e4SLinus Torvalds 	spin_unlock(&child->sighand->siglock);
621da177e4SLinus Torvalds }
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds /*
651da177e4SLinus Torvalds  * unptrace a task: move it back to its original parent and
661da177e4SLinus Torvalds  * remove it from the ptrace list.
671da177e4SLinus Torvalds  *
681da177e4SLinus Torvalds  * Must be called with the tasklist lock write-held.
691da177e4SLinus Torvalds  */
7036c8b586SIngo Molnar void __ptrace_unlink(struct task_struct *child)
711da177e4SLinus Torvalds {
725ecfbae0SOleg Nesterov 	BUG_ON(!child->ptrace);
735ecfbae0SOleg Nesterov 
741da177e4SLinus Torvalds 	child->ptrace = 0;
751da177e4SLinus Torvalds 	child->parent = child->real_parent;
76f470021aSRoland McGrath 	list_del_init(&child->ptrace_entry);
771da177e4SLinus Torvalds 
78bf53de90SMarkus Metzger 	arch_ptrace_untrace(child);
796618a3e2SMatthew Wilcox 	if (task_is_traced(child))
801da177e4SLinus Torvalds 		ptrace_untrace(child);
811da177e4SLinus Torvalds }
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds /*
841da177e4SLinus Torvalds  * Check that we have indeed attached to the thing..
851da177e4SLinus Torvalds  */
861da177e4SLinus Torvalds int ptrace_check_attach(struct task_struct *child, int kill)
871da177e4SLinus Torvalds {
881da177e4SLinus Torvalds 	int ret = -ESRCH;
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds 	/*
911da177e4SLinus Torvalds 	 * We take the read lock around doing both checks to close a
921da177e4SLinus Torvalds 	 * possible race where someone else was tracing our child and
931da177e4SLinus Torvalds 	 * detached between these two checks.  After this locked check,
941da177e4SLinus Torvalds 	 * we are sure that this is our traced child and that can only
951da177e4SLinus Torvalds 	 * be changed by us so it's not changing right after this.
961da177e4SLinus Torvalds 	 */
971da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
98c0c0b649SOleg Nesterov 	if ((child->ptrace & PT_PTRACED) && child->parent == current) {
991da177e4SLinus Torvalds 		ret = 0;
100c0c0b649SOleg Nesterov 		/*
101c0c0b649SOleg Nesterov 		 * child->sighand can't be NULL, release_task()
102c0c0b649SOleg Nesterov 		 * does ptrace_unlink() before __exit_signal().
103c0c0b649SOleg Nesterov 		 */
1041da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
105d9ae90acSOleg Nesterov 		if (task_is_stopped(child))
1061da177e4SLinus Torvalds 			child->state = TASK_TRACED;
107d9ae90acSOleg Nesterov 		else if (!task_is_traced(child) && !kill)
1081da177e4SLinus Torvalds 			ret = -ESRCH;
1091da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
1101da177e4SLinus Torvalds 	}
1111da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
1121da177e4SLinus Torvalds 
113d9ae90acSOleg Nesterov 	if (!ret && !kill)
11485ba2d86SRoland McGrath 		ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH;
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 	/* All systems go.. */
1171da177e4SLinus Torvalds 	return ret;
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds 
120006ebb40SStephen Smalley int __ptrace_may_access(struct task_struct *task, unsigned int mode)
121ab8d11beSMiklos Szeredi {
122c69e8d9cSDavid Howells 	const struct cred *cred = current_cred(), *tcred;
123b6dff3ecSDavid Howells 
124df26c40eSEric W. Biederman 	/* May we inspect the given task?
125df26c40eSEric W. Biederman 	 * This check is used both for attaching with ptrace
126df26c40eSEric W. Biederman 	 * and for allowing access to sensitive information in /proc.
127df26c40eSEric W. Biederman 	 *
128df26c40eSEric W. Biederman 	 * ptrace_attach denies several cases that /proc allows
129df26c40eSEric W. Biederman 	 * because setting up the necessary parent/child relationship
130df26c40eSEric W. Biederman 	 * or halting the specified task is impossible.
131df26c40eSEric W. Biederman 	 */
132df26c40eSEric W. Biederman 	int dumpable = 0;
133df26c40eSEric W. Biederman 	/* Don't let security modules deny introspection */
134df26c40eSEric W. Biederman 	if (task == current)
135df26c40eSEric W. Biederman 		return 0;
136c69e8d9cSDavid Howells 	rcu_read_lock();
137c69e8d9cSDavid Howells 	tcred = __task_cred(task);
138c69e8d9cSDavid Howells 	if ((cred->uid != tcred->euid ||
139c69e8d9cSDavid Howells 	     cred->uid != tcred->suid ||
140c69e8d9cSDavid Howells 	     cred->uid != tcred->uid  ||
141c69e8d9cSDavid Howells 	     cred->gid != tcred->egid ||
142c69e8d9cSDavid Howells 	     cred->gid != tcred->sgid ||
143c69e8d9cSDavid Howells 	     cred->gid != tcred->gid) &&
144c69e8d9cSDavid Howells 	    !capable(CAP_SYS_PTRACE)) {
145c69e8d9cSDavid Howells 		rcu_read_unlock();
146ab8d11beSMiklos Szeredi 		return -EPERM;
147c69e8d9cSDavid Howells 	}
148c69e8d9cSDavid Howells 	rcu_read_unlock();
149ab8d11beSMiklos Szeredi 	smp_rmb();
150df26c40eSEric W. Biederman 	if (task->mm)
1516c5d5238SKawai, Hidehiro 		dumpable = get_dumpable(task->mm);
152df26c40eSEric W. Biederman 	if (!dumpable && !capable(CAP_SYS_PTRACE))
153ab8d11beSMiklos Szeredi 		return -EPERM;
154ab8d11beSMiklos Szeredi 
1555cd9c58fSDavid Howells 	return security_ptrace_may_access(task, mode);
156ab8d11beSMiklos Szeredi }
157ab8d11beSMiklos Szeredi 
158006ebb40SStephen Smalley bool ptrace_may_access(struct task_struct *task, unsigned int mode)
159ab8d11beSMiklos Szeredi {
160ab8d11beSMiklos Szeredi 	int err;
161ab8d11beSMiklos Szeredi 	task_lock(task);
162006ebb40SStephen Smalley 	err = __ptrace_may_access(task, mode);
163ab8d11beSMiklos Szeredi 	task_unlock(task);
1643a709703SRoland McGrath 	return !err;
165ab8d11beSMiklos Szeredi }
166ab8d11beSMiklos Szeredi 
1671da177e4SLinus Torvalds int ptrace_attach(struct task_struct *task)
1681da177e4SLinus Torvalds {
1691da177e4SLinus Torvalds 	int retval;
1706175ecfeSSripathi Kodi 	unsigned long flags;
171f5b40e36SLinus Torvalds 
172a5cb013dSAl Viro 	audit_ptrace(task);
173a5cb013dSAl Viro 
1741da177e4SLinus Torvalds 	retval = -EPERM;
175b79b7ba9SOleg Nesterov 	if (unlikely(task->flags & PF_KTHREAD))
176b79b7ba9SOleg Nesterov 		goto out;
177bac0abd6SPavel Emelyanov 	if (same_thread_group(task, current))
178f5b40e36SLinus Torvalds 		goto out;
179f5b40e36SLinus Torvalds 
180f2f0b00aSOleg Nesterov 	/*
181f2f0b00aSOleg Nesterov 	 * Protect exec's credential calculations against our interference;
1825e751e99SDavid Howells 	 * interference; SUID, SGID and LSM creds get determined differently
1835e751e99SDavid Howells 	 * under ptrace.
184d84f4f99SDavid Howells 	 */
1855e751e99SDavid Howells 	retval = mutex_lock_interruptible(&task->cred_guard_mutex);
186d84f4f99SDavid Howells 	if (retval < 0)
187d84f4f99SDavid Howells 		goto out;
188f358166aSLinus Torvalds repeat:
189f358166aSLinus Torvalds 	/*
190f358166aSLinus Torvalds 	 * Nasty, nasty.
191f358166aSLinus Torvalds 	 *
192f358166aSLinus Torvalds 	 * We want to hold both the task-lock and the
193f358166aSLinus Torvalds 	 * tasklist_lock for writing at the same time.
194f358166aSLinus Torvalds 	 * But that's against the rules (tasklist_lock
195f358166aSLinus Torvalds 	 * is taken for reading by interrupts on other
196f358166aSLinus Torvalds 	 * cpu's that may have task_lock).
197f358166aSLinus Torvalds 	 */
198f5b40e36SLinus Torvalds 	task_lock(task);
1996175ecfeSSripathi Kodi 	if (!write_trylock_irqsave(&tasklist_lock, flags)) {
200f358166aSLinus Torvalds 		task_unlock(task);
201f358166aSLinus Torvalds 		do {
202f358166aSLinus Torvalds 			cpu_relax();
203f358166aSLinus Torvalds 		} while (!write_can_lock(&tasklist_lock));
204f358166aSLinus Torvalds 		goto repeat;
205f358166aSLinus Torvalds 	}
206f5b40e36SLinus Torvalds 
207006ebb40SStephen Smalley 	retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
2081da177e4SLinus Torvalds 	if (retval)
2091da177e4SLinus Torvalds 		goto bad;
2101da177e4SLinus Torvalds 
211b79b7ba9SOleg Nesterov 	retval = -EPERM;
212b79b7ba9SOleg Nesterov 	if (unlikely(task->exit_state))
213b79b7ba9SOleg Nesterov 		goto bad;
214f2f0b00aSOleg Nesterov 	if (task->ptrace)
215b79b7ba9SOleg Nesterov 		goto bad;
216b79b7ba9SOleg Nesterov 
217f2f0b00aSOleg Nesterov 	task->ptrace = PT_PTRACED;
2181da177e4SLinus Torvalds 	if (capable(CAP_SYS_PTRACE))
2191da177e4SLinus Torvalds 		task->ptrace |= PT_PTRACE_CAP;
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds 	__ptrace_link(task, current);
22233e9fc7dSOleg Nesterov 	send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
223b79b7ba9SOleg Nesterov 
224b79b7ba9SOleg Nesterov 	retval = 0;
2251da177e4SLinus Torvalds bad:
2266175ecfeSSripathi Kodi 	write_unlock_irqrestore(&tasklist_lock, flags);
2271da177e4SLinus Torvalds 	task_unlock(task);
2285e751e99SDavid Howells 	mutex_unlock(&task->cred_guard_mutex);
229f5b40e36SLinus Torvalds out:
2301da177e4SLinus Torvalds 	return retval;
2311da177e4SLinus Torvalds }
2321da177e4SLinus Torvalds 
233f2f0b00aSOleg Nesterov /**
234f2f0b00aSOleg Nesterov  * ptrace_traceme  --  helper for PTRACE_TRACEME
235f2f0b00aSOleg Nesterov  *
236f2f0b00aSOleg Nesterov  * Performs checks and sets PT_PTRACED.
237f2f0b00aSOleg Nesterov  * Should be used by all ptrace implementations for PTRACE_TRACEME.
238f2f0b00aSOleg Nesterov  */
239f2f0b00aSOleg Nesterov int ptrace_traceme(void)
240f2f0b00aSOleg Nesterov {
241f2f0b00aSOleg Nesterov 	int ret = -EPERM;
242f2f0b00aSOleg Nesterov 
243f2f0b00aSOleg Nesterov 	/*
244f2f0b00aSOleg Nesterov 	 * Are we already being traced?
245f2f0b00aSOleg Nesterov 	 */
246f2f0b00aSOleg Nesterov repeat:
247f2f0b00aSOleg Nesterov 	task_lock(current);
248f2f0b00aSOleg Nesterov 	if (!current->ptrace) {
249f2f0b00aSOleg Nesterov 		/*
250f2f0b00aSOleg Nesterov 		 * See ptrace_attach() comments about the locking here.
251f2f0b00aSOleg Nesterov 		 */
252f2f0b00aSOleg Nesterov 		unsigned long flags;
253f2f0b00aSOleg Nesterov 		if (!write_trylock_irqsave(&tasklist_lock, flags)) {
254f2f0b00aSOleg Nesterov 			task_unlock(current);
255f2f0b00aSOleg Nesterov 			do {
256f2f0b00aSOleg Nesterov 				cpu_relax();
257f2f0b00aSOleg Nesterov 			} while (!write_can_lock(&tasklist_lock));
258f2f0b00aSOleg Nesterov 			goto repeat;
259f2f0b00aSOleg Nesterov 		}
260f2f0b00aSOleg Nesterov 
261f2f0b00aSOleg Nesterov 		ret = security_ptrace_traceme(current->parent);
262f2f0b00aSOleg Nesterov 
263f2f0b00aSOleg Nesterov 		/*
264f2f0b00aSOleg Nesterov 		 * Check PF_EXITING to ensure ->real_parent has not passed
265f2f0b00aSOleg Nesterov 		 * exit_ptrace(). Otherwise we don't report the error but
266f2f0b00aSOleg Nesterov 		 * pretend ->real_parent untraces us right after return.
267f2f0b00aSOleg Nesterov 		 */
268f2f0b00aSOleg Nesterov 		if (!ret && !(current->real_parent->flags & PF_EXITING)) {
269f2f0b00aSOleg Nesterov 			current->ptrace = PT_PTRACED;
270f2f0b00aSOleg Nesterov 			__ptrace_link(current, current->real_parent);
271f2f0b00aSOleg Nesterov 		}
272f2f0b00aSOleg Nesterov 
273f2f0b00aSOleg Nesterov 		write_unlock_irqrestore(&tasklist_lock, flags);
274f2f0b00aSOleg Nesterov 	}
275f2f0b00aSOleg Nesterov 	task_unlock(current);
276f2f0b00aSOleg Nesterov 	return ret;
277f2f0b00aSOleg Nesterov }
278f2f0b00aSOleg Nesterov 
27939c626aeSOleg Nesterov /*
28039c626aeSOleg Nesterov  * Called with irqs disabled, returns true if childs should reap themselves.
28139c626aeSOleg Nesterov  */
28239c626aeSOleg Nesterov static int ignoring_children(struct sighand_struct *sigh)
28339c626aeSOleg Nesterov {
28439c626aeSOleg Nesterov 	int ret;
28539c626aeSOleg Nesterov 	spin_lock(&sigh->siglock);
28639c626aeSOleg Nesterov 	ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) ||
28739c626aeSOleg Nesterov 	      (sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT);
28839c626aeSOleg Nesterov 	spin_unlock(&sigh->siglock);
28939c626aeSOleg Nesterov 	return ret;
29039c626aeSOleg Nesterov }
29139c626aeSOleg Nesterov 
29239c626aeSOleg Nesterov /*
29339c626aeSOleg Nesterov  * Called with tasklist_lock held for writing.
29439c626aeSOleg Nesterov  * Unlink a traced task, and clean it up if it was a traced zombie.
29539c626aeSOleg Nesterov  * Return true if it needs to be reaped with release_task().
29639c626aeSOleg Nesterov  * (We can't call release_task() here because we already hold tasklist_lock.)
29739c626aeSOleg Nesterov  *
29839c626aeSOleg Nesterov  * If it's a zombie, our attachedness prevented normal parent notification
29939c626aeSOleg Nesterov  * or self-reaping.  Do notification now if it would have happened earlier.
30039c626aeSOleg Nesterov  * If it should reap itself, return true.
30139c626aeSOleg Nesterov  *
30239c626aeSOleg Nesterov  * If it's our own child, there is no notification to do.
30339c626aeSOleg Nesterov  * But if our normal children self-reap, then this child
30439c626aeSOleg Nesterov  * was prevented by ptrace and we must reap it now.
30539c626aeSOleg Nesterov  */
30639c626aeSOleg Nesterov static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
30739c626aeSOleg Nesterov {
30839c626aeSOleg Nesterov 	__ptrace_unlink(p);
30939c626aeSOleg Nesterov 
31039c626aeSOleg Nesterov 	if (p->exit_state == EXIT_ZOMBIE) {
31139c626aeSOleg Nesterov 		if (!task_detached(p) && thread_group_empty(p)) {
31239c626aeSOleg Nesterov 			if (!same_thread_group(p->real_parent, tracer))
31339c626aeSOleg Nesterov 				do_notify_parent(p, p->exit_signal);
31439c626aeSOleg Nesterov 			else if (ignoring_children(tracer->sighand))
31539c626aeSOleg Nesterov 				p->exit_signal = -1;
31639c626aeSOleg Nesterov 		}
31739c626aeSOleg Nesterov 		if (task_detached(p)) {
31839c626aeSOleg Nesterov 			/* Mark it as in the process of being reaped. */
31939c626aeSOleg Nesterov 			p->exit_state = EXIT_DEAD;
32039c626aeSOleg Nesterov 			return true;
32139c626aeSOleg Nesterov 		}
32239c626aeSOleg Nesterov 	}
32339c626aeSOleg Nesterov 
32439c626aeSOleg Nesterov 	return false;
32539c626aeSOleg Nesterov }
32639c626aeSOleg Nesterov 
3271da177e4SLinus Torvalds int ptrace_detach(struct task_struct *child, unsigned int data)
3281da177e4SLinus Torvalds {
32939c626aeSOleg Nesterov 	bool dead = false;
3304576145cSOleg Nesterov 
3317ed20e1aSJesper Juhl 	if (!valid_signal(data))
3321da177e4SLinus Torvalds 		return -EIO;
3331da177e4SLinus Torvalds 
3341da177e4SLinus Torvalds 	/* Architecture-specific hardware disable .. */
3351da177e4SLinus Torvalds 	ptrace_disable(child);
3367d941432SRoland McGrath 	clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
3371da177e4SLinus Torvalds 
33895c3eb76SOleg Nesterov 	write_lock_irq(&tasklist_lock);
33939c626aeSOleg Nesterov 	/*
34039c626aeSOleg Nesterov 	 * This child can be already killed. Make sure de_thread() or
34139c626aeSOleg Nesterov 	 * our sub-thread doing do_wait() didn't do release_task() yet.
34239c626aeSOleg Nesterov 	 */
34395c3eb76SOleg Nesterov 	if (child->ptrace) {
34495c3eb76SOleg Nesterov 		child->exit_code = data;
3454576145cSOleg Nesterov 		dead = __ptrace_detach(current, child);
346edaba2c5SOleg Nesterov 		if (!child->exit_state)
347edaba2c5SOleg Nesterov 			wake_up_process(child);
34895c3eb76SOleg Nesterov 	}
3491da177e4SLinus Torvalds 	write_unlock_irq(&tasklist_lock);
3501da177e4SLinus Torvalds 
3514576145cSOleg Nesterov 	if (unlikely(dead))
3524576145cSOleg Nesterov 		release_task(child);
3534576145cSOleg Nesterov 
3541da177e4SLinus Torvalds 	return 0;
3551da177e4SLinus Torvalds }
3561da177e4SLinus Torvalds 
35739c626aeSOleg Nesterov /*
35839c626aeSOleg Nesterov  * Detach all tasks we were using ptrace on.
35939c626aeSOleg Nesterov  */
36039c626aeSOleg Nesterov void exit_ptrace(struct task_struct *tracer)
36139c626aeSOleg Nesterov {
36239c626aeSOleg Nesterov 	struct task_struct *p, *n;
36339c626aeSOleg Nesterov 	LIST_HEAD(ptrace_dead);
36439c626aeSOleg Nesterov 
36539c626aeSOleg Nesterov 	write_lock_irq(&tasklist_lock);
36639c626aeSOleg Nesterov 	list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) {
36739c626aeSOleg Nesterov 		if (__ptrace_detach(tracer, p))
36839c626aeSOleg Nesterov 			list_add(&p->ptrace_entry, &ptrace_dead);
36939c626aeSOleg Nesterov 	}
37039c626aeSOleg Nesterov 	write_unlock_irq(&tasklist_lock);
37139c626aeSOleg Nesterov 
37239c626aeSOleg Nesterov 	BUG_ON(!list_empty(&tracer->ptraced));
37339c626aeSOleg Nesterov 
37439c626aeSOleg Nesterov 	list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) {
37539c626aeSOleg Nesterov 		list_del_init(&p->ptrace_entry);
37639c626aeSOleg Nesterov 		release_task(p);
37739c626aeSOleg Nesterov 	}
37839c626aeSOleg Nesterov }
37939c626aeSOleg Nesterov 
3801da177e4SLinus Torvalds int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
3811da177e4SLinus Torvalds {
3821da177e4SLinus Torvalds 	int copied = 0;
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds 	while (len > 0) {
3851da177e4SLinus Torvalds 		char buf[128];
3861da177e4SLinus Torvalds 		int this_len, retval;
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
3891da177e4SLinus Torvalds 		retval = access_process_vm(tsk, src, buf, this_len, 0);
3901da177e4SLinus Torvalds 		if (!retval) {
3911da177e4SLinus Torvalds 			if (copied)
3921da177e4SLinus Torvalds 				break;
3931da177e4SLinus Torvalds 			return -EIO;
3941da177e4SLinus Torvalds 		}
3951da177e4SLinus Torvalds 		if (copy_to_user(dst, buf, retval))
3961da177e4SLinus Torvalds 			return -EFAULT;
3971da177e4SLinus Torvalds 		copied += retval;
3981da177e4SLinus Torvalds 		src += retval;
3991da177e4SLinus Torvalds 		dst += retval;
4001da177e4SLinus Torvalds 		len -= retval;
4011da177e4SLinus Torvalds 	}
4021da177e4SLinus Torvalds 	return copied;
4031da177e4SLinus Torvalds }
4041da177e4SLinus Torvalds 
4051da177e4SLinus Torvalds int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
4061da177e4SLinus Torvalds {
4071da177e4SLinus Torvalds 	int copied = 0;
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds 	while (len > 0) {
4101da177e4SLinus Torvalds 		char buf[128];
4111da177e4SLinus Torvalds 		int this_len, retval;
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
4141da177e4SLinus Torvalds 		if (copy_from_user(buf, src, this_len))
4151da177e4SLinus Torvalds 			return -EFAULT;
4161da177e4SLinus Torvalds 		retval = access_process_vm(tsk, dst, buf, this_len, 1);
4171da177e4SLinus Torvalds 		if (!retval) {
4181da177e4SLinus Torvalds 			if (copied)
4191da177e4SLinus Torvalds 				break;
4201da177e4SLinus Torvalds 			return -EIO;
4211da177e4SLinus Torvalds 		}
4221da177e4SLinus Torvalds 		copied += retval;
4231da177e4SLinus Torvalds 		src += retval;
4241da177e4SLinus Torvalds 		dst += retval;
4251da177e4SLinus Torvalds 		len -= retval;
4261da177e4SLinus Torvalds 	}
4271da177e4SLinus Torvalds 	return copied;
4281da177e4SLinus Torvalds }
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds static int ptrace_setoptions(struct task_struct *child, long data)
4311da177e4SLinus Torvalds {
4321da177e4SLinus Torvalds 	child->ptrace &= ~PT_TRACE_MASK;
4331da177e4SLinus Torvalds 
4341da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACESYSGOOD)
4351da177e4SLinus Torvalds 		child->ptrace |= PT_TRACESYSGOOD;
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEFORK)
4381da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_FORK;
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORK)
4411da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK;
4421da177e4SLinus Torvalds 
4431da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACECLONE)
4441da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_CLONE;
4451da177e4SLinus Torvalds 
4461da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXEC)
4471da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXEC;
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORKDONE)
4501da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK_DONE;
4511da177e4SLinus Torvalds 
4521da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXIT)
4531da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXIT;
4541da177e4SLinus Torvalds 
4551da177e4SLinus Torvalds 	return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
4561da177e4SLinus Torvalds }
4571da177e4SLinus Torvalds 
458e16b2781SRoland McGrath static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info)
4591da177e4SLinus Torvalds {
4601da177e4SLinus Torvalds 	int error = -ESRCH;
4611da177e4SLinus Torvalds 
4621da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
4631da177e4SLinus Torvalds 	if (likely(child->sighand != NULL)) {
4641da177e4SLinus Torvalds 		error = -EINVAL;
4651da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
4661da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
467e16b2781SRoland McGrath 			*info = *child->last_siginfo;
4681da177e4SLinus Torvalds 			error = 0;
4691da177e4SLinus Torvalds 		}
4701da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
4711da177e4SLinus Torvalds 	}
4721da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
4731da177e4SLinus Torvalds 	return error;
4741da177e4SLinus Torvalds }
4751da177e4SLinus Torvalds 
476e16b2781SRoland McGrath static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info)
4771da177e4SLinus Torvalds {
4781da177e4SLinus Torvalds 	int error = -ESRCH;
4791da177e4SLinus Torvalds 
4801da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
4811da177e4SLinus Torvalds 	if (likely(child->sighand != NULL)) {
4821da177e4SLinus Torvalds 		error = -EINVAL;
4831da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
4841da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
485e16b2781SRoland McGrath 			*child->last_siginfo = *info;
4861da177e4SLinus Torvalds 			error = 0;
4871da177e4SLinus Torvalds 		}
4881da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
4891da177e4SLinus Torvalds 	}
4901da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
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 
51336df29d7SRoland McGrath static int ptrace_resume(struct task_struct *child, long request, long data)
51436df29d7SRoland McGrath {
51536df29d7SRoland McGrath 	if (!valid_signal(data))
51636df29d7SRoland McGrath 		return -EIO;
51736df29d7SRoland McGrath 
51836df29d7SRoland McGrath 	if (request == PTRACE_SYSCALL)
51936df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
52036df29d7SRoland McGrath 	else
52136df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
52236df29d7SRoland McGrath 
52336df29d7SRoland McGrath #ifdef TIF_SYSCALL_EMU
52436df29d7SRoland McGrath 	if (request == PTRACE_SYSEMU || request == PTRACE_SYSEMU_SINGLESTEP)
52536df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_EMU);
52636df29d7SRoland McGrath 	else
52736df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
52836df29d7SRoland McGrath #endif
52936df29d7SRoland McGrath 
5305b88abbfSRoland McGrath 	if (is_singleblock(request)) {
5315b88abbfSRoland McGrath 		if (unlikely(!arch_has_block_step()))
5325b88abbfSRoland McGrath 			return -EIO;
5335b88abbfSRoland McGrath 		user_enable_block_step(child);
5345b88abbfSRoland McGrath 	} else if (is_singlestep(request) || is_sysemu_singlestep(request)) {
53536df29d7SRoland McGrath 		if (unlikely(!arch_has_single_step()))
53636df29d7SRoland McGrath 			return -EIO;
53736df29d7SRoland McGrath 		user_enable_single_step(child);
5383a709703SRoland McGrath 	} else {
53936df29d7SRoland McGrath 		user_disable_single_step(child);
5403a709703SRoland McGrath 	}
54136df29d7SRoland McGrath 
54236df29d7SRoland McGrath 	child->exit_code = data;
54336df29d7SRoland McGrath 	wake_up_process(child);
54436df29d7SRoland McGrath 
54536df29d7SRoland McGrath 	return 0;
54636df29d7SRoland McGrath }
54736df29d7SRoland McGrath 
5481da177e4SLinus Torvalds int ptrace_request(struct task_struct *child, long request,
5491da177e4SLinus Torvalds 		   long addr, long data)
5501da177e4SLinus Torvalds {
5511da177e4SLinus Torvalds 	int ret = -EIO;
552e16b2781SRoland McGrath 	siginfo_t siginfo;
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds 	switch (request) {
55516c3e389SRoland McGrath 	case PTRACE_PEEKTEXT:
55616c3e389SRoland McGrath 	case PTRACE_PEEKDATA:
55716c3e389SRoland McGrath 		return generic_ptrace_peekdata(child, addr, data);
55816c3e389SRoland McGrath 	case PTRACE_POKETEXT:
55916c3e389SRoland McGrath 	case PTRACE_POKEDATA:
56016c3e389SRoland McGrath 		return generic_ptrace_pokedata(child, addr, data);
56116c3e389SRoland McGrath 
5621da177e4SLinus Torvalds #ifdef PTRACE_OLDSETOPTIONS
5631da177e4SLinus Torvalds 	case PTRACE_OLDSETOPTIONS:
5641da177e4SLinus Torvalds #endif
5651da177e4SLinus Torvalds 	case PTRACE_SETOPTIONS:
5661da177e4SLinus Torvalds 		ret = ptrace_setoptions(child, data);
5671da177e4SLinus Torvalds 		break;
5681da177e4SLinus Torvalds 	case PTRACE_GETEVENTMSG:
5691da177e4SLinus Torvalds 		ret = put_user(child->ptrace_message, (unsigned long __user *) data);
5701da177e4SLinus Torvalds 		break;
571e16b2781SRoland McGrath 
5721da177e4SLinus Torvalds 	case PTRACE_GETSIGINFO:
573e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
574e16b2781SRoland McGrath 		if (!ret)
575e16b2781SRoland McGrath 			ret = copy_siginfo_to_user((siginfo_t __user *) data,
576e16b2781SRoland McGrath 						   &siginfo);
5771da177e4SLinus Torvalds 		break;
578e16b2781SRoland McGrath 
5791da177e4SLinus Torvalds 	case PTRACE_SETSIGINFO:
580e16b2781SRoland McGrath 		if (copy_from_user(&siginfo, (siginfo_t __user *) data,
581e16b2781SRoland McGrath 				   sizeof siginfo))
582e16b2781SRoland McGrath 			ret = -EFAULT;
583e16b2781SRoland McGrath 		else
584e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
5851da177e4SLinus Torvalds 		break;
586e16b2781SRoland McGrath 
5871bcf5482SAlexey Dobriyan 	case PTRACE_DETACH:	 /* detach a process that was attached. */
5881bcf5482SAlexey Dobriyan 		ret = ptrace_detach(child, data);
5891bcf5482SAlexey Dobriyan 		break;
59036df29d7SRoland McGrath 
59136df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
59236df29d7SRoland McGrath 	case PTRACE_SINGLESTEP:
59336df29d7SRoland McGrath #endif
5945b88abbfSRoland McGrath #ifdef PTRACE_SINGLEBLOCK
5955b88abbfSRoland McGrath 	case PTRACE_SINGLEBLOCK:
5965b88abbfSRoland McGrath #endif
59736df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
59836df29d7SRoland McGrath 	case PTRACE_SYSEMU:
59936df29d7SRoland McGrath 	case PTRACE_SYSEMU_SINGLESTEP:
60036df29d7SRoland McGrath #endif
60136df29d7SRoland McGrath 	case PTRACE_SYSCALL:
60236df29d7SRoland McGrath 	case PTRACE_CONT:
60336df29d7SRoland McGrath 		return ptrace_resume(child, request, data);
60436df29d7SRoland McGrath 
60536df29d7SRoland McGrath 	case PTRACE_KILL:
60636df29d7SRoland McGrath 		if (child->exit_state)	/* already dead */
60736df29d7SRoland McGrath 			return 0;
60836df29d7SRoland McGrath 		return ptrace_resume(child, request, SIGKILL);
60936df29d7SRoland McGrath 
6101da177e4SLinus Torvalds 	default:
6111da177e4SLinus Torvalds 		break;
6121da177e4SLinus Torvalds 	}
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds 	return ret;
6151da177e4SLinus Torvalds }
616481bed45SChristoph Hellwig 
6176b9c7ed8SChristoph Hellwig /**
6186b9c7ed8SChristoph Hellwig  * ptrace_get_task_struct  --  grab a task struct reference for ptrace
6196b9c7ed8SChristoph Hellwig  * @pid:       process id to grab a task_struct reference of
6206b9c7ed8SChristoph Hellwig  *
6216b9c7ed8SChristoph Hellwig  * This function is a helper for ptrace implementations.  It checks
6226b9c7ed8SChristoph Hellwig  * permissions and then grabs a task struct for use of the actual
6236b9c7ed8SChristoph Hellwig  * ptrace implementation.
6246b9c7ed8SChristoph Hellwig  *
6256b9c7ed8SChristoph Hellwig  * Returns the task_struct for @pid or an ERR_PTR() on failure.
6266b9c7ed8SChristoph Hellwig  */
6276b9c7ed8SChristoph Hellwig struct task_struct *ptrace_get_task_struct(pid_t pid)
6286b9c7ed8SChristoph Hellwig {
6296b9c7ed8SChristoph Hellwig 	struct task_struct *child;
6306b9c7ed8SChristoph Hellwig 
631481bed45SChristoph Hellwig 	read_lock(&tasklist_lock);
632228ebcbeSPavel Emelyanov 	child = find_task_by_vpid(pid);
633481bed45SChristoph Hellwig 	if (child)
634481bed45SChristoph Hellwig 		get_task_struct(child);
635f400e198SSukadev Bhattiprolu 
636481bed45SChristoph Hellwig 	read_unlock(&tasklist_lock);
637481bed45SChristoph Hellwig 	if (!child)
6386b9c7ed8SChristoph Hellwig 		return ERR_PTR(-ESRCH);
6396b9c7ed8SChristoph Hellwig 	return child;
640481bed45SChristoph Hellwig }
641481bed45SChristoph Hellwig 
6420ac15559SChristoph Hellwig #ifndef arch_ptrace_attach
6430ac15559SChristoph Hellwig #define arch_ptrace_attach(child)	do { } while (0)
6440ac15559SChristoph Hellwig #endif
6450ac15559SChristoph Hellwig 
6461e7bfb21SHeiko Carstens SYSCALL_DEFINE4(ptrace, long, request, long, pid, long, addr, long, data)
647481bed45SChristoph Hellwig {
648481bed45SChristoph Hellwig 	struct task_struct *child;
649481bed45SChristoph Hellwig 	long ret;
650481bed45SChristoph Hellwig 
651481bed45SChristoph Hellwig 	/*
652481bed45SChristoph Hellwig 	 * This lock_kernel fixes a subtle race with suid exec
653481bed45SChristoph Hellwig 	 */
654481bed45SChristoph Hellwig 	lock_kernel();
6556b9c7ed8SChristoph Hellwig 	if (request == PTRACE_TRACEME) {
6566b9c7ed8SChristoph Hellwig 		ret = ptrace_traceme();
6576ea6dd93SHaavard Skinnemoen 		if (!ret)
6586ea6dd93SHaavard Skinnemoen 			arch_ptrace_attach(current);
659481bed45SChristoph Hellwig 		goto out;
6606b9c7ed8SChristoph Hellwig 	}
6616b9c7ed8SChristoph Hellwig 
6626b9c7ed8SChristoph Hellwig 	child = ptrace_get_task_struct(pid);
6636b9c7ed8SChristoph Hellwig 	if (IS_ERR(child)) {
6646b9c7ed8SChristoph Hellwig 		ret = PTR_ERR(child);
6656b9c7ed8SChristoph Hellwig 		goto out;
6666b9c7ed8SChristoph Hellwig 	}
667481bed45SChristoph Hellwig 
668481bed45SChristoph Hellwig 	if (request == PTRACE_ATTACH) {
669481bed45SChristoph Hellwig 		ret = ptrace_attach(child);
6700ac15559SChristoph Hellwig 		/*
6710ac15559SChristoph Hellwig 		 * Some architectures need to do book-keeping after
6720ac15559SChristoph Hellwig 		 * a ptrace attach.
6730ac15559SChristoph Hellwig 		 */
6740ac15559SChristoph Hellwig 		if (!ret)
6750ac15559SChristoph Hellwig 			arch_ptrace_attach(child);
676005f18dfSChristoph Hellwig 		goto out_put_task_struct;
677481bed45SChristoph Hellwig 	}
678481bed45SChristoph Hellwig 
679481bed45SChristoph Hellwig 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
680481bed45SChristoph Hellwig 	if (ret < 0)
681481bed45SChristoph Hellwig 		goto out_put_task_struct;
682481bed45SChristoph Hellwig 
683481bed45SChristoph Hellwig 	ret = arch_ptrace(child, request, addr, data);
684481bed45SChristoph Hellwig 
685481bed45SChristoph Hellwig  out_put_task_struct:
686481bed45SChristoph Hellwig 	put_task_struct(child);
687481bed45SChristoph Hellwig  out:
688481bed45SChristoph Hellwig 	unlock_kernel();
689481bed45SChristoph Hellwig 	return ret;
690481bed45SChristoph Hellwig }
69176647323SAlexey Dobriyan 
69276647323SAlexey Dobriyan int generic_ptrace_peekdata(struct task_struct *tsk, long addr, long data)
69376647323SAlexey Dobriyan {
69476647323SAlexey Dobriyan 	unsigned long tmp;
69576647323SAlexey Dobriyan 	int copied;
69676647323SAlexey Dobriyan 
69776647323SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
69876647323SAlexey Dobriyan 	if (copied != sizeof(tmp))
69976647323SAlexey Dobriyan 		return -EIO;
70076647323SAlexey Dobriyan 	return put_user(tmp, (unsigned long __user *)data);
70176647323SAlexey Dobriyan }
702f284ce72SAlexey Dobriyan 
703f284ce72SAlexey Dobriyan int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data)
704f284ce72SAlexey Dobriyan {
705f284ce72SAlexey Dobriyan 	int copied;
706f284ce72SAlexey Dobriyan 
707f284ce72SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
708f284ce72SAlexey Dobriyan 	return (copied == sizeof(data)) ? 0 : -EIO;
709f284ce72SAlexey Dobriyan }
710032d82d9SRoland McGrath 
71196b8936aSChristoph Hellwig #if defined CONFIG_COMPAT
712032d82d9SRoland McGrath #include <linux/compat.h>
713032d82d9SRoland McGrath 
714032d82d9SRoland McGrath int compat_ptrace_request(struct task_struct *child, compat_long_t request,
715032d82d9SRoland McGrath 			  compat_ulong_t addr, compat_ulong_t data)
716032d82d9SRoland McGrath {
717032d82d9SRoland McGrath 	compat_ulong_t __user *datap = compat_ptr(data);
718032d82d9SRoland McGrath 	compat_ulong_t word;
719e16b2781SRoland McGrath 	siginfo_t siginfo;
720032d82d9SRoland McGrath 	int ret;
721032d82d9SRoland McGrath 
722032d82d9SRoland McGrath 	switch (request) {
723032d82d9SRoland McGrath 	case PTRACE_PEEKTEXT:
724032d82d9SRoland McGrath 	case PTRACE_PEEKDATA:
725032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &word, sizeof(word), 0);
726032d82d9SRoland McGrath 		if (ret != sizeof(word))
727032d82d9SRoland McGrath 			ret = -EIO;
728032d82d9SRoland McGrath 		else
729032d82d9SRoland McGrath 			ret = put_user(word, datap);
730032d82d9SRoland McGrath 		break;
731032d82d9SRoland McGrath 
732032d82d9SRoland McGrath 	case PTRACE_POKETEXT:
733032d82d9SRoland McGrath 	case PTRACE_POKEDATA:
734032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &data, sizeof(data), 1);
735032d82d9SRoland McGrath 		ret = (ret != sizeof(data) ? -EIO : 0);
736032d82d9SRoland McGrath 		break;
737032d82d9SRoland McGrath 
738032d82d9SRoland McGrath 	case PTRACE_GETEVENTMSG:
739032d82d9SRoland McGrath 		ret = put_user((compat_ulong_t) child->ptrace_message, datap);
740032d82d9SRoland McGrath 		break;
741032d82d9SRoland McGrath 
742e16b2781SRoland McGrath 	case PTRACE_GETSIGINFO:
743e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
744e16b2781SRoland McGrath 		if (!ret)
745e16b2781SRoland McGrath 			ret = copy_siginfo_to_user32(
746e16b2781SRoland McGrath 				(struct compat_siginfo __user *) datap,
747e16b2781SRoland McGrath 				&siginfo);
748e16b2781SRoland McGrath 		break;
749e16b2781SRoland McGrath 
750e16b2781SRoland McGrath 	case PTRACE_SETSIGINFO:
751e16b2781SRoland McGrath 		memset(&siginfo, 0, sizeof siginfo);
752e16b2781SRoland McGrath 		if (copy_siginfo_from_user32(
753e16b2781SRoland McGrath 			    &siginfo, (struct compat_siginfo __user *) datap))
754e16b2781SRoland McGrath 			ret = -EFAULT;
755e16b2781SRoland McGrath 		else
756e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
757e16b2781SRoland McGrath 		break;
758e16b2781SRoland McGrath 
759032d82d9SRoland McGrath 	default:
760032d82d9SRoland McGrath 		ret = ptrace_request(child, request, addr, data);
761032d82d9SRoland McGrath 	}
762032d82d9SRoland McGrath 
763032d82d9SRoland McGrath 	return ret;
764032d82d9SRoland McGrath }
765c269f196SRoland McGrath 
766c269f196SRoland McGrath asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
767c269f196SRoland McGrath 				  compat_long_t addr, compat_long_t data)
768c269f196SRoland McGrath {
769c269f196SRoland McGrath 	struct task_struct *child;
770c269f196SRoland McGrath 	long ret;
771c269f196SRoland McGrath 
772c269f196SRoland McGrath 	/*
773c269f196SRoland McGrath 	 * This lock_kernel fixes a subtle race with suid exec
774c269f196SRoland McGrath 	 */
775c269f196SRoland McGrath 	lock_kernel();
776c269f196SRoland McGrath 	if (request == PTRACE_TRACEME) {
777c269f196SRoland McGrath 		ret = ptrace_traceme();
778c269f196SRoland McGrath 		goto out;
779c269f196SRoland McGrath 	}
780c269f196SRoland McGrath 
781c269f196SRoland McGrath 	child = ptrace_get_task_struct(pid);
782c269f196SRoland McGrath 	if (IS_ERR(child)) {
783c269f196SRoland McGrath 		ret = PTR_ERR(child);
784c269f196SRoland McGrath 		goto out;
785c269f196SRoland McGrath 	}
786c269f196SRoland McGrath 
787c269f196SRoland McGrath 	if (request == PTRACE_ATTACH) {
788c269f196SRoland McGrath 		ret = ptrace_attach(child);
789c269f196SRoland McGrath 		/*
790c269f196SRoland McGrath 		 * Some architectures need to do book-keeping after
791c269f196SRoland McGrath 		 * a ptrace attach.
792c269f196SRoland McGrath 		 */
793c269f196SRoland McGrath 		if (!ret)
794c269f196SRoland McGrath 			arch_ptrace_attach(child);
795c269f196SRoland McGrath 		goto out_put_task_struct;
796c269f196SRoland McGrath 	}
797c269f196SRoland McGrath 
798c269f196SRoland McGrath 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
799c269f196SRoland McGrath 	if (!ret)
800c269f196SRoland McGrath 		ret = compat_arch_ptrace(child, request, addr, data);
801c269f196SRoland McGrath 
802c269f196SRoland McGrath  out_put_task_struct:
803c269f196SRoland McGrath 	put_task_struct(child);
804c269f196SRoland McGrath  out:
805c269f196SRoland McGrath 	unlock_kernel();
806c269f196SRoland McGrath 	return ret;
807c269f196SRoland McGrath }
80896b8936aSChristoph Hellwig #endif	/* CONFIG_COMPAT */
809