1323 lines · 33.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * 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/sched/mm.h>#include <linux/sched/coredump.h>#include <linux/sched/task.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/uio.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>#include <linux/compat.h>#include <linux/sched/signal.h> /* * Access another process' address space via ptrace. * Source/target buffer must be kernel space, * Do not walk the page table directly, use get_user_pages */int ptrace_access_vm(struct task_struct *tsk, unsigned long addr,		     void *buf, int len, unsigned int gup_flags){	struct mm_struct *mm;	int ret; 	mm = get_task_mm(tsk);	if (!mm)		return 0; 	if (!tsk->ptrace ||	    (current != tsk->parent) ||	    ((get_dumpable(mm) != SUID_DUMP_USER) &&	     !ptracer_capable(tsk, mm->user_ns))) {		mmput(mm);		return 0;	} 	ret = __access_remote_vm(tsk, mm, addr, buf, len, gup_flags);	mmput(mm); 	return ret;}  void __ptrace_link(struct task_struct *child, struct task_struct *new_parent,		   const struct cred *ptracer_cred){	BUG_ON(!list_empty(&child->ptrace_entry));	list_add(&child->ptrace_entry, &new_parent->ptraced);	child->parent = new_parent;	child->ptracer_cred = get_cred(ptracer_cred);} /* * 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. */static void ptrace_link(struct task_struct *child, struct task_struct *new_parent){	__ptrace_link(child, new_parent, current_cred());} /** * __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){	const struct cred *old_cred;	BUG_ON(!child->ptrace); 	clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); 	child->parent = child->real_parent;	list_del_init(&child->ptrace_entry);	old_cred = child->ptracer_cred;	child->ptracer_cred = NULL;	put_cred(old_cred); 	spin_lock(&child->sighand->siglock);	child->ptrace = 0;	/*	 * 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))		ptrace_signal_wake_up(child, true); 	spin_unlock(&child->sighand->siglock);} /* Ensure that nothing can wake it up, even SIGKILL */static bool ptrace_freeze_traced(struct task_struct *task){	bool ret = false; 	/* Lockless, nobody but us can set this flag */	if (task->jobctl & JOBCTL_LISTENING)		return ret; 	spin_lock_irq(&task->sighand->siglock);	if (task_is_traced(task) && !__fatal_signal_pending(task)) {		task->state = __TASK_TRACED;		ret = true;	}	spin_unlock_irq(&task->sighand->siglock); 	return ret;} static void ptrace_unfreeze_traced(struct task_struct *task){	if (task->state != __TASK_TRACED)		return; 	WARN_ON(!task->ptrace || task->parent != current); 	/*	 * PTRACE_LISTEN can allow ptrace_trap_notify to wake us up remotely.	 * Recheck state under the lock to close this race.	 */	spin_lock_irq(&task->sighand->siglock);	if (task->state == __TASK_TRACED) {		if (__fatal_signal_pending(task))			wake_up_state(task, __TASK_TRACED);		else			task->state = TASK_TRACED;	}	spin_unlock_irq(&task->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. */static 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 && child->parent == current) {		WARN_ON(child->state == __TASK_TRACED);		/*		 * child->sighand can't be NULL, release_task()		 * does ptrace_unlink() before __exit_signal().		 */		if (ignore_state || ptrace_freeze_traced(child))			ret = 0;	}	read_unlock(&tasklist_lock); 	if (!ret && !ignore_state) {		if (!wait_task_inactive(child, __TASK_TRACED)) {			/*			 * This can only happen if may_ptrace_stop() fails and			 * ptrace_stop() changes ->state back to TASK_RUNNING,			 * so we should not worry about leaking __TASK_TRACED.			 */			WARN_ON(child->state == __TASK_TRACED);			ret = -ESRCH;		}	} 	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);} /* Returns 0 on success, -errno on denial. */static int __ptrace_may_access(struct task_struct *task, unsigned int mode){	const struct cred *cred = current_cred(), *tcred;	struct mm_struct *mm;	kuid_t caller_uid;	kgid_t caller_gid; 	if (!(mode & PTRACE_MODE_FSCREDS) == !(mode & PTRACE_MODE_REALCREDS)) {		WARN(1, "denying ptrace access check without PTRACE_MODE_*CREDS\n");		return -EPERM;	} 	/* 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.	 */ 	/* Don't let security modules deny introspection */	if (same_thread_group(task, current))		return 0;	rcu_read_lock();	if (mode & PTRACE_MODE_FSCREDS) {		caller_uid = cred->fsuid;		caller_gid = cred->fsgid;	} else {		/*		 * Using the euid would make more sense here, but something		 * in userland might rely on the old behavior, and this		 * shouldn't be a security problem since		 * PTRACE_MODE_REALCREDS implies that the caller explicitly		 * used a syscall that requests access to another process		 * (and not a filesystem syscall to procfs).		 */		caller_uid = cred->uid;		caller_gid = cred->gid;	}	tcred = __task_cred(task);	if (uid_eq(caller_uid, tcred->euid) &&	    uid_eq(caller_uid, tcred->suid) &&	    uid_eq(caller_uid, tcred->uid)  &&	    gid_eq(caller_gid, tcred->egid) &&	    gid_eq(caller_gid, tcred->sgid) &&	    gid_eq(caller_gid, tcred->gid))		goto ok;	if (ptrace_has_cap(tcred->user_ns, mode))		goto ok;	rcu_read_unlock();	return -EPERM;ok:	rcu_read_unlock();	/*	 * If a task drops privileges and becomes nondumpable (through a syscall	 * like setresuid()) while we are trying to access it, we must ensure	 * that the dumpability is read after the credentials; otherwise,	 * we may be able to attach to a task that we shouldn't be able to	 * attach to (as if the task had dropped privileges without becoming	 * nondumpable).	 * Pairs with a write barrier in commit_creds().	 */	smp_rmb();	mm = task->mm;	if (mm &&	    ((get_dumpable(mm) != SUID_DUMP_USER) &&	     !ptrace_has_cap(mm->user_ns, 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_REALCREDS);	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;	task->ptrace = flags; 	ptrace_link(task, current); 	/* SEIZE doesn't trap tracee on attach */	if (!seize)		send_sig_info(SIGSTOP, SEND_SIG_PRIV, 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_state(task, __TASK_STOPPED); 	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) {		/*		 * We do not bother to change retval or clear JOBCTL_TRAPPING		 * if wait_on_bit() was interrupted by SIGKILL. The tracer will		 * not return to user-mode, it will exit and clear this bit in		 * __ptrace_unlink() if it wasn't already cleared by the tracee;		 * and until then nobody can ptrace this task.		 */		wait_on_bit(&task->jobctl, JOBCTL_TRAPPING_BIT, TASK_KILLABLE);		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.

Lines 1–500 of 1323.