1057 lines · 26.3 KB · C
RawDownload
/* * linux/kernel/ptrace.c * * (C) Copyright 1999 Linus Torvalds * * Common interfaces for "ptrace()" which we do not want * to continually duplicate across every architecture. */ #include <linux/capability.h>#include <linux/export.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/mm.h>#include <linux/highmem.h>#include <linux/pagemap.h>#include <linux/ptrace.h>#include <linux/security.h>#include <linux/signal.h>#include <linux/audit.h>#include <linux/pid_namespace.h>#include <linux/syscalls.h>#include <linux/uaccess.h>#include <linux/regset.h>#include <linux/hw_breakpoint.h>#include <linux/cn_proc.h>  static int ptrace_trapping_sleep_fn(void *flags){	schedule();	return 0;} /* * ptrace a task: make the debugger its new parent and * move it to the ptrace list. * * Must be called with the tasklist lock write-held. */void __ptrace_link(struct task_struct *child, struct task_struct *new_parent){	BUG_ON(!list_empty(&child->ptrace_entry));	list_add(&child->ptrace_entry, &new_parent->ptraced);	child->parent = new_parent;} /** * __ptrace_unlink - unlink ptracee and restore its execution state * @child: ptracee to be unlinked * * Remove @child from the ptrace list, move it back to the original parent, * and restore the execution state so that it conforms to the group stop * state. * * Unlinking can happen via two paths - explicit PTRACE_DETACH or ptracer * exiting.  For PTRACE_DETACH, unless the ptracee has been killed between * ptrace_check_attach() and here, it's guaranteed to be in TASK_TRACED. * If the ptracer is exiting, the ptracee can be in any state. * * After detach, the ptracee should be in a state which conforms to the * group stop.  If the group is stopped or in the process of stopping, the * ptracee should be put into TASK_STOPPED; otherwise, it should be woken * up from TASK_TRACED. * * If the ptracee is in TASK_TRACED and needs to be moved to TASK_STOPPED, * it goes through TRACED -> RUNNING -> STOPPED transition which is similar * to but in the opposite direction of what happens while attaching to a * stopped task.  However, in this direction, the intermediate RUNNING * state is not hidden even from the current ptracer and if it immediately * re-attaches and performs a WNOHANG wait(2), it may fail. * * CONTEXT: * write_lock_irq(tasklist_lock) */void __ptrace_unlink(struct task_struct *child){	BUG_ON(!child->ptrace); 	child->ptrace = 0;	child->parent = child->real_parent;	list_del_init(&child->ptrace_entry); 	spin_lock(&child->sighand->siglock); 	/*	 * Clear all pending traps and TRAPPING.  TRAPPING should be	 * cleared regardless of JOBCTL_STOP_PENDING.  Do it explicitly.	 */	task_clear_jobctl_pending(child, JOBCTL_TRAP_MASK);	task_clear_jobctl_trapping(child); 	/*	 * Reinstate JOBCTL_STOP_PENDING if group stop is in effect and	 * @child isn't dead.	 */	if (!(child->flags & PF_EXITING) &&	    (child->signal->flags & SIGNAL_STOP_STOPPED ||	     child->signal->group_stop_count)) {		child->jobctl |= JOBCTL_STOP_PENDING; 		/*		 * This is only possible if this thread was cloned by the		 * traced task running in the stopped group, set the signal		 * for the future reports.		 * FIXME: we should change ptrace_init_task() to handle this		 * case.		 */		if (!(child->jobctl & JOBCTL_STOP_SIGMASK))			child->jobctl |= SIGSTOP;	} 	/*	 * If transition to TASK_STOPPED is pending or in TASK_TRACED, kick	 * @child in the butt.  Note that @resume should be used iff @child	 * is in TASK_TRACED; otherwise, we might unduly disrupt	 * TASK_KILLABLE sleeps.	 */	if (child->jobctl & JOBCTL_STOP_PENDING || task_is_traced(child))		signal_wake_up(child, task_is_traced(child)); 	spin_unlock(&child->sighand->siglock);} /** * ptrace_check_attach - check whether ptracee is ready for ptrace operation * @child: ptracee to check for * @ignore_state: don't check whether @child is currently %TASK_TRACED * * Check whether @child is being ptraced by %current and ready for further * ptrace operations.  If @ignore_state is %false, @child also should be in * %TASK_TRACED state and on return the child is guaranteed to be traced * and not executing.  If @ignore_state is %true, @child can be in any * state. * * CONTEXT: * Grabs and releases tasklist_lock and @child->sighand->siglock. * * RETURNS: * 0 on success, -ESRCH if %child is not ready. */int ptrace_check_attach(struct task_struct *child, bool ignore_state){	int ret = -ESRCH; 	/*	 * We take the read lock around doing both checks to close a	 * possible race where someone else was tracing our child and	 * detached between these two checks.  After this locked check,	 * we are sure that this is our traced child and that can only	 * be changed by us so it's not changing right after this.	 */	read_lock(&tasklist_lock);	if ((child->ptrace & PT_PTRACED) && child->parent == current) {		/*		 * child->sighand can't be NULL, release_task()		 * does ptrace_unlink() before __exit_signal().		 */		spin_lock_irq(&child->sighand->siglock);		WARN_ON_ONCE(task_is_stopped(child));		if (ignore_state || (task_is_traced(child) &&				     !(child->jobctl & JOBCTL_LISTENING)))			ret = 0;		spin_unlock_irq(&child->sighand->siglock);	}	read_unlock(&tasklist_lock); 	if (!ret && !ignore_state)		ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH; 	/* All systems go.. */	return ret;} static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode){	if (mode & PTRACE_MODE_NOAUDIT)		return has_ns_capability_noaudit(current, ns, CAP_SYS_PTRACE);	else		return has_ns_capability(current, ns, CAP_SYS_PTRACE);} int __ptrace_may_access(struct task_struct *task, unsigned int mode){	const struct cred *cred = current_cred(), *tcred; 	/* May we inspect the given task?	 * This check is used both for attaching with ptrace	 * and for allowing access to sensitive information in /proc.	 *	 * ptrace_attach denies several cases that /proc allows	 * because setting up the necessary parent/child relationship	 * or halting the specified task is impossible.	 */	int dumpable = 0;	/* Don't let security modules deny introspection */	if (task == current)		return 0;	rcu_read_lock();	tcred = __task_cred(task);	if (cred->user->user_ns == tcred->user->user_ns &&	    (cred->uid == tcred->euid &&	     cred->uid == tcred->suid &&	     cred->uid == tcred->uid  &&	     cred->gid == tcred->egid &&	     cred->gid == tcred->sgid &&	     cred->gid == tcred->gid))		goto ok;	if (ptrace_has_cap(tcred->user->user_ns, mode))		goto ok;	rcu_read_unlock();	return -EPERM;ok:	rcu_read_unlock();	smp_rmb();	if (task->mm)		dumpable = get_dumpable(task->mm);	if (!dumpable  && !ptrace_has_cap(task_user_ns(task), mode))		return -EPERM; 	return security_ptrace_access_check(task, mode);} bool ptrace_may_access(struct task_struct *task, unsigned int mode){	int err;	task_lock(task);	err = __ptrace_may_access(task, mode);	task_unlock(task);	return !err;} static int ptrace_attach(struct task_struct *task, long request,			 unsigned long addr,			 unsigned long flags){	bool seize = (request == PTRACE_SEIZE);	int retval; 	retval = -EIO;	if (seize) {		if (addr != 0)			goto out;		if (flags & ~(unsigned long)PTRACE_O_MASK)			goto out;		flags = PT_PTRACED | PT_SEIZED | (flags << PT_OPT_FLAG_SHIFT);	} else {		flags = PT_PTRACED;	} 	audit_ptrace(task); 	retval = -EPERM;	if (unlikely(task->flags & PF_KTHREAD))		goto out;	if (same_thread_group(task, current))		goto out; 	/*	 * Protect exec's credential calculations against our interference;	 * SUID, SGID and LSM creds get determined differently	 * under ptrace.	 */	retval = -ERESTARTNOINTR;	if (mutex_lock_interruptible(&task->signal->cred_guard_mutex))		goto out; 	task_lock(task);	retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);	task_unlock(task);	if (retval)		goto unlock_creds; 	write_lock_irq(&tasklist_lock);	retval = -EPERM;	if (unlikely(task->exit_state))		goto unlock_tasklist;	if (task->ptrace)		goto unlock_tasklist; 	if (seize)		flags |= PT_SEIZED;	if (ns_capable(task_user_ns(task), CAP_SYS_PTRACE))		flags |= PT_PTRACE_CAP;	task->ptrace = flags; 	__ptrace_link(task, current); 	/* SEIZE doesn't trap tracee on attach */	if (!seize)		send_sig_info(SIGSTOP, SEND_SIG_FORCED, task); 	spin_lock(&task->sighand->siglock); 	/*	 * If the task is already STOPPED, set JOBCTL_TRAP_STOP and	 * TRAPPING, and kick it so that it transits to TRACED.  TRAPPING	 * will be cleared if the child completes the transition or any	 * event which clears the group stop states happens.  We'll wait	 * for the transition to complete before returning from this	 * function.	 *	 * This hides STOPPED -> RUNNING -> TRACED transition from the	 * attaching thread but a different thread in the same group can	 * still observe the transient RUNNING state.  IOW, if another	 * thread's WNOHANG wait(2) on the stopped tracee races against	 * ATTACH, the wait(2) may fail due to the transient RUNNING.	 *	 * The following task_is_stopped() test is safe as both transitions	 * in and out of STOPPED are protected by siglock.	 */	if (task_is_stopped(task) &&	    task_set_jobctl_pending(task, JOBCTL_TRAP_STOP | JOBCTL_TRAPPING))		signal_wake_up(task, 1); 	spin_unlock(&task->sighand->siglock); 	retval = 0;unlock_tasklist:	write_unlock_irq(&tasklist_lock);unlock_creds:	mutex_unlock(&task->signal->cred_guard_mutex);out:	if (!retval) {		wait_on_bit(&task->jobctl, JOBCTL_TRAPPING_BIT,			    ptrace_trapping_sleep_fn, TASK_UNINTERRUPTIBLE);		proc_ptrace_connector(task, PTRACE_ATTACH);	} 	return retval;} /** * ptrace_traceme  --  helper for PTRACE_TRACEME * * Performs checks and sets PT_PTRACED. * Should be used by all ptrace implementations for PTRACE_TRACEME. */static int ptrace_traceme(void){	int ret = -EPERM; 	write_lock_irq(&tasklist_lock);	/* Are we already being traced? */	if (!current->ptrace) {		ret = security_ptrace_traceme(current->parent);		/*		 * Check PF_EXITING to ensure ->real_parent has not passed		 * exit_ptrace(). Otherwise we don't report the error but		 * pretend ->real_parent untraces us right after return.		 */		if (!ret && !(current->real_parent->flags & PF_EXITING)) {			current->ptrace = PT_PTRACED;			__ptrace_link(current, current->real_parent);		}	}	write_unlock_irq(&tasklist_lock); 	return ret;} /* * Called with irqs disabled, returns true if childs should reap themselves. */static int ignoring_children(struct sighand_struct *sigh){	int ret;	spin_lock(&sigh->siglock);	ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) ||	      (sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT);	spin_unlock(&sigh->siglock);	return ret;} /* * Called with tasklist_lock held for writing. * Unlink a traced task, and clean it up if it was a traced zombie. * Return true if it needs to be reaped with release_task(). * (We can't call release_task() here because we already hold tasklist_lock.) * * If it's a zombie, our attachedness prevented normal parent notification * or self-reaping.  Do notification now if it would have happened earlier. * If it should reap itself, return true. * * If it's our own child, there is no notification to do. But if our normal * children self-reap, then this child was prevented by ptrace and we must * reap it now, in that case we must also wake up sub-threads sleeping in * do_wait(). */static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p){	bool dead; 	__ptrace_unlink(p); 	if (p->exit_state != EXIT_ZOMBIE)		return false; 	dead = !thread_group_leader(p); 	if (!dead && thread_group_empty(p)) {		if (!same_thread_group(p->real_parent, tracer))			dead = do_notify_parent(p, p->exit_signal);		else if (ignoring_children(tracer->sighand)) {			__wake_up_parent(p, tracer);			dead = true;		}	}	/* Mark it as in the process of being reaped. */	if (dead)		p->exit_state = EXIT_DEAD;	return dead;} static int ptrace_detach(struct task_struct *child, unsigned int data){	bool dead = false; 	if (!valid_signal(data))		return -EIO; 	/* Architecture-specific hardware disable .. */	ptrace_disable(child);	clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); 	write_lock_irq(&tasklist_lock);	/*	 * This child can be already killed. Make sure de_thread() or	 * our sub-thread doing do_wait() didn't do release_task() yet.	 */	if (child->ptrace) {		child->exit_code = data;		dead = __ptrace_detach(current, child);	}	write_unlock_irq(&tasklist_lock); 	proc_ptrace_connector(child, PTRACE_DETACH);	if (unlikely(dead))		release_task(child); 	return 0;} /* * Detach all tasks we were using ptrace on. Called with tasklist held * for writing, and returns with it held too. But note it can release * and reacquire the lock. */void exit_ptrace(struct task_struct *tracer)	__releases(&tasklist_lock)	__acquires(&tasklist_lock){	struct task_struct *p, *n;	LIST_HEAD(ptrace_dead); 	if (likely(list_empty(&tracer->ptraced)))		return; 	list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) {		if (__ptrace_detach(tracer, p))			list_add(&p->ptrace_entry, &ptrace_dead);	} 	write_unlock_irq(&tasklist_lock);	BUG_ON(!list_empty(&tracer->ptraced)); 	list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) {		list_del_init(&p->ptrace_entry);		release_task(p);	} 	write_lock_irq(&tasklist_lock);} int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len){	int copied = 0; 	while (len > 0) {		char buf[128];		int this_len, retval; 		this_len = (len > sizeof(buf)) ? sizeof(buf) : len;		retval = access_process_vm(tsk, src, buf, this_len, 0);		if (!retval) {			if (copied)				break;			return -EIO;		}		if (copy_to_user(dst, buf, retval))			return -EFAULT;		copied += retval;		src += retval;		dst += retval;		len -= retval;	}	return copied;} int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)

Lines 1–500 of 1057.