xref: /openbmc/linux/kernel/ptrace.c (revision bf53de90)
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 {
130df26c40eSEric W. Biederman 	/* May we inspect the given task?
131df26c40eSEric W. Biederman 	 * This check is used both for attaching with ptrace
132df26c40eSEric W. Biederman 	 * and for allowing access to sensitive information in /proc.
133df26c40eSEric W. Biederman 	 *
134df26c40eSEric W. Biederman 	 * ptrace_attach denies several cases that /proc allows
135df26c40eSEric W. Biederman 	 * because setting up the necessary parent/child relationship
136df26c40eSEric W. Biederman 	 * or halting the specified task is impossible.
137df26c40eSEric W. Biederman 	 */
138df26c40eSEric W. Biederman 	int dumpable = 0;
139df26c40eSEric W. Biederman 	/* Don't let security modules deny introspection */
140df26c40eSEric W. Biederman 	if (task == current)
141df26c40eSEric W. Biederman 		return 0;
142ab8d11beSMiklos Szeredi 	if (((current->uid != task->euid) ||
143ab8d11beSMiklos Szeredi 	     (current->uid != task->suid) ||
144ab8d11beSMiklos Szeredi 	     (current->uid != task->uid) ||
145ab8d11beSMiklos Szeredi 	     (current->gid != task->egid) ||
146ab8d11beSMiklos Szeredi 	     (current->gid != task->sgid) ||
147ab8d11beSMiklos Szeredi 	     (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
148ab8d11beSMiklos Szeredi 		return -EPERM;
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);
164006ebb40SStephen Smalley 	return (!err ? true : false);
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;
175bac0abd6SPavel Emelyanov 	if (same_thread_group(task, current))
176f5b40e36SLinus Torvalds 		goto out;
177f5b40e36SLinus Torvalds 
178f358166aSLinus Torvalds repeat:
179f358166aSLinus Torvalds 	/*
180f358166aSLinus Torvalds 	 * Nasty, nasty.
181f358166aSLinus Torvalds 	 *
182f358166aSLinus Torvalds 	 * We want to hold both the task-lock and the
183f358166aSLinus Torvalds 	 * tasklist_lock for writing at the same time.
184f358166aSLinus Torvalds 	 * But that's against the rules (tasklist_lock
185f358166aSLinus Torvalds 	 * is taken for reading by interrupts on other
186f358166aSLinus Torvalds 	 * cpu's that may have task_lock).
187f358166aSLinus Torvalds 	 */
188f5b40e36SLinus Torvalds 	task_lock(task);
1896175ecfeSSripathi Kodi 	if (!write_trylock_irqsave(&tasklist_lock, flags)) {
190f358166aSLinus Torvalds 		task_unlock(task);
191f358166aSLinus Torvalds 		do {
192f358166aSLinus Torvalds 			cpu_relax();
193f358166aSLinus Torvalds 		} while (!write_can_lock(&tasklist_lock));
194f358166aSLinus Torvalds 		goto repeat;
195f358166aSLinus Torvalds 	}
196f5b40e36SLinus Torvalds 
197df26c40eSEric W. Biederman 	if (!task->mm)
198df26c40eSEric W. Biederman 		goto bad;
1991da177e4SLinus Torvalds 	/* the same process cannot be attached many times */
2001da177e4SLinus Torvalds 	if (task->ptrace & PT_PTRACED)
2011da177e4SLinus Torvalds 		goto bad;
202006ebb40SStephen Smalley 	retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
2031da177e4SLinus Torvalds 	if (retval)
2041da177e4SLinus Torvalds 		goto bad;
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds 	/* Go */
2076b39c7bfSOleg Nesterov 	task->ptrace |= PT_PTRACED;
2081da177e4SLinus Torvalds 	if (capable(CAP_SYS_PTRACE))
2091da177e4SLinus Torvalds 		task->ptrace |= PT_PTRACE_CAP;
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds 	__ptrace_link(task, current);
2121da177e4SLinus Torvalds 
21333e9fc7dSOleg Nesterov 	send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
2141da177e4SLinus Torvalds bad:
2156175ecfeSSripathi Kodi 	write_unlock_irqrestore(&tasklist_lock, flags);
2161da177e4SLinus Torvalds 	task_unlock(task);
217f5b40e36SLinus Torvalds out:
2181da177e4SLinus Torvalds 	return retval;
2191da177e4SLinus Torvalds }
2201da177e4SLinus Torvalds 
221d5f70c00SOleg Nesterov static inline void __ptrace_detach(struct task_struct *child, unsigned int data)
2225ecfbae0SOleg Nesterov {
2235ecfbae0SOleg Nesterov 	child->exit_code = data;
2245ecfbae0SOleg Nesterov 	/* .. re-parent .. */
2255ecfbae0SOleg Nesterov 	__ptrace_unlink(child);
2265ecfbae0SOleg Nesterov 	/* .. and wake it up. */
2275ecfbae0SOleg Nesterov 	if (child->exit_state != EXIT_ZOMBIE)
2285ecfbae0SOleg Nesterov 		wake_up_process(child);
2295ecfbae0SOleg Nesterov }
2305ecfbae0SOleg Nesterov 
2311da177e4SLinus Torvalds int ptrace_detach(struct task_struct *child, unsigned int data)
2321da177e4SLinus Torvalds {
2337ed20e1aSJesper Juhl 	if (!valid_signal(data))
2341da177e4SLinus Torvalds 		return -EIO;
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds 	/* Architecture-specific hardware disable .. */
2371da177e4SLinus Torvalds 	ptrace_disable(child);
2387d941432SRoland McGrath 	clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds 	write_lock_irq(&tasklist_lock);
241d5f70c00SOleg Nesterov 	/* protect against de_thread()->release_task() */
2425ecfbae0SOleg Nesterov 	if (child->ptrace)
2435ecfbae0SOleg Nesterov 		__ptrace_detach(child, data);
2441da177e4SLinus Torvalds 	write_unlock_irq(&tasklist_lock);
2451da177e4SLinus Torvalds 
2461da177e4SLinus Torvalds 	return 0;
2471da177e4SLinus Torvalds }
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
2501da177e4SLinus Torvalds {
2511da177e4SLinus Torvalds 	int copied = 0;
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds 	while (len > 0) {
2541da177e4SLinus Torvalds 		char buf[128];
2551da177e4SLinus Torvalds 		int this_len, retval;
2561da177e4SLinus Torvalds 
2571da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
2581da177e4SLinus Torvalds 		retval = access_process_vm(tsk, src, buf, this_len, 0);
2591da177e4SLinus Torvalds 		if (!retval) {
2601da177e4SLinus Torvalds 			if (copied)
2611da177e4SLinus Torvalds 				break;
2621da177e4SLinus Torvalds 			return -EIO;
2631da177e4SLinus Torvalds 		}
2641da177e4SLinus Torvalds 		if (copy_to_user(dst, buf, retval))
2651da177e4SLinus Torvalds 			return -EFAULT;
2661da177e4SLinus Torvalds 		copied += retval;
2671da177e4SLinus Torvalds 		src += retval;
2681da177e4SLinus Torvalds 		dst += retval;
2691da177e4SLinus Torvalds 		len -= retval;
2701da177e4SLinus Torvalds 	}
2711da177e4SLinus Torvalds 	return copied;
2721da177e4SLinus Torvalds }
2731da177e4SLinus Torvalds 
2741da177e4SLinus Torvalds int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
2751da177e4SLinus Torvalds {
2761da177e4SLinus Torvalds 	int copied = 0;
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 	while (len > 0) {
2791da177e4SLinus Torvalds 		char buf[128];
2801da177e4SLinus Torvalds 		int this_len, retval;
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
2831da177e4SLinus Torvalds 		if (copy_from_user(buf, src, this_len))
2841da177e4SLinus Torvalds 			return -EFAULT;
2851da177e4SLinus Torvalds 		retval = access_process_vm(tsk, dst, buf, this_len, 1);
2861da177e4SLinus Torvalds 		if (!retval) {
2871da177e4SLinus Torvalds 			if (copied)
2881da177e4SLinus Torvalds 				break;
2891da177e4SLinus Torvalds 			return -EIO;
2901da177e4SLinus Torvalds 		}
2911da177e4SLinus Torvalds 		copied += retval;
2921da177e4SLinus Torvalds 		src += retval;
2931da177e4SLinus Torvalds 		dst += retval;
2941da177e4SLinus Torvalds 		len -= retval;
2951da177e4SLinus Torvalds 	}
2961da177e4SLinus Torvalds 	return copied;
2971da177e4SLinus Torvalds }
2981da177e4SLinus Torvalds 
2991da177e4SLinus Torvalds static int ptrace_setoptions(struct task_struct *child, long data)
3001da177e4SLinus Torvalds {
3011da177e4SLinus Torvalds 	child->ptrace &= ~PT_TRACE_MASK;
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACESYSGOOD)
3041da177e4SLinus Torvalds 		child->ptrace |= PT_TRACESYSGOOD;
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEFORK)
3071da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_FORK;
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORK)
3101da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK;
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACECLONE)
3131da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_CLONE;
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXEC)
3161da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXEC;
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORKDONE)
3191da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK_DONE;
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXIT)
3221da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXIT;
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds 	return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
3251da177e4SLinus Torvalds }
3261da177e4SLinus Torvalds 
327e16b2781SRoland McGrath static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info)
3281da177e4SLinus Torvalds {
3291da177e4SLinus Torvalds 	int error = -ESRCH;
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
3321da177e4SLinus Torvalds 	if (likely(child->sighand != NULL)) {
3331da177e4SLinus Torvalds 		error = -EINVAL;
3341da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
3351da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
336e16b2781SRoland McGrath 			*info = *child->last_siginfo;
3371da177e4SLinus Torvalds 			error = 0;
3381da177e4SLinus Torvalds 		}
3391da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
3401da177e4SLinus Torvalds 	}
3411da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
3421da177e4SLinus Torvalds 	return error;
3431da177e4SLinus Torvalds }
3441da177e4SLinus Torvalds 
345e16b2781SRoland McGrath static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info)
3461da177e4SLinus Torvalds {
3471da177e4SLinus Torvalds 	int error = -ESRCH;
3481da177e4SLinus Torvalds 
3491da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
3501da177e4SLinus Torvalds 	if (likely(child->sighand != NULL)) {
3511da177e4SLinus Torvalds 		error = -EINVAL;
3521da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
3531da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
354e16b2781SRoland McGrath 			*child->last_siginfo = *info;
3551da177e4SLinus Torvalds 			error = 0;
3561da177e4SLinus Torvalds 		}
3571da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
3581da177e4SLinus Torvalds 	}
3591da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
3601da177e4SLinus Torvalds 	return error;
3611da177e4SLinus Torvalds }
3621da177e4SLinus Torvalds 
36336df29d7SRoland McGrath 
36436df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
36536df29d7SRoland McGrath #define is_singlestep(request)		((request) == PTRACE_SINGLESTEP)
36636df29d7SRoland McGrath #else
36736df29d7SRoland McGrath #define is_singlestep(request)		0
36836df29d7SRoland McGrath #endif
36936df29d7SRoland McGrath 
3705b88abbfSRoland McGrath #ifdef PTRACE_SINGLEBLOCK
3715b88abbfSRoland McGrath #define is_singleblock(request)		((request) == PTRACE_SINGLEBLOCK)
3725b88abbfSRoland McGrath #else
3735b88abbfSRoland McGrath #define is_singleblock(request)		0
3745b88abbfSRoland McGrath #endif
3755b88abbfSRoland McGrath 
37636df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
37736df29d7SRoland McGrath #define is_sysemu_singlestep(request)	((request) == PTRACE_SYSEMU_SINGLESTEP)
37836df29d7SRoland McGrath #else
37936df29d7SRoland McGrath #define is_sysemu_singlestep(request)	0
38036df29d7SRoland McGrath #endif
38136df29d7SRoland McGrath 
38236df29d7SRoland McGrath static int ptrace_resume(struct task_struct *child, long request, long data)
38336df29d7SRoland McGrath {
38436df29d7SRoland McGrath 	if (!valid_signal(data))
38536df29d7SRoland McGrath 		return -EIO;
38636df29d7SRoland McGrath 
38736df29d7SRoland McGrath 	if (request == PTRACE_SYSCALL)
38836df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
38936df29d7SRoland McGrath 	else
39036df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
39136df29d7SRoland McGrath 
39236df29d7SRoland McGrath #ifdef TIF_SYSCALL_EMU
39336df29d7SRoland McGrath 	if (request == PTRACE_SYSEMU || request == PTRACE_SYSEMU_SINGLESTEP)
39436df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_EMU);
39536df29d7SRoland McGrath 	else
39636df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
39736df29d7SRoland McGrath #endif
39836df29d7SRoland McGrath 
3995b88abbfSRoland McGrath 	if (is_singleblock(request)) {
4005b88abbfSRoland McGrath 		if (unlikely(!arch_has_block_step()))
4015b88abbfSRoland McGrath 			return -EIO;
4025b88abbfSRoland McGrath 		user_enable_block_step(child);
4035b88abbfSRoland McGrath 	} else if (is_singlestep(request) || is_sysemu_singlestep(request)) {
40436df29d7SRoland McGrath 		if (unlikely(!arch_has_single_step()))
40536df29d7SRoland McGrath 			return -EIO;
40636df29d7SRoland McGrath 		user_enable_single_step(child);
40736df29d7SRoland McGrath 	}
40836df29d7SRoland McGrath 	else
40936df29d7SRoland McGrath 		user_disable_single_step(child);
41036df29d7SRoland McGrath 
41136df29d7SRoland McGrath 	child->exit_code = data;
41236df29d7SRoland McGrath 	wake_up_process(child);
41336df29d7SRoland McGrath 
41436df29d7SRoland McGrath 	return 0;
41536df29d7SRoland McGrath }
41636df29d7SRoland McGrath 
4171da177e4SLinus Torvalds int ptrace_request(struct task_struct *child, long request,
4181da177e4SLinus Torvalds 		   long addr, long data)
4191da177e4SLinus Torvalds {
4201da177e4SLinus Torvalds 	int ret = -EIO;
421e16b2781SRoland McGrath 	siginfo_t siginfo;
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds 	switch (request) {
42416c3e389SRoland McGrath 	case PTRACE_PEEKTEXT:
42516c3e389SRoland McGrath 	case PTRACE_PEEKDATA:
42616c3e389SRoland McGrath 		return generic_ptrace_peekdata(child, addr, data);
42716c3e389SRoland McGrath 	case PTRACE_POKETEXT:
42816c3e389SRoland McGrath 	case PTRACE_POKEDATA:
42916c3e389SRoland McGrath 		return generic_ptrace_pokedata(child, addr, data);
43016c3e389SRoland McGrath 
4311da177e4SLinus Torvalds #ifdef PTRACE_OLDSETOPTIONS
4321da177e4SLinus Torvalds 	case PTRACE_OLDSETOPTIONS:
4331da177e4SLinus Torvalds #endif
4341da177e4SLinus Torvalds 	case PTRACE_SETOPTIONS:
4351da177e4SLinus Torvalds 		ret = ptrace_setoptions(child, data);
4361da177e4SLinus Torvalds 		break;
4371da177e4SLinus Torvalds 	case PTRACE_GETEVENTMSG:
4381da177e4SLinus Torvalds 		ret = put_user(child->ptrace_message, (unsigned long __user *) data);
4391da177e4SLinus Torvalds 		break;
440e16b2781SRoland McGrath 
4411da177e4SLinus Torvalds 	case PTRACE_GETSIGINFO:
442e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
443e16b2781SRoland McGrath 		if (!ret)
444e16b2781SRoland McGrath 			ret = copy_siginfo_to_user((siginfo_t __user *) data,
445e16b2781SRoland McGrath 						   &siginfo);
4461da177e4SLinus Torvalds 		break;
447e16b2781SRoland McGrath 
4481da177e4SLinus Torvalds 	case PTRACE_SETSIGINFO:
449e16b2781SRoland McGrath 		if (copy_from_user(&siginfo, (siginfo_t __user *) data,
450e16b2781SRoland McGrath 				   sizeof siginfo))
451e16b2781SRoland McGrath 			ret = -EFAULT;
452e16b2781SRoland McGrath 		else
453e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
4541da177e4SLinus Torvalds 		break;
455e16b2781SRoland McGrath 
4561bcf5482SAlexey Dobriyan 	case PTRACE_DETACH:	 /* detach a process that was attached. */
4571bcf5482SAlexey Dobriyan 		ret = ptrace_detach(child, data);
4581bcf5482SAlexey Dobriyan 		break;
45936df29d7SRoland McGrath 
46036df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
46136df29d7SRoland McGrath 	case PTRACE_SINGLESTEP:
46236df29d7SRoland McGrath #endif
4635b88abbfSRoland McGrath #ifdef PTRACE_SINGLEBLOCK
4645b88abbfSRoland McGrath 	case PTRACE_SINGLEBLOCK:
4655b88abbfSRoland McGrath #endif
46636df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
46736df29d7SRoland McGrath 	case PTRACE_SYSEMU:
46836df29d7SRoland McGrath 	case PTRACE_SYSEMU_SINGLESTEP:
46936df29d7SRoland McGrath #endif
47036df29d7SRoland McGrath 	case PTRACE_SYSCALL:
47136df29d7SRoland McGrath 	case PTRACE_CONT:
47236df29d7SRoland McGrath 		return ptrace_resume(child, request, data);
47336df29d7SRoland McGrath 
47436df29d7SRoland McGrath 	case PTRACE_KILL:
47536df29d7SRoland McGrath 		if (child->exit_state)	/* already dead */
47636df29d7SRoland McGrath 			return 0;
47736df29d7SRoland McGrath 		return ptrace_resume(child, request, SIGKILL);
47836df29d7SRoland McGrath 
4791da177e4SLinus Torvalds 	default:
4801da177e4SLinus Torvalds 		break;
4811da177e4SLinus Torvalds 	}
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds 	return ret;
4841da177e4SLinus Torvalds }
485481bed45SChristoph Hellwig 
4866b9c7ed8SChristoph Hellwig /**
4876b9c7ed8SChristoph Hellwig  * ptrace_traceme  --  helper for PTRACE_TRACEME
4886b9c7ed8SChristoph Hellwig  *
4896b9c7ed8SChristoph Hellwig  * Performs checks and sets PT_PTRACED.
4906b9c7ed8SChristoph Hellwig  * Should be used by all ptrace implementations for PTRACE_TRACEME.
4916b9c7ed8SChristoph Hellwig  */
4926b9c7ed8SChristoph Hellwig int ptrace_traceme(void)
493481bed45SChristoph Hellwig {
494f5b40e36SLinus Torvalds 	int ret = -EPERM;
495481bed45SChristoph Hellwig 
496481bed45SChristoph Hellwig 	/*
497481bed45SChristoph Hellwig 	 * Are we already being traced?
498481bed45SChristoph Hellwig 	 */
499f470021aSRoland McGrath repeat:
500f5b40e36SLinus Torvalds 	task_lock(current);
501f5b40e36SLinus Torvalds 	if (!(current->ptrace & PT_PTRACED)) {
502f470021aSRoland McGrath 		/*
503f470021aSRoland McGrath 		 * See ptrace_attach() comments about the locking here.
504f470021aSRoland McGrath 		 */
505f470021aSRoland McGrath 		unsigned long flags;
506f470021aSRoland McGrath 		if (!write_trylock_irqsave(&tasklist_lock, flags)) {
507f470021aSRoland McGrath 			task_unlock(current);
508f470021aSRoland McGrath 			do {
509f470021aSRoland McGrath 				cpu_relax();
510f470021aSRoland McGrath 			} while (!write_can_lock(&tasklist_lock));
511f470021aSRoland McGrath 			goto repeat;
512f470021aSRoland McGrath 		}
513f470021aSRoland McGrath 
5145cd9c58fSDavid Howells 		ret = security_ptrace_traceme(current->parent);
515f470021aSRoland McGrath 
516481bed45SChristoph Hellwig 		/*
517481bed45SChristoph Hellwig 		 * Set the ptrace bit in the process ptrace flags.
518f470021aSRoland McGrath 		 * Then link us on our parent's ptraced list.
519481bed45SChristoph Hellwig 		 */
520f470021aSRoland McGrath 		if (!ret) {
521481bed45SChristoph Hellwig 			current->ptrace |= PT_PTRACED;
522f470021aSRoland McGrath 			__ptrace_link(current, current->real_parent);
523f470021aSRoland McGrath 		}
524f470021aSRoland McGrath 
525f470021aSRoland McGrath 		write_unlock_irqrestore(&tasklist_lock, flags);
526f5b40e36SLinus Torvalds 	}
527f5b40e36SLinus Torvalds 	task_unlock(current);
528f5b40e36SLinus Torvalds 	return ret;
529481bed45SChristoph Hellwig }
530481bed45SChristoph Hellwig 
5316b9c7ed8SChristoph Hellwig /**
5326b9c7ed8SChristoph Hellwig  * ptrace_get_task_struct  --  grab a task struct reference for ptrace
5336b9c7ed8SChristoph Hellwig  * @pid:       process id to grab a task_struct reference of
5346b9c7ed8SChristoph Hellwig  *
5356b9c7ed8SChristoph Hellwig  * This function is a helper for ptrace implementations.  It checks
5366b9c7ed8SChristoph Hellwig  * permissions and then grabs a task struct for use of the actual
5376b9c7ed8SChristoph Hellwig  * ptrace implementation.
5386b9c7ed8SChristoph Hellwig  *
5396b9c7ed8SChristoph Hellwig  * Returns the task_struct for @pid or an ERR_PTR() on failure.
5406b9c7ed8SChristoph Hellwig  */
5416b9c7ed8SChristoph Hellwig struct task_struct *ptrace_get_task_struct(pid_t pid)
5426b9c7ed8SChristoph Hellwig {
5436b9c7ed8SChristoph Hellwig 	struct task_struct *child;
5446b9c7ed8SChristoph Hellwig 
545481bed45SChristoph Hellwig 	read_lock(&tasklist_lock);
546228ebcbeSPavel Emelyanov 	child = find_task_by_vpid(pid);
547481bed45SChristoph Hellwig 	if (child)
548481bed45SChristoph Hellwig 		get_task_struct(child);
549f400e198SSukadev Bhattiprolu 
550481bed45SChristoph Hellwig 	read_unlock(&tasklist_lock);
551481bed45SChristoph Hellwig 	if (!child)
5526b9c7ed8SChristoph Hellwig 		return ERR_PTR(-ESRCH);
5536b9c7ed8SChristoph Hellwig 	return child;
554481bed45SChristoph Hellwig }
555481bed45SChristoph Hellwig 
5560ac15559SChristoph Hellwig #ifndef arch_ptrace_attach
5570ac15559SChristoph Hellwig #define arch_ptrace_attach(child)	do { } while (0)
5580ac15559SChristoph Hellwig #endif
5590ac15559SChristoph Hellwig 
560481bed45SChristoph Hellwig asmlinkage long sys_ptrace(long request, long pid, long addr, long data)
561481bed45SChristoph Hellwig {
562481bed45SChristoph Hellwig 	struct task_struct *child;
563481bed45SChristoph Hellwig 	long ret;
564481bed45SChristoph Hellwig 
565481bed45SChristoph Hellwig 	/*
566481bed45SChristoph Hellwig 	 * This lock_kernel fixes a subtle race with suid exec
567481bed45SChristoph Hellwig 	 */
568481bed45SChristoph Hellwig 	lock_kernel();
5696b9c7ed8SChristoph Hellwig 	if (request == PTRACE_TRACEME) {
5706b9c7ed8SChristoph Hellwig 		ret = ptrace_traceme();
5716ea6dd93SHaavard Skinnemoen 		if (!ret)
5726ea6dd93SHaavard Skinnemoen 			arch_ptrace_attach(current);
573481bed45SChristoph Hellwig 		goto out;
5746b9c7ed8SChristoph Hellwig 	}
5756b9c7ed8SChristoph Hellwig 
5766b9c7ed8SChristoph Hellwig 	child = ptrace_get_task_struct(pid);
5776b9c7ed8SChristoph Hellwig 	if (IS_ERR(child)) {
5786b9c7ed8SChristoph Hellwig 		ret = PTR_ERR(child);
5796b9c7ed8SChristoph Hellwig 		goto out;
5806b9c7ed8SChristoph Hellwig 	}
581481bed45SChristoph Hellwig 
582481bed45SChristoph Hellwig 	if (request == PTRACE_ATTACH) {
583481bed45SChristoph Hellwig 		ret = ptrace_attach(child);
5840ac15559SChristoph Hellwig 		/*
5850ac15559SChristoph Hellwig 		 * Some architectures need to do book-keeping after
5860ac15559SChristoph Hellwig 		 * a ptrace attach.
5870ac15559SChristoph Hellwig 		 */
5880ac15559SChristoph Hellwig 		if (!ret)
5890ac15559SChristoph Hellwig 			arch_ptrace_attach(child);
590005f18dfSChristoph Hellwig 		goto out_put_task_struct;
591481bed45SChristoph Hellwig 	}
592481bed45SChristoph Hellwig 
593481bed45SChristoph Hellwig 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
594481bed45SChristoph Hellwig 	if (ret < 0)
595481bed45SChristoph Hellwig 		goto out_put_task_struct;
596481bed45SChristoph Hellwig 
597481bed45SChristoph Hellwig 	ret = arch_ptrace(child, request, addr, data);
598481bed45SChristoph Hellwig 	if (ret < 0)
599481bed45SChristoph Hellwig 		goto out_put_task_struct;
600481bed45SChristoph Hellwig 
601481bed45SChristoph Hellwig  out_put_task_struct:
602481bed45SChristoph Hellwig 	put_task_struct(child);
603481bed45SChristoph Hellwig  out:
604481bed45SChristoph Hellwig 	unlock_kernel();
605481bed45SChristoph Hellwig 	return ret;
606481bed45SChristoph Hellwig }
60776647323SAlexey Dobriyan 
60876647323SAlexey Dobriyan int generic_ptrace_peekdata(struct task_struct *tsk, long addr, long data)
60976647323SAlexey Dobriyan {
61076647323SAlexey Dobriyan 	unsigned long tmp;
61176647323SAlexey Dobriyan 	int copied;
61276647323SAlexey Dobriyan 
61376647323SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
61476647323SAlexey Dobriyan 	if (copied != sizeof(tmp))
61576647323SAlexey Dobriyan 		return -EIO;
61676647323SAlexey Dobriyan 	return put_user(tmp, (unsigned long __user *)data);
61776647323SAlexey Dobriyan }
618f284ce72SAlexey Dobriyan 
619f284ce72SAlexey Dobriyan int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data)
620f284ce72SAlexey Dobriyan {
621f284ce72SAlexey Dobriyan 	int copied;
622f284ce72SAlexey Dobriyan 
623f284ce72SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
624f284ce72SAlexey Dobriyan 	return (copied == sizeof(data)) ? 0 : -EIO;
625f284ce72SAlexey Dobriyan }
626032d82d9SRoland McGrath 
62796b8936aSChristoph Hellwig #if defined CONFIG_COMPAT
628032d82d9SRoland McGrath #include <linux/compat.h>
629032d82d9SRoland McGrath 
630032d82d9SRoland McGrath int compat_ptrace_request(struct task_struct *child, compat_long_t request,
631032d82d9SRoland McGrath 			  compat_ulong_t addr, compat_ulong_t data)
632032d82d9SRoland McGrath {
633032d82d9SRoland McGrath 	compat_ulong_t __user *datap = compat_ptr(data);
634032d82d9SRoland McGrath 	compat_ulong_t word;
635e16b2781SRoland McGrath 	siginfo_t siginfo;
636032d82d9SRoland McGrath 	int ret;
637032d82d9SRoland McGrath 
638032d82d9SRoland McGrath 	switch (request) {
639032d82d9SRoland McGrath 	case PTRACE_PEEKTEXT:
640032d82d9SRoland McGrath 	case PTRACE_PEEKDATA:
641032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &word, sizeof(word), 0);
642032d82d9SRoland McGrath 		if (ret != sizeof(word))
643032d82d9SRoland McGrath 			ret = -EIO;
644032d82d9SRoland McGrath 		else
645032d82d9SRoland McGrath 			ret = put_user(word, datap);
646032d82d9SRoland McGrath 		break;
647032d82d9SRoland McGrath 
648032d82d9SRoland McGrath 	case PTRACE_POKETEXT:
649032d82d9SRoland McGrath 	case PTRACE_POKEDATA:
650032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &data, sizeof(data), 1);
651032d82d9SRoland McGrath 		ret = (ret != sizeof(data) ? -EIO : 0);
652032d82d9SRoland McGrath 		break;
653032d82d9SRoland McGrath 
654032d82d9SRoland McGrath 	case PTRACE_GETEVENTMSG:
655032d82d9SRoland McGrath 		ret = put_user((compat_ulong_t) child->ptrace_message, datap);
656032d82d9SRoland McGrath 		break;
657032d82d9SRoland McGrath 
658e16b2781SRoland McGrath 	case PTRACE_GETSIGINFO:
659e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
660e16b2781SRoland McGrath 		if (!ret)
661e16b2781SRoland McGrath 			ret = copy_siginfo_to_user32(
662e16b2781SRoland McGrath 				(struct compat_siginfo __user *) datap,
663e16b2781SRoland McGrath 				&siginfo);
664e16b2781SRoland McGrath 		break;
665e16b2781SRoland McGrath 
666e16b2781SRoland McGrath 	case PTRACE_SETSIGINFO:
667e16b2781SRoland McGrath 		memset(&siginfo, 0, sizeof siginfo);
668e16b2781SRoland McGrath 		if (copy_siginfo_from_user32(
669e16b2781SRoland McGrath 			    &siginfo, (struct compat_siginfo __user *) datap))
670e16b2781SRoland McGrath 			ret = -EFAULT;
671e16b2781SRoland McGrath 		else
672e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
673e16b2781SRoland McGrath 		break;
674e16b2781SRoland McGrath 
675032d82d9SRoland McGrath 	default:
676032d82d9SRoland McGrath 		ret = ptrace_request(child, request, addr, data);
677032d82d9SRoland McGrath 	}
678032d82d9SRoland McGrath 
679032d82d9SRoland McGrath 	return ret;
680032d82d9SRoland McGrath }
681c269f196SRoland McGrath 
682c269f196SRoland McGrath asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
683c269f196SRoland McGrath 				  compat_long_t addr, compat_long_t data)
684c269f196SRoland McGrath {
685c269f196SRoland McGrath 	struct task_struct *child;
686c269f196SRoland McGrath 	long ret;
687c269f196SRoland McGrath 
688c269f196SRoland McGrath 	/*
689c269f196SRoland McGrath 	 * This lock_kernel fixes a subtle race with suid exec
690c269f196SRoland McGrath 	 */
691c269f196SRoland McGrath 	lock_kernel();
692c269f196SRoland McGrath 	if (request == PTRACE_TRACEME) {
693c269f196SRoland McGrath 		ret = ptrace_traceme();
694c269f196SRoland McGrath 		goto out;
695c269f196SRoland McGrath 	}
696c269f196SRoland McGrath 
697c269f196SRoland McGrath 	child = ptrace_get_task_struct(pid);
698c269f196SRoland McGrath 	if (IS_ERR(child)) {
699c269f196SRoland McGrath 		ret = PTR_ERR(child);
700c269f196SRoland McGrath 		goto out;
701c269f196SRoland McGrath 	}
702c269f196SRoland McGrath 
703c269f196SRoland McGrath 	if (request == PTRACE_ATTACH) {
704c269f196SRoland McGrath 		ret = ptrace_attach(child);
705c269f196SRoland McGrath 		/*
706c269f196SRoland McGrath 		 * Some architectures need to do book-keeping after
707c269f196SRoland McGrath 		 * a ptrace attach.
708c269f196SRoland McGrath 		 */
709c269f196SRoland McGrath 		if (!ret)
710c269f196SRoland McGrath 			arch_ptrace_attach(child);
711c269f196SRoland McGrath 		goto out_put_task_struct;
712c269f196SRoland McGrath 	}
713c269f196SRoland McGrath 
714c269f196SRoland McGrath 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
715c269f196SRoland McGrath 	if (!ret)
716c269f196SRoland McGrath 		ret = compat_arch_ptrace(child, request, addr, data);
717c269f196SRoland McGrath 
718c269f196SRoland McGrath  out_put_task_struct:
719c269f196SRoland McGrath 	put_task_struct(child);
720c269f196SRoland McGrath  out:
721c269f196SRoland McGrath 	unlock_kernel();
722c269f196SRoland McGrath 	return ret;
723c269f196SRoland McGrath }
72496b8936aSChristoph Hellwig #endif	/* CONFIG_COMPAT */
725