xref: /openbmc/linux/kernel/ptrace.c (revision 95c3eb76)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/kernel/ptrace.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * (C) Copyright 1999 Linus Torvalds
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Common interfaces for "ptrace()" which we do not want
71da177e4SLinus Torvalds  * to continually duplicate across every architecture.
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
10c59ede7bSRandy.Dunlap #include <linux/capability.h>
111da177e4SLinus Torvalds #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/sched.h>
131da177e4SLinus Torvalds #include <linux/errno.h>
141da177e4SLinus Torvalds #include <linux/mm.h>
151da177e4SLinus Torvalds #include <linux/highmem.h>
161da177e4SLinus Torvalds #include <linux/pagemap.h>
171da177e4SLinus Torvalds #include <linux/smp_lock.h>
181da177e4SLinus Torvalds #include <linux/ptrace.h>
191da177e4SLinus Torvalds #include <linux/security.h>
207ed20e1aSJesper Juhl #include <linux/signal.h>
21a5cb013dSAl Viro #include <linux/audit.h>
22b488893aSPavel Emelyanov #include <linux/pid_namespace.h>
23f17d30a8SAdrian Bunk #include <linux/syscalls.h>
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds #include <asm/pgtable.h>
261da177e4SLinus Torvalds #include <asm/uaccess.h>
271da177e4SLinus Torvalds 
28bf53de90SMarkus Metzger 
29bf53de90SMarkus Metzger /*
30bf53de90SMarkus Metzger  * Initialize a new task whose father had been ptraced.
31bf53de90SMarkus Metzger  *
32bf53de90SMarkus Metzger  * Called from copy_process().
33bf53de90SMarkus Metzger  */
34bf53de90SMarkus Metzger void ptrace_fork(struct task_struct *child, unsigned long clone_flags)
35bf53de90SMarkus Metzger {
36bf53de90SMarkus Metzger 	arch_ptrace_fork(child, clone_flags);
37bf53de90SMarkus Metzger }
38bf53de90SMarkus Metzger 
391da177e4SLinus Torvalds /*
401da177e4SLinus Torvalds  * ptrace a task: make the debugger its new parent and
411da177e4SLinus Torvalds  * move it to the ptrace list.
421da177e4SLinus Torvalds  *
431da177e4SLinus Torvalds  * Must be called with the tasklist lock write-held.
441da177e4SLinus Torvalds  */
4536c8b586SIngo Molnar void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
461da177e4SLinus Torvalds {
47f470021aSRoland McGrath 	BUG_ON(!list_empty(&child->ptrace_entry));
48f470021aSRoland McGrath 	list_add(&child->ptrace_entry, &new_parent->ptraced);
491da177e4SLinus Torvalds 	child->parent = new_parent;
501da177e4SLinus Torvalds }
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds /*
531da177e4SLinus Torvalds  * Turn a tracing stop into a normal stop now, since with no tracer there
541da177e4SLinus Torvalds  * would be no way to wake it up with SIGCONT or SIGKILL.  If there was a
551da177e4SLinus Torvalds  * signal sent that would resume the child, but didn't because it was in
561da177e4SLinus Torvalds  * TASK_TRACED, resume it now.
571da177e4SLinus Torvalds  * Requires that irqs be disabled.
581da177e4SLinus Torvalds  */
59b747c8c1SAdrian Bunk static void ptrace_untrace(struct task_struct *child)
601da177e4SLinus Torvalds {
611da177e4SLinus Torvalds 	spin_lock(&child->sighand->siglock);
626618a3e2SMatthew Wilcox 	if (task_is_traced(child)) {
631da177e4SLinus Torvalds 		if (child->signal->flags & SIGNAL_STOP_STOPPED) {
64d9ae90acSOleg Nesterov 			__set_task_state(child, TASK_STOPPED);
651da177e4SLinus Torvalds 		} else {
661da177e4SLinus Torvalds 			signal_wake_up(child, 1);
671da177e4SLinus Torvalds 		}
681da177e4SLinus Torvalds 	}
691da177e4SLinus Torvalds 	spin_unlock(&child->sighand->siglock);
701da177e4SLinus Torvalds }
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds /*
731da177e4SLinus Torvalds  * unptrace a task: move it back to its original parent and
741da177e4SLinus Torvalds  * remove it from the ptrace list.
751da177e4SLinus Torvalds  *
761da177e4SLinus Torvalds  * Must be called with the tasklist lock write-held.
771da177e4SLinus Torvalds  */
7836c8b586SIngo Molnar void __ptrace_unlink(struct task_struct *child)
791da177e4SLinus Torvalds {
805ecfbae0SOleg Nesterov 	BUG_ON(!child->ptrace);
815ecfbae0SOleg Nesterov 
821da177e4SLinus Torvalds 	child->ptrace = 0;
831da177e4SLinus Torvalds 	child->parent = child->real_parent;
84f470021aSRoland McGrath 	list_del_init(&child->ptrace_entry);
851da177e4SLinus Torvalds 
86bf53de90SMarkus Metzger 	arch_ptrace_untrace(child);
876618a3e2SMatthew Wilcox 	if (task_is_traced(child))
881da177e4SLinus Torvalds 		ptrace_untrace(child);
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds /*
921da177e4SLinus Torvalds  * Check that we have indeed attached to the thing..
931da177e4SLinus Torvalds  */
941da177e4SLinus Torvalds int ptrace_check_attach(struct task_struct *child, int kill)
951da177e4SLinus Torvalds {
961da177e4SLinus Torvalds 	int ret = -ESRCH;
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds 	/*
991da177e4SLinus Torvalds 	 * We take the read lock around doing both checks to close a
1001da177e4SLinus Torvalds 	 * possible race where someone else was tracing our child and
1011da177e4SLinus Torvalds 	 * detached between these two checks.  After this locked check,
1021da177e4SLinus Torvalds 	 * we are sure that this is our traced child and that can only
1031da177e4SLinus Torvalds 	 * be changed by us so it's not changing right after this.
1041da177e4SLinus Torvalds 	 */
1051da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
106c0c0b649SOleg Nesterov 	if ((child->ptrace & PT_PTRACED) && child->parent == current) {
1071da177e4SLinus Torvalds 		ret = 0;
108c0c0b649SOleg Nesterov 		/*
109c0c0b649SOleg Nesterov 		 * child->sighand can't be NULL, release_task()
110c0c0b649SOleg Nesterov 		 * does ptrace_unlink() before __exit_signal().
111c0c0b649SOleg Nesterov 		 */
1121da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
113d9ae90acSOleg Nesterov 		if (task_is_stopped(child))
1141da177e4SLinus Torvalds 			child->state = TASK_TRACED;
115d9ae90acSOleg Nesterov 		else if (!task_is_traced(child) && !kill)
1161da177e4SLinus Torvalds 			ret = -ESRCH;
1171da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
1181da177e4SLinus Torvalds 	}
1191da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
1201da177e4SLinus Torvalds 
121d9ae90acSOleg Nesterov 	if (!ret && !kill)
12285ba2d86SRoland McGrath 		ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH;
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds 	/* All systems go.. */
1251da177e4SLinus Torvalds 	return ret;
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds 
128006ebb40SStephen Smalley int __ptrace_may_access(struct task_struct *task, unsigned int mode)
129ab8d11beSMiklos Szeredi {
130c69e8d9cSDavid Howells 	const struct cred *cred = current_cred(), *tcred;
131b6dff3ecSDavid Howells 
132df26c40eSEric W. Biederman 	/* May we inspect the given task?
133df26c40eSEric W. Biederman 	 * This check is used both for attaching with ptrace
134df26c40eSEric W. Biederman 	 * and for allowing access to sensitive information in /proc.
135df26c40eSEric W. Biederman 	 *
136df26c40eSEric W. Biederman 	 * ptrace_attach denies several cases that /proc allows
137df26c40eSEric W. Biederman 	 * because setting up the necessary parent/child relationship
138df26c40eSEric W. Biederman 	 * or halting the specified task is impossible.
139df26c40eSEric W. Biederman 	 */
140df26c40eSEric W. Biederman 	int dumpable = 0;
141df26c40eSEric W. Biederman 	/* Don't let security modules deny introspection */
142df26c40eSEric W. Biederman 	if (task == current)
143df26c40eSEric W. Biederman 		return 0;
144c69e8d9cSDavid Howells 	rcu_read_lock();
145c69e8d9cSDavid Howells 	tcred = __task_cred(task);
146c69e8d9cSDavid Howells 	if ((cred->uid != tcred->euid ||
147c69e8d9cSDavid Howells 	     cred->uid != tcred->suid ||
148c69e8d9cSDavid Howells 	     cred->uid != tcred->uid  ||
149c69e8d9cSDavid Howells 	     cred->gid != tcred->egid ||
150c69e8d9cSDavid Howells 	     cred->gid != tcred->sgid ||
151c69e8d9cSDavid Howells 	     cred->gid != tcred->gid) &&
152c69e8d9cSDavid Howells 	    !capable(CAP_SYS_PTRACE)) {
153c69e8d9cSDavid Howells 		rcu_read_unlock();
154ab8d11beSMiklos Szeredi 		return -EPERM;
155c69e8d9cSDavid Howells 	}
156c69e8d9cSDavid Howells 	rcu_read_unlock();
157ab8d11beSMiklos Szeredi 	smp_rmb();
158df26c40eSEric W. Biederman 	if (task->mm)
1596c5d5238SKawai, Hidehiro 		dumpable = get_dumpable(task->mm);
160df26c40eSEric W. Biederman 	if (!dumpable && !capable(CAP_SYS_PTRACE))
161ab8d11beSMiklos Szeredi 		return -EPERM;
162ab8d11beSMiklos Szeredi 
1635cd9c58fSDavid Howells 	return security_ptrace_may_access(task, mode);
164ab8d11beSMiklos Szeredi }
165ab8d11beSMiklos Szeredi 
166006ebb40SStephen Smalley bool ptrace_may_access(struct task_struct *task, unsigned int mode)
167ab8d11beSMiklos Szeredi {
168ab8d11beSMiklos Szeredi 	int err;
169ab8d11beSMiklos Szeredi 	task_lock(task);
170006ebb40SStephen Smalley 	err = __ptrace_may_access(task, mode);
171ab8d11beSMiklos Szeredi 	task_unlock(task);
172006ebb40SStephen Smalley 	return (!err ? true : false);
173ab8d11beSMiklos Szeredi }
174ab8d11beSMiklos Szeredi 
1751da177e4SLinus Torvalds int ptrace_attach(struct task_struct *task)
1761da177e4SLinus Torvalds {
1771da177e4SLinus Torvalds 	int retval;
1786175ecfeSSripathi Kodi 	unsigned long flags;
179f5b40e36SLinus Torvalds 
180a5cb013dSAl Viro 	audit_ptrace(task);
181a5cb013dSAl Viro 
1821da177e4SLinus Torvalds 	retval = -EPERM;
183bac0abd6SPavel Emelyanov 	if (same_thread_group(task, current))
184f5b40e36SLinus Torvalds 		goto out;
185f5b40e36SLinus Torvalds 
186d84f4f99SDavid Howells 	/* Protect exec's credential calculations against our interference;
187d84f4f99SDavid Howells 	 * SUID, SGID and LSM creds get determined differently under ptrace.
188d84f4f99SDavid Howells 	 */
189d84f4f99SDavid Howells 	retval = mutex_lock_interruptible(&current->cred_exec_mutex);
190d84f4f99SDavid Howells 	if (retval  < 0)
191d84f4f99SDavid Howells 		goto out;
192d84f4f99SDavid Howells 
193d84f4f99SDavid Howells 	retval = -EPERM;
194f358166aSLinus Torvalds repeat:
195f358166aSLinus Torvalds 	/*
196f358166aSLinus Torvalds 	 * Nasty, nasty.
197f358166aSLinus Torvalds 	 *
198f358166aSLinus Torvalds 	 * We want to hold both the task-lock and the
199f358166aSLinus Torvalds 	 * tasklist_lock for writing at the same time.
200f358166aSLinus Torvalds 	 * But that's against the rules (tasklist_lock
201f358166aSLinus Torvalds 	 * is taken for reading by interrupts on other
202f358166aSLinus Torvalds 	 * cpu's that may have task_lock).
203f358166aSLinus Torvalds 	 */
204f5b40e36SLinus Torvalds 	task_lock(task);
2056175ecfeSSripathi Kodi 	if (!write_trylock_irqsave(&tasklist_lock, flags)) {
206f358166aSLinus Torvalds 		task_unlock(task);
207f358166aSLinus Torvalds 		do {
208f358166aSLinus Torvalds 			cpu_relax();
209f358166aSLinus Torvalds 		} while (!write_can_lock(&tasklist_lock));
210f358166aSLinus Torvalds 		goto repeat;
211f358166aSLinus Torvalds 	}
212f5b40e36SLinus Torvalds 
213df26c40eSEric W. Biederman 	if (!task->mm)
214df26c40eSEric W. Biederman 		goto bad;
2151da177e4SLinus Torvalds 	/* the same process cannot be attached many times */
2161da177e4SLinus Torvalds 	if (task->ptrace & PT_PTRACED)
2171da177e4SLinus Torvalds 		goto bad;
218006ebb40SStephen Smalley 	retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
2191da177e4SLinus Torvalds 	if (retval)
2201da177e4SLinus Torvalds 		goto bad;
2211da177e4SLinus Torvalds 
2221da177e4SLinus Torvalds 	/* Go */
2236b39c7bfSOleg Nesterov 	task->ptrace |= PT_PTRACED;
2241da177e4SLinus Torvalds 	if (capable(CAP_SYS_PTRACE))
2251da177e4SLinus Torvalds 		task->ptrace |= PT_PTRACE_CAP;
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds 	__ptrace_link(task, current);
2281da177e4SLinus Torvalds 
22933e9fc7dSOleg Nesterov 	send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
2301da177e4SLinus Torvalds bad:
2316175ecfeSSripathi Kodi 	write_unlock_irqrestore(&tasklist_lock, flags);
2321da177e4SLinus Torvalds 	task_unlock(task);
233d84f4f99SDavid Howells 	mutex_unlock(&current->cred_exec_mutex);
234f5b40e36SLinus Torvalds out:
2351da177e4SLinus Torvalds 	return retval;
2361da177e4SLinus Torvalds }
2371da177e4SLinus Torvalds 
2381da177e4SLinus Torvalds int ptrace_detach(struct task_struct *child, unsigned int data)
2391da177e4SLinus Torvalds {
2407ed20e1aSJesper Juhl 	if (!valid_signal(data))
2411da177e4SLinus Torvalds 		return -EIO;
2421da177e4SLinus Torvalds 
2431da177e4SLinus Torvalds 	/* Architecture-specific hardware disable .. */
2441da177e4SLinus Torvalds 	ptrace_disable(child);
2457d941432SRoland McGrath 	clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
2461da177e4SLinus Torvalds 
247d5f70c00SOleg Nesterov 	/* protect against de_thread()->release_task() */
24895c3eb76SOleg Nesterov 	write_lock_irq(&tasklist_lock);
24995c3eb76SOleg Nesterov 	if (child->ptrace) {
25095c3eb76SOleg Nesterov 		child->exit_code = data;
25195c3eb76SOleg Nesterov 
25295c3eb76SOleg Nesterov 		__ptrace_unlink(child);
25395c3eb76SOleg Nesterov 
25495c3eb76SOleg Nesterov 		if (!child->exit_state)
25595c3eb76SOleg Nesterov 			wake_up_process(child);
25695c3eb76SOleg Nesterov 	}
2571da177e4SLinus Torvalds 	write_unlock_irq(&tasklist_lock);
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	return 0;
2601da177e4SLinus Torvalds }
2611da177e4SLinus Torvalds 
2621da177e4SLinus Torvalds int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
2631da177e4SLinus Torvalds {
2641da177e4SLinus Torvalds 	int copied = 0;
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds 	while (len > 0) {
2671da177e4SLinus Torvalds 		char buf[128];
2681da177e4SLinus Torvalds 		int this_len, retval;
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
2711da177e4SLinus Torvalds 		retval = access_process_vm(tsk, src, buf, this_len, 0);
2721da177e4SLinus Torvalds 		if (!retval) {
2731da177e4SLinus Torvalds 			if (copied)
2741da177e4SLinus Torvalds 				break;
2751da177e4SLinus Torvalds 			return -EIO;
2761da177e4SLinus Torvalds 		}
2771da177e4SLinus Torvalds 		if (copy_to_user(dst, buf, retval))
2781da177e4SLinus Torvalds 			return -EFAULT;
2791da177e4SLinus Torvalds 		copied += retval;
2801da177e4SLinus Torvalds 		src += retval;
2811da177e4SLinus Torvalds 		dst += retval;
2821da177e4SLinus Torvalds 		len -= retval;
2831da177e4SLinus Torvalds 	}
2841da177e4SLinus Torvalds 	return copied;
2851da177e4SLinus Torvalds }
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
2881da177e4SLinus Torvalds {
2891da177e4SLinus Torvalds 	int copied = 0;
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds 	while (len > 0) {
2921da177e4SLinus Torvalds 		char buf[128];
2931da177e4SLinus Torvalds 		int this_len, retval;
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
2961da177e4SLinus Torvalds 		if (copy_from_user(buf, src, this_len))
2971da177e4SLinus Torvalds 			return -EFAULT;
2981da177e4SLinus Torvalds 		retval = access_process_vm(tsk, dst, buf, this_len, 1);
2991da177e4SLinus Torvalds 		if (!retval) {
3001da177e4SLinus Torvalds 			if (copied)
3011da177e4SLinus Torvalds 				break;
3021da177e4SLinus Torvalds 			return -EIO;
3031da177e4SLinus Torvalds 		}
3041da177e4SLinus Torvalds 		copied += retval;
3051da177e4SLinus Torvalds 		src += retval;
3061da177e4SLinus Torvalds 		dst += retval;
3071da177e4SLinus Torvalds 		len -= retval;
3081da177e4SLinus Torvalds 	}
3091da177e4SLinus Torvalds 	return copied;
3101da177e4SLinus Torvalds }
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds static int ptrace_setoptions(struct task_struct *child, long data)
3131da177e4SLinus Torvalds {
3141da177e4SLinus Torvalds 	child->ptrace &= ~PT_TRACE_MASK;
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACESYSGOOD)
3171da177e4SLinus Torvalds 		child->ptrace |= PT_TRACESYSGOOD;
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEFORK)
3201da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_FORK;
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORK)
3231da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK;
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACECLONE)
3261da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_CLONE;
3271da177e4SLinus Torvalds 
3281da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXEC)
3291da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXEC;
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORKDONE)
3321da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK_DONE;
3331da177e4SLinus Torvalds 
3341da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXIT)
3351da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXIT;
3361da177e4SLinus Torvalds 
3371da177e4SLinus Torvalds 	return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
3381da177e4SLinus Torvalds }
3391da177e4SLinus Torvalds 
340e16b2781SRoland McGrath static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info)
3411da177e4SLinus Torvalds {
3421da177e4SLinus Torvalds 	int error = -ESRCH;
3431da177e4SLinus Torvalds 
3441da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
3451da177e4SLinus Torvalds 	if (likely(child->sighand != NULL)) {
3461da177e4SLinus Torvalds 		error = -EINVAL;
3471da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
3481da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
349e16b2781SRoland McGrath 			*info = *child->last_siginfo;
3501da177e4SLinus Torvalds 			error = 0;
3511da177e4SLinus Torvalds 		}
3521da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
3531da177e4SLinus Torvalds 	}
3541da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
3551da177e4SLinus Torvalds 	return error;
3561da177e4SLinus Torvalds }
3571da177e4SLinus Torvalds 
358e16b2781SRoland McGrath static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info)
3591da177e4SLinus Torvalds {
3601da177e4SLinus Torvalds 	int error = -ESRCH;
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
3631da177e4SLinus Torvalds 	if (likely(child->sighand != NULL)) {
3641da177e4SLinus Torvalds 		error = -EINVAL;
3651da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
3661da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
367e16b2781SRoland McGrath 			*child->last_siginfo = *info;
3681da177e4SLinus Torvalds 			error = 0;
3691da177e4SLinus Torvalds 		}
3701da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
3711da177e4SLinus Torvalds 	}
3721da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
3731da177e4SLinus Torvalds 	return error;
3741da177e4SLinus Torvalds }
3751da177e4SLinus Torvalds 
37636df29d7SRoland McGrath 
37736df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
37836df29d7SRoland McGrath #define is_singlestep(request)		((request) == PTRACE_SINGLESTEP)
37936df29d7SRoland McGrath #else
38036df29d7SRoland McGrath #define is_singlestep(request)		0
38136df29d7SRoland McGrath #endif
38236df29d7SRoland McGrath 
3835b88abbfSRoland McGrath #ifdef PTRACE_SINGLEBLOCK
3845b88abbfSRoland McGrath #define is_singleblock(request)		((request) == PTRACE_SINGLEBLOCK)
3855b88abbfSRoland McGrath #else
3865b88abbfSRoland McGrath #define is_singleblock(request)		0
3875b88abbfSRoland McGrath #endif
3885b88abbfSRoland McGrath 
38936df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
39036df29d7SRoland McGrath #define is_sysemu_singlestep(request)	((request) == PTRACE_SYSEMU_SINGLESTEP)
39136df29d7SRoland McGrath #else
39236df29d7SRoland McGrath #define is_sysemu_singlestep(request)	0
39336df29d7SRoland McGrath #endif
39436df29d7SRoland McGrath 
39536df29d7SRoland McGrath static int ptrace_resume(struct task_struct *child, long request, long data)
39636df29d7SRoland McGrath {
39736df29d7SRoland McGrath 	if (!valid_signal(data))
39836df29d7SRoland McGrath 		return -EIO;
39936df29d7SRoland McGrath 
40036df29d7SRoland McGrath 	if (request == PTRACE_SYSCALL)
40136df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
40236df29d7SRoland McGrath 	else
40336df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
40436df29d7SRoland McGrath 
40536df29d7SRoland McGrath #ifdef TIF_SYSCALL_EMU
40636df29d7SRoland McGrath 	if (request == PTRACE_SYSEMU || request == PTRACE_SYSEMU_SINGLESTEP)
40736df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_EMU);
40836df29d7SRoland McGrath 	else
40936df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
41036df29d7SRoland McGrath #endif
41136df29d7SRoland McGrath 
4125b88abbfSRoland McGrath 	if (is_singleblock(request)) {
4135b88abbfSRoland McGrath 		if (unlikely(!arch_has_block_step()))
4145b88abbfSRoland McGrath 			return -EIO;
4155b88abbfSRoland McGrath 		user_enable_block_step(child);
4165b88abbfSRoland McGrath 	} else if (is_singlestep(request) || is_sysemu_singlestep(request)) {
41736df29d7SRoland McGrath 		if (unlikely(!arch_has_single_step()))
41836df29d7SRoland McGrath 			return -EIO;
41936df29d7SRoland McGrath 		user_enable_single_step(child);
42036df29d7SRoland McGrath 	}
42136df29d7SRoland McGrath 	else
42236df29d7SRoland McGrath 		user_disable_single_step(child);
42336df29d7SRoland McGrath 
42436df29d7SRoland McGrath 	child->exit_code = data;
42536df29d7SRoland McGrath 	wake_up_process(child);
42636df29d7SRoland McGrath 
42736df29d7SRoland McGrath 	return 0;
42836df29d7SRoland McGrath }
42936df29d7SRoland McGrath 
4301da177e4SLinus Torvalds int ptrace_request(struct task_struct *child, long request,
4311da177e4SLinus Torvalds 		   long addr, long data)
4321da177e4SLinus Torvalds {
4331da177e4SLinus Torvalds 	int ret = -EIO;
434e16b2781SRoland McGrath 	siginfo_t siginfo;
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 	switch (request) {
43716c3e389SRoland McGrath 	case PTRACE_PEEKTEXT:
43816c3e389SRoland McGrath 	case PTRACE_PEEKDATA:
43916c3e389SRoland McGrath 		return generic_ptrace_peekdata(child, addr, data);
44016c3e389SRoland McGrath 	case PTRACE_POKETEXT:
44116c3e389SRoland McGrath 	case PTRACE_POKEDATA:
44216c3e389SRoland McGrath 		return generic_ptrace_pokedata(child, addr, data);
44316c3e389SRoland McGrath 
4441da177e4SLinus Torvalds #ifdef PTRACE_OLDSETOPTIONS
4451da177e4SLinus Torvalds 	case PTRACE_OLDSETOPTIONS:
4461da177e4SLinus Torvalds #endif
4471da177e4SLinus Torvalds 	case PTRACE_SETOPTIONS:
4481da177e4SLinus Torvalds 		ret = ptrace_setoptions(child, data);
4491da177e4SLinus Torvalds 		break;
4501da177e4SLinus Torvalds 	case PTRACE_GETEVENTMSG:
4511da177e4SLinus Torvalds 		ret = put_user(child->ptrace_message, (unsigned long __user *) data);
4521da177e4SLinus Torvalds 		break;
453e16b2781SRoland McGrath 
4541da177e4SLinus Torvalds 	case PTRACE_GETSIGINFO:
455e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
456e16b2781SRoland McGrath 		if (!ret)
457e16b2781SRoland McGrath 			ret = copy_siginfo_to_user((siginfo_t __user *) data,
458e16b2781SRoland McGrath 						   &siginfo);
4591da177e4SLinus Torvalds 		break;
460e16b2781SRoland McGrath 
4611da177e4SLinus Torvalds 	case PTRACE_SETSIGINFO:
462e16b2781SRoland McGrath 		if (copy_from_user(&siginfo, (siginfo_t __user *) data,
463e16b2781SRoland McGrath 				   sizeof siginfo))
464e16b2781SRoland McGrath 			ret = -EFAULT;
465e16b2781SRoland McGrath 		else
466e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
4671da177e4SLinus Torvalds 		break;
468e16b2781SRoland McGrath 
4691bcf5482SAlexey Dobriyan 	case PTRACE_DETACH:	 /* detach a process that was attached. */
4701bcf5482SAlexey Dobriyan 		ret = ptrace_detach(child, data);
4711bcf5482SAlexey Dobriyan 		break;
47236df29d7SRoland McGrath 
47336df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
47436df29d7SRoland McGrath 	case PTRACE_SINGLESTEP:
47536df29d7SRoland McGrath #endif
4765b88abbfSRoland McGrath #ifdef PTRACE_SINGLEBLOCK
4775b88abbfSRoland McGrath 	case PTRACE_SINGLEBLOCK:
4785b88abbfSRoland McGrath #endif
47936df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
48036df29d7SRoland McGrath 	case PTRACE_SYSEMU:
48136df29d7SRoland McGrath 	case PTRACE_SYSEMU_SINGLESTEP:
48236df29d7SRoland McGrath #endif
48336df29d7SRoland McGrath 	case PTRACE_SYSCALL:
48436df29d7SRoland McGrath 	case PTRACE_CONT:
48536df29d7SRoland McGrath 		return ptrace_resume(child, request, data);
48636df29d7SRoland McGrath 
48736df29d7SRoland McGrath 	case PTRACE_KILL:
48836df29d7SRoland McGrath 		if (child->exit_state)	/* already dead */
48936df29d7SRoland McGrath 			return 0;
49036df29d7SRoland McGrath 		return ptrace_resume(child, request, SIGKILL);
49136df29d7SRoland McGrath 
4921da177e4SLinus Torvalds 	default:
4931da177e4SLinus Torvalds 		break;
4941da177e4SLinus Torvalds 	}
4951da177e4SLinus Torvalds 
4961da177e4SLinus Torvalds 	return ret;
4971da177e4SLinus Torvalds }
498481bed45SChristoph Hellwig 
4996b9c7ed8SChristoph Hellwig /**
5006b9c7ed8SChristoph Hellwig  * ptrace_traceme  --  helper for PTRACE_TRACEME
5016b9c7ed8SChristoph Hellwig  *
5026b9c7ed8SChristoph Hellwig  * Performs checks and sets PT_PTRACED.
5036b9c7ed8SChristoph Hellwig  * Should be used by all ptrace implementations for PTRACE_TRACEME.
5046b9c7ed8SChristoph Hellwig  */
5056b9c7ed8SChristoph Hellwig int ptrace_traceme(void)
506481bed45SChristoph Hellwig {
507f5b40e36SLinus Torvalds 	int ret = -EPERM;
508481bed45SChristoph Hellwig 
509481bed45SChristoph Hellwig 	/*
510481bed45SChristoph Hellwig 	 * Are we already being traced?
511481bed45SChristoph Hellwig 	 */
512f470021aSRoland McGrath repeat:
513f5b40e36SLinus Torvalds 	task_lock(current);
514f5b40e36SLinus Torvalds 	if (!(current->ptrace & PT_PTRACED)) {
515f470021aSRoland McGrath 		/*
516f470021aSRoland McGrath 		 * See ptrace_attach() comments about the locking here.
517f470021aSRoland McGrath 		 */
518f470021aSRoland McGrath 		unsigned long flags;
519f470021aSRoland McGrath 		if (!write_trylock_irqsave(&tasklist_lock, flags)) {
520f470021aSRoland McGrath 			task_unlock(current);
521f470021aSRoland McGrath 			do {
522f470021aSRoland McGrath 				cpu_relax();
523f470021aSRoland McGrath 			} while (!write_can_lock(&tasklist_lock));
524f470021aSRoland McGrath 			goto repeat;
525f470021aSRoland McGrath 		}
526f470021aSRoland McGrath 
5275cd9c58fSDavid Howells 		ret = security_ptrace_traceme(current->parent);
528f470021aSRoland McGrath 
529481bed45SChristoph Hellwig 		/*
530481bed45SChristoph Hellwig 		 * Set the ptrace bit in the process ptrace flags.
531f470021aSRoland McGrath 		 * Then link us on our parent's ptraced list.
532481bed45SChristoph Hellwig 		 */
533f470021aSRoland McGrath 		if (!ret) {
534481bed45SChristoph Hellwig 			current->ptrace |= PT_PTRACED;
535f470021aSRoland McGrath 			__ptrace_link(current, current->real_parent);
536f470021aSRoland McGrath 		}
537f470021aSRoland McGrath 
538f470021aSRoland McGrath 		write_unlock_irqrestore(&tasklist_lock, flags);
539f5b40e36SLinus Torvalds 	}
540f5b40e36SLinus Torvalds 	task_unlock(current);
541f5b40e36SLinus Torvalds 	return ret;
542481bed45SChristoph Hellwig }
543481bed45SChristoph Hellwig 
5446b9c7ed8SChristoph Hellwig /**
5456b9c7ed8SChristoph Hellwig  * ptrace_get_task_struct  --  grab a task struct reference for ptrace
5466b9c7ed8SChristoph Hellwig  * @pid:       process id to grab a task_struct reference of
5476b9c7ed8SChristoph Hellwig  *
5486b9c7ed8SChristoph Hellwig  * This function is a helper for ptrace implementations.  It checks
5496b9c7ed8SChristoph Hellwig  * permissions and then grabs a task struct for use of the actual
5506b9c7ed8SChristoph Hellwig  * ptrace implementation.
5516b9c7ed8SChristoph Hellwig  *
5526b9c7ed8SChristoph Hellwig  * Returns the task_struct for @pid or an ERR_PTR() on failure.
5536b9c7ed8SChristoph Hellwig  */
5546b9c7ed8SChristoph Hellwig struct task_struct *ptrace_get_task_struct(pid_t pid)
5556b9c7ed8SChristoph Hellwig {
5566b9c7ed8SChristoph Hellwig 	struct task_struct *child;
5576b9c7ed8SChristoph Hellwig 
558481bed45SChristoph Hellwig 	read_lock(&tasklist_lock);
559228ebcbeSPavel Emelyanov 	child = find_task_by_vpid(pid);
560481bed45SChristoph Hellwig 	if (child)
561481bed45SChristoph Hellwig 		get_task_struct(child);
562f400e198SSukadev Bhattiprolu 
563481bed45SChristoph Hellwig 	read_unlock(&tasklist_lock);
564481bed45SChristoph Hellwig 	if (!child)
5656b9c7ed8SChristoph Hellwig 		return ERR_PTR(-ESRCH);
5666b9c7ed8SChristoph Hellwig 	return child;
567481bed45SChristoph Hellwig }
568481bed45SChristoph Hellwig 
5690ac15559SChristoph Hellwig #ifndef arch_ptrace_attach
5700ac15559SChristoph Hellwig #define arch_ptrace_attach(child)	do { } while (0)
5710ac15559SChristoph Hellwig #endif
5720ac15559SChristoph Hellwig 
5731e7bfb21SHeiko Carstens SYSCALL_DEFINE4(ptrace, long, request, long, pid, long, addr, long, data)
574481bed45SChristoph Hellwig {
575481bed45SChristoph Hellwig 	struct task_struct *child;
576481bed45SChristoph Hellwig 	long ret;
577481bed45SChristoph Hellwig 
578481bed45SChristoph Hellwig 	/*
579481bed45SChristoph Hellwig 	 * This lock_kernel fixes a subtle race with suid exec
580481bed45SChristoph Hellwig 	 */
581481bed45SChristoph Hellwig 	lock_kernel();
5826b9c7ed8SChristoph Hellwig 	if (request == PTRACE_TRACEME) {
5836b9c7ed8SChristoph Hellwig 		ret = ptrace_traceme();
5846ea6dd93SHaavard Skinnemoen 		if (!ret)
5856ea6dd93SHaavard Skinnemoen 			arch_ptrace_attach(current);
586481bed45SChristoph Hellwig 		goto out;
5876b9c7ed8SChristoph Hellwig 	}
5886b9c7ed8SChristoph Hellwig 
5896b9c7ed8SChristoph Hellwig 	child = ptrace_get_task_struct(pid);
5906b9c7ed8SChristoph Hellwig 	if (IS_ERR(child)) {
5916b9c7ed8SChristoph Hellwig 		ret = PTR_ERR(child);
5926b9c7ed8SChristoph Hellwig 		goto out;
5936b9c7ed8SChristoph Hellwig 	}
594481bed45SChristoph Hellwig 
595481bed45SChristoph Hellwig 	if (request == PTRACE_ATTACH) {
596481bed45SChristoph Hellwig 		ret = ptrace_attach(child);
5970ac15559SChristoph Hellwig 		/*
5980ac15559SChristoph Hellwig 		 * Some architectures need to do book-keeping after
5990ac15559SChristoph Hellwig 		 * a ptrace attach.
6000ac15559SChristoph Hellwig 		 */
6010ac15559SChristoph Hellwig 		if (!ret)
6020ac15559SChristoph Hellwig 			arch_ptrace_attach(child);
603005f18dfSChristoph Hellwig 		goto out_put_task_struct;
604481bed45SChristoph Hellwig 	}
605481bed45SChristoph Hellwig 
606481bed45SChristoph Hellwig 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
607481bed45SChristoph Hellwig 	if (ret < 0)
608481bed45SChristoph Hellwig 		goto out_put_task_struct;
609481bed45SChristoph Hellwig 
610481bed45SChristoph Hellwig 	ret = arch_ptrace(child, request, addr, data);
611481bed45SChristoph Hellwig 	if (ret < 0)
612481bed45SChristoph Hellwig 		goto out_put_task_struct;
613481bed45SChristoph Hellwig 
614481bed45SChristoph Hellwig  out_put_task_struct:
615481bed45SChristoph Hellwig 	put_task_struct(child);
616481bed45SChristoph Hellwig  out:
617481bed45SChristoph Hellwig 	unlock_kernel();
618481bed45SChristoph Hellwig 	return ret;
619481bed45SChristoph Hellwig }
62076647323SAlexey Dobriyan 
62176647323SAlexey Dobriyan int generic_ptrace_peekdata(struct task_struct *tsk, long addr, long data)
62276647323SAlexey Dobriyan {
62376647323SAlexey Dobriyan 	unsigned long tmp;
62476647323SAlexey Dobriyan 	int copied;
62576647323SAlexey Dobriyan 
62676647323SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
62776647323SAlexey Dobriyan 	if (copied != sizeof(tmp))
62876647323SAlexey Dobriyan 		return -EIO;
62976647323SAlexey Dobriyan 	return put_user(tmp, (unsigned long __user *)data);
63076647323SAlexey Dobriyan }
631f284ce72SAlexey Dobriyan 
632f284ce72SAlexey Dobriyan int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data)
633f284ce72SAlexey Dobriyan {
634f284ce72SAlexey Dobriyan 	int copied;
635f284ce72SAlexey Dobriyan 
636f284ce72SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
637f284ce72SAlexey Dobriyan 	return (copied == sizeof(data)) ? 0 : -EIO;
638f284ce72SAlexey Dobriyan }
639032d82d9SRoland McGrath 
64096b8936aSChristoph Hellwig #if defined CONFIG_COMPAT
641032d82d9SRoland McGrath #include <linux/compat.h>
642032d82d9SRoland McGrath 
643032d82d9SRoland McGrath int compat_ptrace_request(struct task_struct *child, compat_long_t request,
644032d82d9SRoland McGrath 			  compat_ulong_t addr, compat_ulong_t data)
645032d82d9SRoland McGrath {
646032d82d9SRoland McGrath 	compat_ulong_t __user *datap = compat_ptr(data);
647032d82d9SRoland McGrath 	compat_ulong_t word;
648e16b2781SRoland McGrath 	siginfo_t siginfo;
649032d82d9SRoland McGrath 	int ret;
650032d82d9SRoland McGrath 
651032d82d9SRoland McGrath 	switch (request) {
652032d82d9SRoland McGrath 	case PTRACE_PEEKTEXT:
653032d82d9SRoland McGrath 	case PTRACE_PEEKDATA:
654032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &word, sizeof(word), 0);
655032d82d9SRoland McGrath 		if (ret != sizeof(word))
656032d82d9SRoland McGrath 			ret = -EIO;
657032d82d9SRoland McGrath 		else
658032d82d9SRoland McGrath 			ret = put_user(word, datap);
659032d82d9SRoland McGrath 		break;
660032d82d9SRoland McGrath 
661032d82d9SRoland McGrath 	case PTRACE_POKETEXT:
662032d82d9SRoland McGrath 	case PTRACE_POKEDATA:
663032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &data, sizeof(data), 1);
664032d82d9SRoland McGrath 		ret = (ret != sizeof(data) ? -EIO : 0);
665032d82d9SRoland McGrath 		break;
666032d82d9SRoland McGrath 
667032d82d9SRoland McGrath 	case PTRACE_GETEVENTMSG:
668032d82d9SRoland McGrath 		ret = put_user((compat_ulong_t) child->ptrace_message, datap);
669032d82d9SRoland McGrath 		break;
670032d82d9SRoland McGrath 
671e16b2781SRoland McGrath 	case PTRACE_GETSIGINFO:
672e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
673e16b2781SRoland McGrath 		if (!ret)
674e16b2781SRoland McGrath 			ret = copy_siginfo_to_user32(
675e16b2781SRoland McGrath 				(struct compat_siginfo __user *) datap,
676e16b2781SRoland McGrath 				&siginfo);
677e16b2781SRoland McGrath 		break;
678e16b2781SRoland McGrath 
679e16b2781SRoland McGrath 	case PTRACE_SETSIGINFO:
680e16b2781SRoland McGrath 		memset(&siginfo, 0, sizeof siginfo);
681e16b2781SRoland McGrath 		if (copy_siginfo_from_user32(
682e16b2781SRoland McGrath 			    &siginfo, (struct compat_siginfo __user *) datap))
683e16b2781SRoland McGrath 			ret = -EFAULT;
684e16b2781SRoland McGrath 		else
685e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
686e16b2781SRoland McGrath 		break;
687e16b2781SRoland McGrath 
688032d82d9SRoland McGrath 	default:
689032d82d9SRoland McGrath 		ret = ptrace_request(child, request, addr, data);
690032d82d9SRoland McGrath 	}
691032d82d9SRoland McGrath 
692032d82d9SRoland McGrath 	return ret;
693032d82d9SRoland McGrath }
694c269f196SRoland McGrath 
695c269f196SRoland McGrath asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
696c269f196SRoland McGrath 				  compat_long_t addr, compat_long_t data)
697c269f196SRoland McGrath {
698c269f196SRoland McGrath 	struct task_struct *child;
699c269f196SRoland McGrath 	long ret;
700c269f196SRoland McGrath 
701c269f196SRoland McGrath 	/*
702c269f196SRoland McGrath 	 * This lock_kernel fixes a subtle race with suid exec
703c269f196SRoland McGrath 	 */
704c269f196SRoland McGrath 	lock_kernel();
705c269f196SRoland McGrath 	if (request == PTRACE_TRACEME) {
706c269f196SRoland McGrath 		ret = ptrace_traceme();
707c269f196SRoland McGrath 		goto out;
708c269f196SRoland McGrath 	}
709c269f196SRoland McGrath 
710c269f196SRoland McGrath 	child = ptrace_get_task_struct(pid);
711c269f196SRoland McGrath 	if (IS_ERR(child)) {
712c269f196SRoland McGrath 		ret = PTR_ERR(child);
713c269f196SRoland McGrath 		goto out;
714c269f196SRoland McGrath 	}
715c269f196SRoland McGrath 
716c269f196SRoland McGrath 	if (request == PTRACE_ATTACH) {
717c269f196SRoland McGrath 		ret = ptrace_attach(child);
718c269f196SRoland McGrath 		/*
719c269f196SRoland McGrath 		 * Some architectures need to do book-keeping after
720c269f196SRoland McGrath 		 * a ptrace attach.
721c269f196SRoland McGrath 		 */
722c269f196SRoland McGrath 		if (!ret)
723c269f196SRoland McGrath 			arch_ptrace_attach(child);
724c269f196SRoland McGrath 		goto out_put_task_struct;
725c269f196SRoland McGrath 	}
726c269f196SRoland McGrath 
727c269f196SRoland McGrath 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
728c269f196SRoland McGrath 	if (!ret)
729c269f196SRoland McGrath 		ret = compat_arch_ptrace(child, request, addr, data);
730c269f196SRoland McGrath 
731c269f196SRoland McGrath  out_put_task_struct:
732c269f196SRoland McGrath 	put_task_struct(child);
733c269f196SRoland McGrath  out:
734c269f196SRoland McGrath 	unlock_kernel();
735c269f196SRoland McGrath 	return ret;
736c269f196SRoland McGrath }
73796b8936aSChristoph Hellwig #endif	/* CONFIG_COMPAT */
738