xref: /openbmc/linux/kernel/ptrace.c (revision 96b8936a)
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 
281da177e4SLinus Torvalds /*
291da177e4SLinus Torvalds  * ptrace a task: make the debugger its new parent and
301da177e4SLinus Torvalds  * move it to the ptrace list.
311da177e4SLinus Torvalds  *
321da177e4SLinus Torvalds  * Must be called with the tasklist lock write-held.
331da177e4SLinus Torvalds  */
3436c8b586SIngo Molnar void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
351da177e4SLinus Torvalds {
36f470021aSRoland McGrath 	BUG_ON(!list_empty(&child->ptrace_entry));
37f470021aSRoland McGrath 	list_add(&child->ptrace_entry, &new_parent->ptraced);
381da177e4SLinus Torvalds 	child->parent = new_parent;
391da177e4SLinus Torvalds }
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds /*
421da177e4SLinus Torvalds  * Turn a tracing stop into a normal stop now, since with no tracer there
431da177e4SLinus Torvalds  * would be no way to wake it up with SIGCONT or SIGKILL.  If there was a
441da177e4SLinus Torvalds  * signal sent that would resume the child, but didn't because it was in
451da177e4SLinus Torvalds  * TASK_TRACED, resume it now.
461da177e4SLinus Torvalds  * Requires that irqs be disabled.
471da177e4SLinus Torvalds  */
48b747c8c1SAdrian Bunk static void ptrace_untrace(struct task_struct *child)
491da177e4SLinus Torvalds {
501da177e4SLinus Torvalds 	spin_lock(&child->sighand->siglock);
516618a3e2SMatthew Wilcox 	if (task_is_traced(child)) {
521da177e4SLinus Torvalds 		if (child->signal->flags & SIGNAL_STOP_STOPPED) {
53d9ae90acSOleg Nesterov 			__set_task_state(child, TASK_STOPPED);
541da177e4SLinus Torvalds 		} else {
551da177e4SLinus Torvalds 			signal_wake_up(child, 1);
561da177e4SLinus Torvalds 		}
571da177e4SLinus Torvalds 	}
581da177e4SLinus Torvalds 	spin_unlock(&child->sighand->siglock);
591da177e4SLinus Torvalds }
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds /*
621da177e4SLinus Torvalds  * unptrace a task: move it back to its original parent and
631da177e4SLinus Torvalds  * remove it from the ptrace list.
641da177e4SLinus Torvalds  *
651da177e4SLinus Torvalds  * Must be called with the tasklist lock write-held.
661da177e4SLinus Torvalds  */
6736c8b586SIngo Molnar void __ptrace_unlink(struct task_struct *child)
681da177e4SLinus Torvalds {
695ecfbae0SOleg Nesterov 	BUG_ON(!child->ptrace);
705ecfbae0SOleg Nesterov 
711da177e4SLinus Torvalds 	child->ptrace = 0;
721da177e4SLinus Torvalds 	child->parent = child->real_parent;
73f470021aSRoland McGrath 	list_del_init(&child->ptrace_entry);
741da177e4SLinus Torvalds 
756618a3e2SMatthew Wilcox 	if (task_is_traced(child))
761da177e4SLinus Torvalds 		ptrace_untrace(child);
771da177e4SLinus Torvalds }
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds /*
801da177e4SLinus Torvalds  * Check that we have indeed attached to the thing..
811da177e4SLinus Torvalds  */
821da177e4SLinus Torvalds int ptrace_check_attach(struct task_struct *child, int kill)
831da177e4SLinus Torvalds {
841da177e4SLinus Torvalds 	int ret = -ESRCH;
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 	/*
871da177e4SLinus Torvalds 	 * We take the read lock around doing both checks to close a
881da177e4SLinus Torvalds 	 * possible race where someone else was tracing our child and
891da177e4SLinus Torvalds 	 * detached between these two checks.  After this locked check,
901da177e4SLinus Torvalds 	 * we are sure that this is our traced child and that can only
911da177e4SLinus Torvalds 	 * be changed by us so it's not changing right after this.
921da177e4SLinus Torvalds 	 */
931da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
94c0c0b649SOleg Nesterov 	if ((child->ptrace & PT_PTRACED) && child->parent == current) {
951da177e4SLinus Torvalds 		ret = 0;
96c0c0b649SOleg Nesterov 		/*
97c0c0b649SOleg Nesterov 		 * child->sighand can't be NULL, release_task()
98c0c0b649SOleg Nesterov 		 * does ptrace_unlink() before __exit_signal().
99c0c0b649SOleg Nesterov 		 */
1001da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
101d9ae90acSOleg Nesterov 		if (task_is_stopped(child))
1021da177e4SLinus Torvalds 			child->state = TASK_TRACED;
103d9ae90acSOleg Nesterov 		else if (!task_is_traced(child) && !kill)
1041da177e4SLinus Torvalds 			ret = -ESRCH;
1051da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
1061da177e4SLinus Torvalds 	}
1071da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
1081da177e4SLinus Torvalds 
109d9ae90acSOleg Nesterov 	if (!ret && !kill)
11085ba2d86SRoland McGrath 		ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH;
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds 	/* All systems go.. */
1131da177e4SLinus Torvalds 	return ret;
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds 
116006ebb40SStephen Smalley int __ptrace_may_access(struct task_struct *task, unsigned int mode)
117ab8d11beSMiklos Szeredi {
118df26c40eSEric W. Biederman 	/* May we inspect the given task?
119df26c40eSEric W. Biederman 	 * This check is used both for attaching with ptrace
120df26c40eSEric W. Biederman 	 * and for allowing access to sensitive information in /proc.
121df26c40eSEric W. Biederman 	 *
122df26c40eSEric W. Biederman 	 * ptrace_attach denies several cases that /proc allows
123df26c40eSEric W. Biederman 	 * because setting up the necessary parent/child relationship
124df26c40eSEric W. Biederman 	 * or halting the specified task is impossible.
125df26c40eSEric W. Biederman 	 */
126df26c40eSEric W. Biederman 	int dumpable = 0;
127df26c40eSEric W. Biederman 	/* Don't let security modules deny introspection */
128df26c40eSEric W. Biederman 	if (task == current)
129df26c40eSEric W. Biederman 		return 0;
130ab8d11beSMiklos Szeredi 	if (((current->uid != task->euid) ||
131ab8d11beSMiklos Szeredi 	     (current->uid != task->suid) ||
132ab8d11beSMiklos Szeredi 	     (current->uid != task->uid) ||
133ab8d11beSMiklos Szeredi 	     (current->gid != task->egid) ||
134ab8d11beSMiklos Szeredi 	     (current->gid != task->sgid) ||
135ab8d11beSMiklos Szeredi 	     (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
136ab8d11beSMiklos Szeredi 		return -EPERM;
137ab8d11beSMiklos Szeredi 	smp_rmb();
138df26c40eSEric W. Biederman 	if (task->mm)
1396c5d5238SKawai, Hidehiro 		dumpable = get_dumpable(task->mm);
140df26c40eSEric W. Biederman 	if (!dumpable && !capable(CAP_SYS_PTRACE))
141ab8d11beSMiklos Szeredi 		return -EPERM;
142ab8d11beSMiklos Szeredi 
1435cd9c58fSDavid Howells 	return security_ptrace_may_access(task, mode);
144ab8d11beSMiklos Szeredi }
145ab8d11beSMiklos Szeredi 
146006ebb40SStephen Smalley bool ptrace_may_access(struct task_struct *task, unsigned int mode)
147ab8d11beSMiklos Szeredi {
148ab8d11beSMiklos Szeredi 	int err;
149ab8d11beSMiklos Szeredi 	task_lock(task);
150006ebb40SStephen Smalley 	err = __ptrace_may_access(task, mode);
151ab8d11beSMiklos Szeredi 	task_unlock(task);
152006ebb40SStephen Smalley 	return (!err ? true : false);
153ab8d11beSMiklos Szeredi }
154ab8d11beSMiklos Szeredi 
1551da177e4SLinus Torvalds int ptrace_attach(struct task_struct *task)
1561da177e4SLinus Torvalds {
1571da177e4SLinus Torvalds 	int retval;
1586175ecfeSSripathi Kodi 	unsigned long flags;
159f5b40e36SLinus Torvalds 
160a5cb013dSAl Viro 	audit_ptrace(task);
161a5cb013dSAl Viro 
1621da177e4SLinus Torvalds 	retval = -EPERM;
163bac0abd6SPavel Emelyanov 	if (same_thread_group(task, current))
164f5b40e36SLinus Torvalds 		goto out;
165f5b40e36SLinus Torvalds 
166f358166aSLinus Torvalds repeat:
167f358166aSLinus Torvalds 	/*
168f358166aSLinus Torvalds 	 * Nasty, nasty.
169f358166aSLinus Torvalds 	 *
170f358166aSLinus Torvalds 	 * We want to hold both the task-lock and the
171f358166aSLinus Torvalds 	 * tasklist_lock for writing at the same time.
172f358166aSLinus Torvalds 	 * But that's against the rules (tasklist_lock
173f358166aSLinus Torvalds 	 * is taken for reading by interrupts on other
174f358166aSLinus Torvalds 	 * cpu's that may have task_lock).
175f358166aSLinus Torvalds 	 */
176f5b40e36SLinus Torvalds 	task_lock(task);
1776175ecfeSSripathi Kodi 	if (!write_trylock_irqsave(&tasklist_lock, flags)) {
178f358166aSLinus Torvalds 		task_unlock(task);
179f358166aSLinus Torvalds 		do {
180f358166aSLinus Torvalds 			cpu_relax();
181f358166aSLinus Torvalds 		} while (!write_can_lock(&tasklist_lock));
182f358166aSLinus Torvalds 		goto repeat;
183f358166aSLinus Torvalds 	}
184f5b40e36SLinus Torvalds 
185df26c40eSEric W. Biederman 	if (!task->mm)
186df26c40eSEric W. Biederman 		goto bad;
1871da177e4SLinus Torvalds 	/* the same process cannot be attached many times */
1881da177e4SLinus Torvalds 	if (task->ptrace & PT_PTRACED)
1891da177e4SLinus Torvalds 		goto bad;
190006ebb40SStephen Smalley 	retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
1911da177e4SLinus Torvalds 	if (retval)
1921da177e4SLinus Torvalds 		goto bad;
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds 	/* Go */
1956b39c7bfSOleg Nesterov 	task->ptrace |= PT_PTRACED;
1961da177e4SLinus Torvalds 	if (capable(CAP_SYS_PTRACE))
1971da177e4SLinus Torvalds 		task->ptrace |= PT_PTRACE_CAP;
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds 	__ptrace_link(task, current);
2001da177e4SLinus Torvalds 
20133e9fc7dSOleg Nesterov 	send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
2021da177e4SLinus Torvalds bad:
2036175ecfeSSripathi Kodi 	write_unlock_irqrestore(&tasklist_lock, flags);
2041da177e4SLinus Torvalds 	task_unlock(task);
205f5b40e36SLinus Torvalds out:
2061da177e4SLinus Torvalds 	return retval;
2071da177e4SLinus Torvalds }
2081da177e4SLinus Torvalds 
209d5f70c00SOleg Nesterov static inline void __ptrace_detach(struct task_struct *child, unsigned int data)
2105ecfbae0SOleg Nesterov {
2115ecfbae0SOleg Nesterov 	child->exit_code = data;
2125ecfbae0SOleg Nesterov 	/* .. re-parent .. */
2135ecfbae0SOleg Nesterov 	__ptrace_unlink(child);
2145ecfbae0SOleg Nesterov 	/* .. and wake it up. */
2155ecfbae0SOleg Nesterov 	if (child->exit_state != EXIT_ZOMBIE)
2165ecfbae0SOleg Nesterov 		wake_up_process(child);
2175ecfbae0SOleg Nesterov }
2185ecfbae0SOleg Nesterov 
2191da177e4SLinus Torvalds int ptrace_detach(struct task_struct *child, unsigned int data)
2201da177e4SLinus Torvalds {
2217ed20e1aSJesper Juhl 	if (!valid_signal(data))
2221da177e4SLinus Torvalds 		return -EIO;
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds 	/* Architecture-specific hardware disable .. */
2251da177e4SLinus Torvalds 	ptrace_disable(child);
2267d941432SRoland McGrath 	clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds 	write_lock_irq(&tasklist_lock);
229d5f70c00SOleg Nesterov 	/* protect against de_thread()->release_task() */
2305ecfbae0SOleg Nesterov 	if (child->ptrace)
2315ecfbae0SOleg Nesterov 		__ptrace_detach(child, data);
2321da177e4SLinus Torvalds 	write_unlock_irq(&tasklist_lock);
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds 	return 0;
2351da177e4SLinus Torvalds }
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
2381da177e4SLinus Torvalds {
2391da177e4SLinus Torvalds 	int copied = 0;
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds 	while (len > 0) {
2421da177e4SLinus Torvalds 		char buf[128];
2431da177e4SLinus Torvalds 		int this_len, retval;
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
2461da177e4SLinus Torvalds 		retval = access_process_vm(tsk, src, buf, this_len, 0);
2471da177e4SLinus Torvalds 		if (!retval) {
2481da177e4SLinus Torvalds 			if (copied)
2491da177e4SLinus Torvalds 				break;
2501da177e4SLinus Torvalds 			return -EIO;
2511da177e4SLinus Torvalds 		}
2521da177e4SLinus Torvalds 		if (copy_to_user(dst, buf, retval))
2531da177e4SLinus Torvalds 			return -EFAULT;
2541da177e4SLinus Torvalds 		copied += retval;
2551da177e4SLinus Torvalds 		src += retval;
2561da177e4SLinus Torvalds 		dst += retval;
2571da177e4SLinus Torvalds 		len -= retval;
2581da177e4SLinus Torvalds 	}
2591da177e4SLinus Torvalds 	return copied;
2601da177e4SLinus Torvalds }
2611da177e4SLinus Torvalds 
2621da177e4SLinus Torvalds int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long 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 		if (copy_from_user(buf, src, this_len))
2721da177e4SLinus Torvalds 			return -EFAULT;
2731da177e4SLinus Torvalds 		retval = access_process_vm(tsk, dst, buf, this_len, 1);
2741da177e4SLinus Torvalds 		if (!retval) {
2751da177e4SLinus Torvalds 			if (copied)
2761da177e4SLinus Torvalds 				break;
2771da177e4SLinus Torvalds 			return -EIO;
2781da177e4SLinus Torvalds 		}
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 static int ptrace_setoptions(struct task_struct *child, long data)
2881da177e4SLinus Torvalds {
2891da177e4SLinus Torvalds 	child->ptrace &= ~PT_TRACE_MASK;
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACESYSGOOD)
2921da177e4SLinus Torvalds 		child->ptrace |= PT_TRACESYSGOOD;
2931da177e4SLinus Torvalds 
2941da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEFORK)
2951da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_FORK;
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORK)
2981da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK;
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACECLONE)
3011da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_CLONE;
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXEC)
3041da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXEC;
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORKDONE)
3071da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK_DONE;
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXIT)
3101da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXIT;
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds 	return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
3131da177e4SLinus Torvalds }
3141da177e4SLinus Torvalds 
315e16b2781SRoland McGrath static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info)
3161da177e4SLinus Torvalds {
3171da177e4SLinus Torvalds 	int error = -ESRCH;
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
3201da177e4SLinus Torvalds 	if (likely(child->sighand != NULL)) {
3211da177e4SLinus Torvalds 		error = -EINVAL;
3221da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
3231da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
324e16b2781SRoland McGrath 			*info = *child->last_siginfo;
3251da177e4SLinus Torvalds 			error = 0;
3261da177e4SLinus Torvalds 		}
3271da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
3281da177e4SLinus Torvalds 	}
3291da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
3301da177e4SLinus Torvalds 	return error;
3311da177e4SLinus Torvalds }
3321da177e4SLinus Torvalds 
333e16b2781SRoland McGrath static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info)
3341da177e4SLinus Torvalds {
3351da177e4SLinus Torvalds 	int error = -ESRCH;
3361da177e4SLinus Torvalds 
3371da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
3381da177e4SLinus Torvalds 	if (likely(child->sighand != NULL)) {
3391da177e4SLinus Torvalds 		error = -EINVAL;
3401da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
3411da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
342e16b2781SRoland McGrath 			*child->last_siginfo = *info;
3431da177e4SLinus Torvalds 			error = 0;
3441da177e4SLinus Torvalds 		}
3451da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
3461da177e4SLinus Torvalds 	}
3471da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
3481da177e4SLinus Torvalds 	return error;
3491da177e4SLinus Torvalds }
3501da177e4SLinus Torvalds 
35136df29d7SRoland McGrath 
35236df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
35336df29d7SRoland McGrath #define is_singlestep(request)		((request) == PTRACE_SINGLESTEP)
35436df29d7SRoland McGrath #else
35536df29d7SRoland McGrath #define is_singlestep(request)		0
35636df29d7SRoland McGrath #endif
35736df29d7SRoland McGrath 
3585b88abbfSRoland McGrath #ifdef PTRACE_SINGLEBLOCK
3595b88abbfSRoland McGrath #define is_singleblock(request)		((request) == PTRACE_SINGLEBLOCK)
3605b88abbfSRoland McGrath #else
3615b88abbfSRoland McGrath #define is_singleblock(request)		0
3625b88abbfSRoland McGrath #endif
3635b88abbfSRoland McGrath 
36436df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
36536df29d7SRoland McGrath #define is_sysemu_singlestep(request)	((request) == PTRACE_SYSEMU_SINGLESTEP)
36636df29d7SRoland McGrath #else
36736df29d7SRoland McGrath #define is_sysemu_singlestep(request)	0
36836df29d7SRoland McGrath #endif
36936df29d7SRoland McGrath 
37036df29d7SRoland McGrath static int ptrace_resume(struct task_struct *child, long request, long data)
37136df29d7SRoland McGrath {
37236df29d7SRoland McGrath 	if (!valid_signal(data))
37336df29d7SRoland McGrath 		return -EIO;
37436df29d7SRoland McGrath 
37536df29d7SRoland McGrath 	if (request == PTRACE_SYSCALL)
37636df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
37736df29d7SRoland McGrath 	else
37836df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
37936df29d7SRoland McGrath 
38036df29d7SRoland McGrath #ifdef TIF_SYSCALL_EMU
38136df29d7SRoland McGrath 	if (request == PTRACE_SYSEMU || request == PTRACE_SYSEMU_SINGLESTEP)
38236df29d7SRoland McGrath 		set_tsk_thread_flag(child, TIF_SYSCALL_EMU);
38336df29d7SRoland McGrath 	else
38436df29d7SRoland McGrath 		clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
38536df29d7SRoland McGrath #endif
38636df29d7SRoland McGrath 
3875b88abbfSRoland McGrath 	if (is_singleblock(request)) {
3885b88abbfSRoland McGrath 		if (unlikely(!arch_has_block_step()))
3895b88abbfSRoland McGrath 			return -EIO;
3905b88abbfSRoland McGrath 		user_enable_block_step(child);
3915b88abbfSRoland McGrath 	} else if (is_singlestep(request) || is_sysemu_singlestep(request)) {
39236df29d7SRoland McGrath 		if (unlikely(!arch_has_single_step()))
39336df29d7SRoland McGrath 			return -EIO;
39436df29d7SRoland McGrath 		user_enable_single_step(child);
39536df29d7SRoland McGrath 	}
39636df29d7SRoland McGrath 	else
39736df29d7SRoland McGrath 		user_disable_single_step(child);
39836df29d7SRoland McGrath 
39936df29d7SRoland McGrath 	child->exit_code = data;
40036df29d7SRoland McGrath 	wake_up_process(child);
40136df29d7SRoland McGrath 
40236df29d7SRoland McGrath 	return 0;
40336df29d7SRoland McGrath }
40436df29d7SRoland McGrath 
4051da177e4SLinus Torvalds int ptrace_request(struct task_struct *child, long request,
4061da177e4SLinus Torvalds 		   long addr, long data)
4071da177e4SLinus Torvalds {
4081da177e4SLinus Torvalds 	int ret = -EIO;
409e16b2781SRoland McGrath 	siginfo_t siginfo;
4101da177e4SLinus Torvalds 
4111da177e4SLinus Torvalds 	switch (request) {
41216c3e389SRoland McGrath 	case PTRACE_PEEKTEXT:
41316c3e389SRoland McGrath 	case PTRACE_PEEKDATA:
41416c3e389SRoland McGrath 		return generic_ptrace_peekdata(child, addr, data);
41516c3e389SRoland McGrath 	case PTRACE_POKETEXT:
41616c3e389SRoland McGrath 	case PTRACE_POKEDATA:
41716c3e389SRoland McGrath 		return generic_ptrace_pokedata(child, addr, data);
41816c3e389SRoland McGrath 
4191da177e4SLinus Torvalds #ifdef PTRACE_OLDSETOPTIONS
4201da177e4SLinus Torvalds 	case PTRACE_OLDSETOPTIONS:
4211da177e4SLinus Torvalds #endif
4221da177e4SLinus Torvalds 	case PTRACE_SETOPTIONS:
4231da177e4SLinus Torvalds 		ret = ptrace_setoptions(child, data);
4241da177e4SLinus Torvalds 		break;
4251da177e4SLinus Torvalds 	case PTRACE_GETEVENTMSG:
4261da177e4SLinus Torvalds 		ret = put_user(child->ptrace_message, (unsigned long __user *) data);
4271da177e4SLinus Torvalds 		break;
428e16b2781SRoland McGrath 
4291da177e4SLinus Torvalds 	case PTRACE_GETSIGINFO:
430e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
431e16b2781SRoland McGrath 		if (!ret)
432e16b2781SRoland McGrath 			ret = copy_siginfo_to_user((siginfo_t __user *) data,
433e16b2781SRoland McGrath 						   &siginfo);
4341da177e4SLinus Torvalds 		break;
435e16b2781SRoland McGrath 
4361da177e4SLinus Torvalds 	case PTRACE_SETSIGINFO:
437e16b2781SRoland McGrath 		if (copy_from_user(&siginfo, (siginfo_t __user *) data,
438e16b2781SRoland McGrath 				   sizeof siginfo))
439e16b2781SRoland McGrath 			ret = -EFAULT;
440e16b2781SRoland McGrath 		else
441e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
4421da177e4SLinus Torvalds 		break;
443e16b2781SRoland McGrath 
4441bcf5482SAlexey Dobriyan 	case PTRACE_DETACH:	 /* detach a process that was attached. */
4451bcf5482SAlexey Dobriyan 		ret = ptrace_detach(child, data);
4461bcf5482SAlexey Dobriyan 		break;
44736df29d7SRoland McGrath 
44836df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
44936df29d7SRoland McGrath 	case PTRACE_SINGLESTEP:
45036df29d7SRoland McGrath #endif
4515b88abbfSRoland McGrath #ifdef PTRACE_SINGLEBLOCK
4525b88abbfSRoland McGrath 	case PTRACE_SINGLEBLOCK:
4535b88abbfSRoland McGrath #endif
45436df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
45536df29d7SRoland McGrath 	case PTRACE_SYSEMU:
45636df29d7SRoland McGrath 	case PTRACE_SYSEMU_SINGLESTEP:
45736df29d7SRoland McGrath #endif
45836df29d7SRoland McGrath 	case PTRACE_SYSCALL:
45936df29d7SRoland McGrath 	case PTRACE_CONT:
46036df29d7SRoland McGrath 		return ptrace_resume(child, request, data);
46136df29d7SRoland McGrath 
46236df29d7SRoland McGrath 	case PTRACE_KILL:
46336df29d7SRoland McGrath 		if (child->exit_state)	/* already dead */
46436df29d7SRoland McGrath 			return 0;
46536df29d7SRoland McGrath 		return ptrace_resume(child, request, SIGKILL);
46636df29d7SRoland McGrath 
4671da177e4SLinus Torvalds 	default:
4681da177e4SLinus Torvalds 		break;
4691da177e4SLinus Torvalds 	}
4701da177e4SLinus Torvalds 
4711da177e4SLinus Torvalds 	return ret;
4721da177e4SLinus Torvalds }
473481bed45SChristoph Hellwig 
4746b9c7ed8SChristoph Hellwig /**
4756b9c7ed8SChristoph Hellwig  * ptrace_traceme  --  helper for PTRACE_TRACEME
4766b9c7ed8SChristoph Hellwig  *
4776b9c7ed8SChristoph Hellwig  * Performs checks and sets PT_PTRACED.
4786b9c7ed8SChristoph Hellwig  * Should be used by all ptrace implementations for PTRACE_TRACEME.
4796b9c7ed8SChristoph Hellwig  */
4806b9c7ed8SChristoph Hellwig int ptrace_traceme(void)
481481bed45SChristoph Hellwig {
482f5b40e36SLinus Torvalds 	int ret = -EPERM;
483481bed45SChristoph Hellwig 
484481bed45SChristoph Hellwig 	/*
485481bed45SChristoph Hellwig 	 * Are we already being traced?
486481bed45SChristoph Hellwig 	 */
487f470021aSRoland McGrath repeat:
488f5b40e36SLinus Torvalds 	task_lock(current);
489f5b40e36SLinus Torvalds 	if (!(current->ptrace & PT_PTRACED)) {
490f470021aSRoland McGrath 		/*
491f470021aSRoland McGrath 		 * See ptrace_attach() comments about the locking here.
492f470021aSRoland McGrath 		 */
493f470021aSRoland McGrath 		unsigned long flags;
494f470021aSRoland McGrath 		if (!write_trylock_irqsave(&tasklist_lock, flags)) {
495f470021aSRoland McGrath 			task_unlock(current);
496f470021aSRoland McGrath 			do {
497f470021aSRoland McGrath 				cpu_relax();
498f470021aSRoland McGrath 			} while (!write_can_lock(&tasklist_lock));
499f470021aSRoland McGrath 			goto repeat;
500f470021aSRoland McGrath 		}
501f470021aSRoland McGrath 
5025cd9c58fSDavid Howells 		ret = security_ptrace_traceme(current->parent);
503f470021aSRoland McGrath 
504481bed45SChristoph Hellwig 		/*
505481bed45SChristoph Hellwig 		 * Set the ptrace bit in the process ptrace flags.
506f470021aSRoland McGrath 		 * Then link us on our parent's ptraced list.
507481bed45SChristoph Hellwig 		 */
508f470021aSRoland McGrath 		if (!ret) {
509481bed45SChristoph Hellwig 			current->ptrace |= PT_PTRACED;
510f470021aSRoland McGrath 			__ptrace_link(current, current->real_parent);
511f470021aSRoland McGrath 		}
512f470021aSRoland McGrath 
513f470021aSRoland McGrath 		write_unlock_irqrestore(&tasklist_lock, flags);
514f5b40e36SLinus Torvalds 	}
515f5b40e36SLinus Torvalds 	task_unlock(current);
516f5b40e36SLinus Torvalds 	return ret;
517481bed45SChristoph Hellwig }
518481bed45SChristoph Hellwig 
5196b9c7ed8SChristoph Hellwig /**
5206b9c7ed8SChristoph Hellwig  * ptrace_get_task_struct  --  grab a task struct reference for ptrace
5216b9c7ed8SChristoph Hellwig  * @pid:       process id to grab a task_struct reference of
5226b9c7ed8SChristoph Hellwig  *
5236b9c7ed8SChristoph Hellwig  * This function is a helper for ptrace implementations.  It checks
5246b9c7ed8SChristoph Hellwig  * permissions and then grabs a task struct for use of the actual
5256b9c7ed8SChristoph Hellwig  * ptrace implementation.
5266b9c7ed8SChristoph Hellwig  *
5276b9c7ed8SChristoph Hellwig  * Returns the task_struct for @pid or an ERR_PTR() on failure.
5286b9c7ed8SChristoph Hellwig  */
5296b9c7ed8SChristoph Hellwig struct task_struct *ptrace_get_task_struct(pid_t pid)
5306b9c7ed8SChristoph Hellwig {
5316b9c7ed8SChristoph Hellwig 	struct task_struct *child;
5326b9c7ed8SChristoph Hellwig 
533481bed45SChristoph Hellwig 	read_lock(&tasklist_lock);
534228ebcbeSPavel Emelyanov 	child = find_task_by_vpid(pid);
535481bed45SChristoph Hellwig 	if (child)
536481bed45SChristoph Hellwig 		get_task_struct(child);
537f400e198SSukadev Bhattiprolu 
538481bed45SChristoph Hellwig 	read_unlock(&tasklist_lock);
539481bed45SChristoph Hellwig 	if (!child)
5406b9c7ed8SChristoph Hellwig 		return ERR_PTR(-ESRCH);
5416b9c7ed8SChristoph Hellwig 	return child;
542481bed45SChristoph Hellwig }
543481bed45SChristoph Hellwig 
5440ac15559SChristoph Hellwig #ifndef arch_ptrace_attach
5450ac15559SChristoph Hellwig #define arch_ptrace_attach(child)	do { } while (0)
5460ac15559SChristoph Hellwig #endif
5470ac15559SChristoph Hellwig 
548481bed45SChristoph Hellwig asmlinkage long sys_ptrace(long request, long pid, long addr, long data)
549481bed45SChristoph Hellwig {
550481bed45SChristoph Hellwig 	struct task_struct *child;
551481bed45SChristoph Hellwig 	long ret;
552481bed45SChristoph Hellwig 
553481bed45SChristoph Hellwig 	/*
554481bed45SChristoph Hellwig 	 * This lock_kernel fixes a subtle race with suid exec
555481bed45SChristoph Hellwig 	 */
556481bed45SChristoph Hellwig 	lock_kernel();
5576b9c7ed8SChristoph Hellwig 	if (request == PTRACE_TRACEME) {
5586b9c7ed8SChristoph Hellwig 		ret = ptrace_traceme();
5596ea6dd93SHaavard Skinnemoen 		if (!ret)
5606ea6dd93SHaavard Skinnemoen 			arch_ptrace_attach(current);
561481bed45SChristoph Hellwig 		goto out;
5626b9c7ed8SChristoph Hellwig 	}
5636b9c7ed8SChristoph Hellwig 
5646b9c7ed8SChristoph Hellwig 	child = ptrace_get_task_struct(pid);
5656b9c7ed8SChristoph Hellwig 	if (IS_ERR(child)) {
5666b9c7ed8SChristoph Hellwig 		ret = PTR_ERR(child);
5676b9c7ed8SChristoph Hellwig 		goto out;
5686b9c7ed8SChristoph Hellwig 	}
569481bed45SChristoph Hellwig 
570481bed45SChristoph Hellwig 	if (request == PTRACE_ATTACH) {
571481bed45SChristoph Hellwig 		ret = ptrace_attach(child);
5720ac15559SChristoph Hellwig 		/*
5730ac15559SChristoph Hellwig 		 * Some architectures need to do book-keeping after
5740ac15559SChristoph Hellwig 		 * a ptrace attach.
5750ac15559SChristoph Hellwig 		 */
5760ac15559SChristoph Hellwig 		if (!ret)
5770ac15559SChristoph Hellwig 			arch_ptrace_attach(child);
578005f18dfSChristoph Hellwig 		goto out_put_task_struct;
579481bed45SChristoph Hellwig 	}
580481bed45SChristoph Hellwig 
581481bed45SChristoph Hellwig 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
582481bed45SChristoph Hellwig 	if (ret < 0)
583481bed45SChristoph Hellwig 		goto out_put_task_struct;
584481bed45SChristoph Hellwig 
585481bed45SChristoph Hellwig 	ret = arch_ptrace(child, request, addr, data);
586481bed45SChristoph Hellwig 	if (ret < 0)
587481bed45SChristoph Hellwig 		goto out_put_task_struct;
588481bed45SChristoph Hellwig 
589481bed45SChristoph Hellwig  out_put_task_struct:
590481bed45SChristoph Hellwig 	put_task_struct(child);
591481bed45SChristoph Hellwig  out:
592481bed45SChristoph Hellwig 	unlock_kernel();
593481bed45SChristoph Hellwig 	return ret;
594481bed45SChristoph Hellwig }
59576647323SAlexey Dobriyan 
59676647323SAlexey Dobriyan int generic_ptrace_peekdata(struct task_struct *tsk, long addr, long data)
59776647323SAlexey Dobriyan {
59876647323SAlexey Dobriyan 	unsigned long tmp;
59976647323SAlexey Dobriyan 	int copied;
60076647323SAlexey Dobriyan 
60176647323SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
60276647323SAlexey Dobriyan 	if (copied != sizeof(tmp))
60376647323SAlexey Dobriyan 		return -EIO;
60476647323SAlexey Dobriyan 	return put_user(tmp, (unsigned long __user *)data);
60576647323SAlexey Dobriyan }
606f284ce72SAlexey Dobriyan 
607f284ce72SAlexey Dobriyan int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data)
608f284ce72SAlexey Dobriyan {
609f284ce72SAlexey Dobriyan 	int copied;
610f284ce72SAlexey Dobriyan 
611f284ce72SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
612f284ce72SAlexey Dobriyan 	return (copied == sizeof(data)) ? 0 : -EIO;
613f284ce72SAlexey Dobriyan }
614032d82d9SRoland McGrath 
61596b8936aSChristoph Hellwig #if defined CONFIG_COMPAT
616032d82d9SRoland McGrath #include <linux/compat.h>
617032d82d9SRoland McGrath 
618032d82d9SRoland McGrath int compat_ptrace_request(struct task_struct *child, compat_long_t request,
619032d82d9SRoland McGrath 			  compat_ulong_t addr, compat_ulong_t data)
620032d82d9SRoland McGrath {
621032d82d9SRoland McGrath 	compat_ulong_t __user *datap = compat_ptr(data);
622032d82d9SRoland McGrath 	compat_ulong_t word;
623e16b2781SRoland McGrath 	siginfo_t siginfo;
624032d82d9SRoland McGrath 	int ret;
625032d82d9SRoland McGrath 
626032d82d9SRoland McGrath 	switch (request) {
627032d82d9SRoland McGrath 	case PTRACE_PEEKTEXT:
628032d82d9SRoland McGrath 	case PTRACE_PEEKDATA:
629032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &word, sizeof(word), 0);
630032d82d9SRoland McGrath 		if (ret != sizeof(word))
631032d82d9SRoland McGrath 			ret = -EIO;
632032d82d9SRoland McGrath 		else
633032d82d9SRoland McGrath 			ret = put_user(word, datap);
634032d82d9SRoland McGrath 		break;
635032d82d9SRoland McGrath 
636032d82d9SRoland McGrath 	case PTRACE_POKETEXT:
637032d82d9SRoland McGrath 	case PTRACE_POKEDATA:
638032d82d9SRoland McGrath 		ret = access_process_vm(child, addr, &data, sizeof(data), 1);
639032d82d9SRoland McGrath 		ret = (ret != sizeof(data) ? -EIO : 0);
640032d82d9SRoland McGrath 		break;
641032d82d9SRoland McGrath 
642032d82d9SRoland McGrath 	case PTRACE_GETEVENTMSG:
643032d82d9SRoland McGrath 		ret = put_user((compat_ulong_t) child->ptrace_message, datap);
644032d82d9SRoland McGrath 		break;
645032d82d9SRoland McGrath 
646e16b2781SRoland McGrath 	case PTRACE_GETSIGINFO:
647e16b2781SRoland McGrath 		ret = ptrace_getsiginfo(child, &siginfo);
648e16b2781SRoland McGrath 		if (!ret)
649e16b2781SRoland McGrath 			ret = copy_siginfo_to_user32(
650e16b2781SRoland McGrath 				(struct compat_siginfo __user *) datap,
651e16b2781SRoland McGrath 				&siginfo);
652e16b2781SRoland McGrath 		break;
653e16b2781SRoland McGrath 
654e16b2781SRoland McGrath 	case PTRACE_SETSIGINFO:
655e16b2781SRoland McGrath 		memset(&siginfo, 0, sizeof siginfo);
656e16b2781SRoland McGrath 		if (copy_siginfo_from_user32(
657e16b2781SRoland McGrath 			    &siginfo, (struct compat_siginfo __user *) datap))
658e16b2781SRoland McGrath 			ret = -EFAULT;
659e16b2781SRoland McGrath 		else
660e16b2781SRoland McGrath 			ret = ptrace_setsiginfo(child, &siginfo);
661e16b2781SRoland McGrath 		break;
662e16b2781SRoland McGrath 
663032d82d9SRoland McGrath 	default:
664032d82d9SRoland McGrath 		ret = ptrace_request(child, request, addr, data);
665032d82d9SRoland McGrath 	}
666032d82d9SRoland McGrath 
667032d82d9SRoland McGrath 	return ret;
668032d82d9SRoland McGrath }
669c269f196SRoland McGrath 
670c269f196SRoland McGrath asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
671c269f196SRoland McGrath 				  compat_long_t addr, compat_long_t data)
672c269f196SRoland McGrath {
673c269f196SRoland McGrath 	struct task_struct *child;
674c269f196SRoland McGrath 	long ret;
675c269f196SRoland McGrath 
676c269f196SRoland McGrath 	/*
677c269f196SRoland McGrath 	 * This lock_kernel fixes a subtle race with suid exec
678c269f196SRoland McGrath 	 */
679c269f196SRoland McGrath 	lock_kernel();
680c269f196SRoland McGrath 	if (request == PTRACE_TRACEME) {
681c269f196SRoland McGrath 		ret = ptrace_traceme();
682c269f196SRoland McGrath 		goto out;
683c269f196SRoland McGrath 	}
684c269f196SRoland McGrath 
685c269f196SRoland McGrath 	child = ptrace_get_task_struct(pid);
686c269f196SRoland McGrath 	if (IS_ERR(child)) {
687c269f196SRoland McGrath 		ret = PTR_ERR(child);
688c269f196SRoland McGrath 		goto out;
689c269f196SRoland McGrath 	}
690c269f196SRoland McGrath 
691c269f196SRoland McGrath 	if (request == PTRACE_ATTACH) {
692c269f196SRoland McGrath 		ret = ptrace_attach(child);
693c269f196SRoland McGrath 		/*
694c269f196SRoland McGrath 		 * Some architectures need to do book-keeping after
695c269f196SRoland McGrath 		 * a ptrace attach.
696c269f196SRoland McGrath 		 */
697c269f196SRoland McGrath 		if (!ret)
698c269f196SRoland McGrath 			arch_ptrace_attach(child);
699c269f196SRoland McGrath 		goto out_put_task_struct;
700c269f196SRoland McGrath 	}
701c269f196SRoland McGrath 
702c269f196SRoland McGrath 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
703c269f196SRoland McGrath 	if (!ret)
704c269f196SRoland McGrath 		ret = compat_arch_ptrace(child, request, addr, data);
705c269f196SRoland McGrath 
706c269f196SRoland McGrath  out_put_task_struct:
707c269f196SRoland McGrath 	put_task_struct(child);
708c269f196SRoland McGrath  out:
709c269f196SRoland McGrath 	unlock_kernel();
710c269f196SRoland McGrath 	return ret;
711c269f196SRoland McGrath }
71296b8936aSChristoph Hellwig #endif	/* CONFIG_COMPAT */
713