xref: /openbmc/linux/kernel/ptrace.c (revision 36df29d7)
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>
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds #include <asm/pgtable.h>
251da177e4SLinus Torvalds #include <asm/uaccess.h>
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds /*
281da177e4SLinus Torvalds  * ptrace a task: make the debugger its new parent and
291da177e4SLinus Torvalds  * move it to the ptrace list.
301da177e4SLinus Torvalds  *
311da177e4SLinus Torvalds  * Must be called with the tasklist lock write-held.
321da177e4SLinus Torvalds  */
3336c8b586SIngo Molnar void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
341da177e4SLinus Torvalds {
35524223caSEric Sesterhenn 	BUG_ON(!list_empty(&child->ptrace_list));
361da177e4SLinus Torvalds 	if (child->parent == new_parent)
371da177e4SLinus Torvalds 		return;
381da177e4SLinus Torvalds 	list_add(&child->ptrace_list, &child->parent->ptrace_children);
399b678eceSOleg Nesterov 	remove_parent(child);
401da177e4SLinus Torvalds 	child->parent = new_parent;
419b678eceSOleg Nesterov 	add_parent(child);
421da177e4SLinus Torvalds }
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds /*
451da177e4SLinus Torvalds  * Turn a tracing stop into a normal stop now, since with no tracer there
461da177e4SLinus Torvalds  * would be no way to wake it up with SIGCONT or SIGKILL.  If there was a
471da177e4SLinus Torvalds  * signal sent that would resume the child, but didn't because it was in
481da177e4SLinus Torvalds  * TASK_TRACED, resume it now.
491da177e4SLinus Torvalds  * Requires that irqs be disabled.
501da177e4SLinus Torvalds  */
5136c8b586SIngo Molnar void ptrace_untrace(struct task_struct *child)
521da177e4SLinus Torvalds {
531da177e4SLinus Torvalds 	spin_lock(&child->sighand->siglock);
541da177e4SLinus Torvalds 	if (child->state == TASK_TRACED) {
551da177e4SLinus Torvalds 		if (child->signal->flags & SIGNAL_STOP_STOPPED) {
561da177e4SLinus Torvalds 			child->state = TASK_STOPPED;
571da177e4SLinus Torvalds 		} else {
581da177e4SLinus Torvalds 			signal_wake_up(child, 1);
591da177e4SLinus Torvalds 		}
601da177e4SLinus Torvalds 	}
611da177e4SLinus Torvalds 	spin_unlock(&child->sighand->siglock);
621da177e4SLinus Torvalds }
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds /*
651da177e4SLinus Torvalds  * unptrace a task: move it back to its original parent and
661da177e4SLinus Torvalds  * remove it from the ptrace list.
671da177e4SLinus Torvalds  *
681da177e4SLinus Torvalds  * Must be called with the tasklist lock write-held.
691da177e4SLinus Torvalds  */
7036c8b586SIngo Molnar void __ptrace_unlink(struct task_struct *child)
711da177e4SLinus Torvalds {
725ecfbae0SOleg Nesterov 	BUG_ON(!child->ptrace);
735ecfbae0SOleg Nesterov 
741da177e4SLinus Torvalds 	child->ptrace = 0;
751da177e4SLinus Torvalds 	if (!list_empty(&child->ptrace_list)) {
761da177e4SLinus Torvalds 		list_del_init(&child->ptrace_list);
779b678eceSOleg Nesterov 		remove_parent(child);
781da177e4SLinus Torvalds 		child->parent = child->real_parent;
799b678eceSOleg Nesterov 		add_parent(child);
801da177e4SLinus Torvalds 	}
811da177e4SLinus Torvalds 
82e57a5059SRoland McGrath 	if (child->state == TASK_TRACED)
831da177e4SLinus Torvalds 		ptrace_untrace(child);
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds /*
871da177e4SLinus Torvalds  * Check that we have indeed attached to the thing..
881da177e4SLinus Torvalds  */
891da177e4SLinus Torvalds int ptrace_check_attach(struct task_struct *child, int kill)
901da177e4SLinus Torvalds {
911da177e4SLinus Torvalds 	int ret = -ESRCH;
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds 	/*
941da177e4SLinus Torvalds 	 * We take the read lock around doing both checks to close a
951da177e4SLinus Torvalds 	 * possible race where someone else was tracing our child and
961da177e4SLinus Torvalds 	 * detached between these two checks.  After this locked check,
971da177e4SLinus Torvalds 	 * we are sure that this is our traced child and that can only
981da177e4SLinus Torvalds 	 * be changed by us so it's not changing right after this.
991da177e4SLinus Torvalds 	 */
1001da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
1011da177e4SLinus Torvalds 	if ((child->ptrace & PT_PTRACED) && child->parent == current &&
1021da177e4SLinus Torvalds 	    (!(child->ptrace & PT_ATTACHED) || child->real_parent != current)
1031da177e4SLinus Torvalds 	    && child->signal != NULL) {
1041da177e4SLinus Torvalds 		ret = 0;
1051da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
1061da177e4SLinus Torvalds 		if (child->state == TASK_STOPPED) {
1071da177e4SLinus Torvalds 			child->state = TASK_TRACED;
1081da177e4SLinus Torvalds 		} else if (child->state != TASK_TRACED && !kill) {
1091da177e4SLinus Torvalds 			ret = -ESRCH;
1101da177e4SLinus Torvalds 		}
1111da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
1121da177e4SLinus Torvalds 	}
1131da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds 	if (!ret && !kill) {
1161da177e4SLinus Torvalds 		wait_task_inactive(child);
1171da177e4SLinus Torvalds 	}
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 	/* All systems go.. */
1201da177e4SLinus Torvalds 	return ret;
1211da177e4SLinus Torvalds }
1221da177e4SLinus Torvalds 
123831830b5SAl Viro int __ptrace_may_attach(struct task_struct *task)
124ab8d11beSMiklos Szeredi {
125df26c40eSEric W. Biederman 	/* May we inspect the given task?
126df26c40eSEric W. Biederman 	 * This check is used both for attaching with ptrace
127df26c40eSEric W. Biederman 	 * and for allowing access to sensitive information in /proc.
128df26c40eSEric W. Biederman 	 *
129df26c40eSEric W. Biederman 	 * ptrace_attach denies several cases that /proc allows
130df26c40eSEric W. Biederman 	 * because setting up the necessary parent/child relationship
131df26c40eSEric W. Biederman 	 * or halting the specified task is impossible.
132df26c40eSEric W. Biederman 	 */
133df26c40eSEric W. Biederman 	int dumpable = 0;
134df26c40eSEric W. Biederman 	/* Don't let security modules deny introspection */
135df26c40eSEric W. Biederman 	if (task == current)
136df26c40eSEric W. Biederman 		return 0;
137ab8d11beSMiklos Szeredi 	if (((current->uid != task->euid) ||
138ab8d11beSMiklos Szeredi 	     (current->uid != task->suid) ||
139ab8d11beSMiklos Szeredi 	     (current->uid != task->uid) ||
140ab8d11beSMiklos Szeredi 	     (current->gid != task->egid) ||
141ab8d11beSMiklos Szeredi 	     (current->gid != task->sgid) ||
142ab8d11beSMiklos Szeredi 	     (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
143ab8d11beSMiklos Szeredi 		return -EPERM;
144ab8d11beSMiklos Szeredi 	smp_rmb();
145df26c40eSEric W. Biederman 	if (task->mm)
1466c5d5238SKawai, Hidehiro 		dumpable = get_dumpable(task->mm);
147df26c40eSEric W. Biederman 	if (!dumpable && !capable(CAP_SYS_PTRACE))
148ab8d11beSMiklos Szeredi 		return -EPERM;
149ab8d11beSMiklos Szeredi 
150ab8d11beSMiklos Szeredi 	return security_ptrace(current, task);
151ab8d11beSMiklos Szeredi }
152ab8d11beSMiklos Szeredi 
153ab8d11beSMiklos Szeredi int ptrace_may_attach(struct task_struct *task)
154ab8d11beSMiklos Szeredi {
155ab8d11beSMiklos Szeredi 	int err;
156ab8d11beSMiklos Szeredi 	task_lock(task);
157831830b5SAl Viro 	err = __ptrace_may_attach(task);
158ab8d11beSMiklos Szeredi 	task_unlock(task);
159ab8d11beSMiklos Szeredi 	return !err;
160ab8d11beSMiklos Szeredi }
161ab8d11beSMiklos Szeredi 
1621da177e4SLinus Torvalds int ptrace_attach(struct task_struct *task)
1631da177e4SLinus Torvalds {
1641da177e4SLinus Torvalds 	int retval;
1656175ecfeSSripathi Kodi 	unsigned long flags;
166f5b40e36SLinus Torvalds 
167a5cb013dSAl Viro 	audit_ptrace(task);
168a5cb013dSAl Viro 
1691da177e4SLinus Torvalds 	retval = -EPERM;
1701da177e4SLinus Torvalds 	if (task->pid <= 1)
171f5b40e36SLinus Torvalds 		goto out;
172bac0abd6SPavel Emelyanov 	if (same_thread_group(task, current))
173f5b40e36SLinus Torvalds 		goto out;
174f5b40e36SLinus Torvalds 
175f358166aSLinus Torvalds repeat:
176f358166aSLinus Torvalds 	/*
177f358166aSLinus Torvalds 	 * Nasty, nasty.
178f358166aSLinus Torvalds 	 *
179f358166aSLinus Torvalds 	 * We want to hold both the task-lock and the
180f358166aSLinus Torvalds 	 * tasklist_lock for writing at the same time.
181f358166aSLinus Torvalds 	 * But that's against the rules (tasklist_lock
182f358166aSLinus Torvalds 	 * is taken for reading by interrupts on other
183f358166aSLinus Torvalds 	 * cpu's that may have task_lock).
184f358166aSLinus Torvalds 	 */
185f5b40e36SLinus Torvalds 	task_lock(task);
1866175ecfeSSripathi Kodi 	if (!write_trylock_irqsave(&tasklist_lock, flags)) {
187f358166aSLinus Torvalds 		task_unlock(task);
188f358166aSLinus Torvalds 		do {
189f358166aSLinus Torvalds 			cpu_relax();
190f358166aSLinus Torvalds 		} while (!write_can_lock(&tasklist_lock));
191f358166aSLinus Torvalds 		goto repeat;
192f358166aSLinus Torvalds 	}
193f5b40e36SLinus Torvalds 
194df26c40eSEric W. Biederman 	if (!task->mm)
195df26c40eSEric W. Biederman 		goto bad;
1961da177e4SLinus Torvalds 	/* the same process cannot be attached many times */
1971da177e4SLinus Torvalds 	if (task->ptrace & PT_PTRACED)
1981da177e4SLinus Torvalds 		goto bad;
199b8c9a187SLinus Torvalds 	retval = __ptrace_may_attach(task);
2001da177e4SLinus Torvalds 	if (retval)
2011da177e4SLinus Torvalds 		goto bad;
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds 	/* Go */
2041da177e4SLinus Torvalds 	task->ptrace |= PT_PTRACED | ((task->real_parent != current)
2051da177e4SLinus Torvalds 				      ? PT_ATTACHED : 0);
2061da177e4SLinus Torvalds 	if (capable(CAP_SYS_PTRACE))
2071da177e4SLinus Torvalds 		task->ptrace |= PT_PTRACE_CAP;
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds 	__ptrace_link(task, current);
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds 	force_sig_specific(SIGSTOP, task);
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds bad:
2146175ecfeSSripathi Kodi 	write_unlock_irqrestore(&tasklist_lock, flags);
2151da177e4SLinus Torvalds 	task_unlock(task);
216f5b40e36SLinus Torvalds out:
2171da177e4SLinus Torvalds 	return retval;
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds 
220d5f70c00SOleg Nesterov static inline void __ptrace_detach(struct task_struct *child, unsigned int data)
2215ecfbae0SOleg Nesterov {
2225ecfbae0SOleg Nesterov 	child->exit_code = data;
2235ecfbae0SOleg Nesterov 	/* .. re-parent .. */
2245ecfbae0SOleg Nesterov 	__ptrace_unlink(child);
2255ecfbae0SOleg Nesterov 	/* .. and wake it up. */
2265ecfbae0SOleg Nesterov 	if (child->exit_state != EXIT_ZOMBIE)
2275ecfbae0SOleg Nesterov 		wake_up_process(child);
2285ecfbae0SOleg Nesterov }
2295ecfbae0SOleg Nesterov 
2301da177e4SLinus Torvalds int ptrace_detach(struct task_struct *child, unsigned int data)
2311da177e4SLinus Torvalds {
2327ed20e1aSJesper Juhl 	if (!valid_signal(data))
2331da177e4SLinus Torvalds 		return -EIO;
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds 	/* Architecture-specific hardware disable .. */
2361da177e4SLinus Torvalds 	ptrace_disable(child);
2377d941432SRoland McGrath 	clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
2381da177e4SLinus Torvalds 
2391da177e4SLinus Torvalds 	write_lock_irq(&tasklist_lock);
240d5f70c00SOleg Nesterov 	/* protect against de_thread()->release_task() */
2415ecfbae0SOleg Nesterov 	if (child->ptrace)
2425ecfbae0SOleg Nesterov 		__ptrace_detach(child, data);
2431da177e4SLinus Torvalds 	write_unlock_irq(&tasklist_lock);
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	return 0;
2461da177e4SLinus Torvalds }
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
2491da177e4SLinus Torvalds {
2501da177e4SLinus Torvalds 	int copied = 0;
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds 	while (len > 0) {
2531da177e4SLinus Torvalds 		char buf[128];
2541da177e4SLinus Torvalds 		int this_len, retval;
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
2571da177e4SLinus Torvalds 		retval = access_process_vm(tsk, src, buf, this_len, 0);
2581da177e4SLinus Torvalds 		if (!retval) {
2591da177e4SLinus Torvalds 			if (copied)
2601da177e4SLinus Torvalds 				break;
2611da177e4SLinus Torvalds 			return -EIO;
2621da177e4SLinus Torvalds 		}
2631da177e4SLinus Torvalds 		if (copy_to_user(dst, buf, retval))
2641da177e4SLinus Torvalds 			return -EFAULT;
2651da177e4SLinus Torvalds 		copied += retval;
2661da177e4SLinus Torvalds 		src += retval;
2671da177e4SLinus Torvalds 		dst += retval;
2681da177e4SLinus Torvalds 		len -= retval;
2691da177e4SLinus Torvalds 	}
2701da177e4SLinus Torvalds 	return copied;
2711da177e4SLinus Torvalds }
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
2741da177e4SLinus Torvalds {
2751da177e4SLinus Torvalds 	int copied = 0;
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds 	while (len > 0) {
2781da177e4SLinus Torvalds 		char buf[128];
2791da177e4SLinus Torvalds 		int this_len, retval;
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
2821da177e4SLinus Torvalds 		if (copy_from_user(buf, src, this_len))
2831da177e4SLinus Torvalds 			return -EFAULT;
2841da177e4SLinus Torvalds 		retval = access_process_vm(tsk, dst, buf, this_len, 1);
2851da177e4SLinus Torvalds 		if (!retval) {
2861da177e4SLinus Torvalds 			if (copied)
2871da177e4SLinus Torvalds 				break;
2881da177e4SLinus Torvalds 			return -EIO;
2891da177e4SLinus Torvalds 		}
2901da177e4SLinus Torvalds 		copied += retval;
2911da177e4SLinus Torvalds 		src += retval;
2921da177e4SLinus Torvalds 		dst += retval;
2931da177e4SLinus Torvalds 		len -= retval;
2941da177e4SLinus Torvalds 	}
2951da177e4SLinus Torvalds 	return copied;
2961da177e4SLinus Torvalds }
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds static int ptrace_setoptions(struct task_struct *child, long data)
2991da177e4SLinus Torvalds {
3001da177e4SLinus Torvalds 	child->ptrace &= ~PT_TRACE_MASK;
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACESYSGOOD)
3031da177e4SLinus Torvalds 		child->ptrace |= PT_TRACESYSGOOD;
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEFORK)
3061da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_FORK;
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORK)
3091da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK;
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACECLONE)
3121da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_CLONE;
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXEC)
3151da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXEC;
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEVFORKDONE)
3181da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_VFORK_DONE;
3191da177e4SLinus Torvalds 
3201da177e4SLinus Torvalds 	if (data & PTRACE_O_TRACEEXIT)
3211da177e4SLinus Torvalds 		child->ptrace |= PT_TRACE_EXIT;
3221da177e4SLinus Torvalds 
3231da177e4SLinus Torvalds 	return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
3241da177e4SLinus Torvalds }
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds static int ptrace_getsiginfo(struct task_struct *child, siginfo_t __user * data)
3271da177e4SLinus Torvalds {
3281da177e4SLinus Torvalds 	siginfo_t lastinfo;
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)) {
3361da177e4SLinus Torvalds 			lastinfo = *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 	if (!error)
3431da177e4SLinus Torvalds 		return copy_siginfo_to_user(data, &lastinfo);
3441da177e4SLinus Torvalds 	return error;
3451da177e4SLinus Torvalds }
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds static int ptrace_setsiginfo(struct task_struct *child, siginfo_t __user * data)
3481da177e4SLinus Torvalds {
3491da177e4SLinus Torvalds 	siginfo_t newinfo;
3501da177e4SLinus Torvalds 	int error = -ESRCH;
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds 	if (copy_from_user(&newinfo, data, sizeof (siginfo_t)))
3531da177e4SLinus Torvalds 		return -EFAULT;
3541da177e4SLinus Torvalds 
3551da177e4SLinus Torvalds 	read_lock(&tasklist_lock);
3561da177e4SLinus Torvalds 	if (likely(child->sighand != NULL)) {
3571da177e4SLinus Torvalds 		error = -EINVAL;
3581da177e4SLinus Torvalds 		spin_lock_irq(&child->sighand->siglock);
3591da177e4SLinus Torvalds 		if (likely(child->last_siginfo != NULL)) {
3601da177e4SLinus Torvalds 			*child->last_siginfo = newinfo;
3611da177e4SLinus Torvalds 			error = 0;
3621da177e4SLinus Torvalds 		}
3631da177e4SLinus Torvalds 		spin_unlock_irq(&child->sighand->siglock);
3641da177e4SLinus Torvalds 	}
3651da177e4SLinus Torvalds 	read_unlock(&tasklist_lock);
3661da177e4SLinus Torvalds 	return error;
3671da177e4SLinus Torvalds }
3681da177e4SLinus Torvalds 
36936df29d7SRoland McGrath 
37036df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
37136df29d7SRoland McGrath #define is_singlestep(request)		((request) == PTRACE_SINGLESTEP)
37236df29d7SRoland McGrath #else
37336df29d7SRoland McGrath #define is_singlestep(request)		0
37436df29d7SRoland McGrath #endif
37536df29d7SRoland 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 
39936df29d7SRoland McGrath 	if (is_singlestep(request) || is_sysemu_singlestep(request)) {
40036df29d7SRoland McGrath 		if (unlikely(!arch_has_single_step()))
40136df29d7SRoland McGrath 			return -EIO;
40236df29d7SRoland McGrath 		user_enable_single_step(child);
40336df29d7SRoland McGrath 	}
40436df29d7SRoland McGrath 	else
40536df29d7SRoland McGrath 		user_disable_single_step(child);
40636df29d7SRoland McGrath 
40736df29d7SRoland McGrath 	child->exit_code = data;
40836df29d7SRoland McGrath 	wake_up_process(child);
40936df29d7SRoland McGrath 
41036df29d7SRoland McGrath 	return 0;
41136df29d7SRoland McGrath }
41236df29d7SRoland McGrath 
4131da177e4SLinus Torvalds int ptrace_request(struct task_struct *child, long request,
4141da177e4SLinus Torvalds 		   long addr, long data)
4151da177e4SLinus Torvalds {
4161da177e4SLinus Torvalds 	int ret = -EIO;
4171da177e4SLinus Torvalds 
4181da177e4SLinus Torvalds 	switch (request) {
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;
4281da177e4SLinus Torvalds 	case PTRACE_GETSIGINFO:
4291da177e4SLinus Torvalds 		ret = ptrace_getsiginfo(child, (siginfo_t __user *) data);
4301da177e4SLinus Torvalds 		break;
4311da177e4SLinus Torvalds 	case PTRACE_SETSIGINFO:
4321da177e4SLinus Torvalds 		ret = ptrace_setsiginfo(child, (siginfo_t __user *) data);
4331da177e4SLinus Torvalds 		break;
4341bcf5482SAlexey Dobriyan 	case PTRACE_DETACH:	 /* detach a process that was attached. */
4351bcf5482SAlexey Dobriyan 		ret = ptrace_detach(child, data);
4361bcf5482SAlexey Dobriyan 		break;
43736df29d7SRoland McGrath 
43836df29d7SRoland McGrath #ifdef PTRACE_SINGLESTEP
43936df29d7SRoland McGrath 	case PTRACE_SINGLESTEP:
44036df29d7SRoland McGrath #endif
44136df29d7SRoland McGrath #ifdef PTRACE_SYSEMU
44236df29d7SRoland McGrath 	case PTRACE_SYSEMU:
44336df29d7SRoland McGrath 	case PTRACE_SYSEMU_SINGLESTEP:
44436df29d7SRoland McGrath #endif
44536df29d7SRoland McGrath 	case PTRACE_SYSCALL:
44636df29d7SRoland McGrath 	case PTRACE_CONT:
44736df29d7SRoland McGrath 		return ptrace_resume(child, request, data);
44836df29d7SRoland McGrath 
44936df29d7SRoland McGrath 	case PTRACE_KILL:
45036df29d7SRoland McGrath 		if (child->exit_state)	/* already dead */
45136df29d7SRoland McGrath 			return 0;
45236df29d7SRoland McGrath 		return ptrace_resume(child, request, SIGKILL);
45336df29d7SRoland McGrath 
4541da177e4SLinus Torvalds 	default:
4551da177e4SLinus Torvalds 		break;
4561da177e4SLinus Torvalds 	}
4571da177e4SLinus Torvalds 
4581da177e4SLinus Torvalds 	return ret;
4591da177e4SLinus Torvalds }
460481bed45SChristoph Hellwig 
4616b9c7ed8SChristoph Hellwig /**
4626b9c7ed8SChristoph Hellwig  * ptrace_traceme  --  helper for PTRACE_TRACEME
4636b9c7ed8SChristoph Hellwig  *
4646b9c7ed8SChristoph Hellwig  * Performs checks and sets PT_PTRACED.
4656b9c7ed8SChristoph Hellwig  * Should be used by all ptrace implementations for PTRACE_TRACEME.
4666b9c7ed8SChristoph Hellwig  */
4676b9c7ed8SChristoph Hellwig int ptrace_traceme(void)
468481bed45SChristoph Hellwig {
469f5b40e36SLinus Torvalds 	int ret = -EPERM;
470481bed45SChristoph Hellwig 
471481bed45SChristoph Hellwig 	/*
472481bed45SChristoph Hellwig 	 * Are we already being traced?
473481bed45SChristoph Hellwig 	 */
474f5b40e36SLinus Torvalds 	task_lock(current);
475f5b40e36SLinus Torvalds 	if (!(current->ptrace & PT_PTRACED)) {
476481bed45SChristoph Hellwig 		ret = security_ptrace(current->parent, current);
477481bed45SChristoph Hellwig 		/*
478481bed45SChristoph Hellwig 		 * Set the ptrace bit in the process ptrace flags.
479481bed45SChristoph Hellwig 		 */
480f5b40e36SLinus Torvalds 		if (!ret)
481481bed45SChristoph Hellwig 			current->ptrace |= PT_PTRACED;
482f5b40e36SLinus Torvalds 	}
483f5b40e36SLinus Torvalds 	task_unlock(current);
484f5b40e36SLinus Torvalds 	return ret;
485481bed45SChristoph Hellwig }
486481bed45SChristoph Hellwig 
4876b9c7ed8SChristoph Hellwig /**
4886b9c7ed8SChristoph Hellwig  * ptrace_get_task_struct  --  grab a task struct reference for ptrace
4896b9c7ed8SChristoph Hellwig  * @pid:       process id to grab a task_struct reference of
4906b9c7ed8SChristoph Hellwig  *
4916b9c7ed8SChristoph Hellwig  * This function is a helper for ptrace implementations.  It checks
4926b9c7ed8SChristoph Hellwig  * permissions and then grabs a task struct for use of the actual
4936b9c7ed8SChristoph Hellwig  * ptrace implementation.
4946b9c7ed8SChristoph Hellwig  *
4956b9c7ed8SChristoph Hellwig  * Returns the task_struct for @pid or an ERR_PTR() on failure.
4966b9c7ed8SChristoph Hellwig  */
4976b9c7ed8SChristoph Hellwig struct task_struct *ptrace_get_task_struct(pid_t pid)
4986b9c7ed8SChristoph Hellwig {
4996b9c7ed8SChristoph Hellwig 	struct task_struct *child;
5006b9c7ed8SChristoph Hellwig 
501481bed45SChristoph Hellwig 	/*
5026b9c7ed8SChristoph Hellwig 	 * Tracing init is not allowed.
503481bed45SChristoph Hellwig 	 */
504481bed45SChristoph Hellwig 	if (pid == 1)
5056b9c7ed8SChristoph Hellwig 		return ERR_PTR(-EPERM);
506481bed45SChristoph Hellwig 
507481bed45SChristoph Hellwig 	read_lock(&tasklist_lock);
508228ebcbeSPavel Emelyanov 	child = find_task_by_vpid(pid);
509481bed45SChristoph Hellwig 	if (child)
510481bed45SChristoph Hellwig 		get_task_struct(child);
511f400e198SSukadev Bhattiprolu 
512481bed45SChristoph Hellwig 	read_unlock(&tasklist_lock);
513481bed45SChristoph Hellwig 	if (!child)
5146b9c7ed8SChristoph Hellwig 		return ERR_PTR(-ESRCH);
5156b9c7ed8SChristoph Hellwig 	return child;
516481bed45SChristoph Hellwig }
517481bed45SChristoph Hellwig 
5180ac15559SChristoph Hellwig #ifndef arch_ptrace_attach
5190ac15559SChristoph Hellwig #define arch_ptrace_attach(child)	do { } while (0)
5200ac15559SChristoph Hellwig #endif
5210ac15559SChristoph Hellwig 
5226b9c7ed8SChristoph Hellwig #ifndef __ARCH_SYS_PTRACE
523481bed45SChristoph Hellwig asmlinkage long sys_ptrace(long request, long pid, long addr, long data)
524481bed45SChristoph Hellwig {
525481bed45SChristoph Hellwig 	struct task_struct *child;
526481bed45SChristoph Hellwig 	long ret;
527481bed45SChristoph Hellwig 
528481bed45SChristoph Hellwig 	/*
529481bed45SChristoph Hellwig 	 * This lock_kernel fixes a subtle race with suid exec
530481bed45SChristoph Hellwig 	 */
531481bed45SChristoph Hellwig 	lock_kernel();
5326b9c7ed8SChristoph Hellwig 	if (request == PTRACE_TRACEME) {
5336b9c7ed8SChristoph Hellwig 		ret = ptrace_traceme();
5346ea6dd93SHaavard Skinnemoen 		if (!ret)
5356ea6dd93SHaavard Skinnemoen 			arch_ptrace_attach(current);
536481bed45SChristoph Hellwig 		goto out;
5376b9c7ed8SChristoph Hellwig 	}
5386b9c7ed8SChristoph Hellwig 
5396b9c7ed8SChristoph Hellwig 	child = ptrace_get_task_struct(pid);
5406b9c7ed8SChristoph Hellwig 	if (IS_ERR(child)) {
5416b9c7ed8SChristoph Hellwig 		ret = PTR_ERR(child);
5426b9c7ed8SChristoph Hellwig 		goto out;
5436b9c7ed8SChristoph Hellwig 	}
544481bed45SChristoph Hellwig 
545481bed45SChristoph Hellwig 	if (request == PTRACE_ATTACH) {
546481bed45SChristoph Hellwig 		ret = ptrace_attach(child);
5470ac15559SChristoph Hellwig 		/*
5480ac15559SChristoph Hellwig 		 * Some architectures need to do book-keeping after
5490ac15559SChristoph Hellwig 		 * a ptrace attach.
5500ac15559SChristoph Hellwig 		 */
5510ac15559SChristoph Hellwig 		if (!ret)
5520ac15559SChristoph Hellwig 			arch_ptrace_attach(child);
553005f18dfSChristoph Hellwig 		goto out_put_task_struct;
554481bed45SChristoph Hellwig 	}
555481bed45SChristoph Hellwig 
556481bed45SChristoph Hellwig 	ret = ptrace_check_attach(child, request == PTRACE_KILL);
557481bed45SChristoph Hellwig 	if (ret < 0)
558481bed45SChristoph Hellwig 		goto out_put_task_struct;
559481bed45SChristoph Hellwig 
560481bed45SChristoph Hellwig 	ret = arch_ptrace(child, request, addr, data);
561481bed45SChristoph Hellwig 	if (ret < 0)
562481bed45SChristoph Hellwig 		goto out_put_task_struct;
563481bed45SChristoph Hellwig 
564481bed45SChristoph Hellwig  out_put_task_struct:
565481bed45SChristoph Hellwig 	put_task_struct(child);
566481bed45SChristoph Hellwig  out:
567481bed45SChristoph Hellwig 	unlock_kernel();
568481bed45SChristoph Hellwig 	return ret;
569481bed45SChristoph Hellwig }
570481bed45SChristoph Hellwig #endif /* __ARCH_SYS_PTRACE */
57176647323SAlexey Dobriyan 
57276647323SAlexey Dobriyan int generic_ptrace_peekdata(struct task_struct *tsk, long addr, long data)
57376647323SAlexey Dobriyan {
57476647323SAlexey Dobriyan 	unsigned long tmp;
57576647323SAlexey Dobriyan 	int copied;
57676647323SAlexey Dobriyan 
57776647323SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
57876647323SAlexey Dobriyan 	if (copied != sizeof(tmp))
57976647323SAlexey Dobriyan 		return -EIO;
58076647323SAlexey Dobriyan 	return put_user(tmp, (unsigned long __user *)data);
58176647323SAlexey Dobriyan }
582f284ce72SAlexey Dobriyan 
583f284ce72SAlexey Dobriyan int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data)
584f284ce72SAlexey Dobriyan {
585f284ce72SAlexey Dobriyan 	int copied;
586f284ce72SAlexey Dobriyan 
587f284ce72SAlexey Dobriyan 	copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
588f284ce72SAlexey Dobriyan 	return (copied == sizeof(data)) ? 0 : -EIO;
589f284ce72SAlexey Dobriyan }
590