📄 ptrace.c
字号:
/* * 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/module.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/mm.h>#include <linux/highmem.h>#include <linux/pagemap.h>#include <linux/smp_lock.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 <asm/pgtable.h>#include <asm/uaccess.h>/* * 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;} /* * Turn a tracing stop into a normal stop now, since with no tracer there * would be no way to wake it up with SIGCONT or SIGKILL. If there was a * signal sent that would resume the child, but didn't because it was in * TASK_TRACED, resume it now. * Requires that irqs be disabled. */void ptrace_untrace(struct task_struct *child){ spin_lock(&child->sighand->siglock); if (task_is_traced(child)) { if (child->signal->flags & SIGNAL_STOP_STOPPED) { __set_task_state(child, TASK_STOPPED); } else { signal_wake_up(child, 1); } } spin_unlock(&child->sighand->siglock);}/* * unptrace a task: move it back to its original parent and * remove it from the ptrace list. * * Must be called with the tasklist lock write-held. */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); if (task_is_traced(child)) ptrace_untrace(child);}/* * Check that we have indeed attached to the thing.. */int ptrace_check_attach(struct task_struct *child, int kill){ 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) { ret = 0; /* * child->sighand can't be NULL, release_task() * does ptrace_unlink() before __exit_signal(). */ spin_lock_irq(&child->sighand->siglock); if (task_is_stopped(child)) child->state = TASK_TRACED; else if (!task_is_traced(child) && !kill) ret = -ESRCH; spin_unlock_irq(&child->sighand->siglock); } read_unlock(&tasklist_lock); if (!ret && !kill) ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH; /* All systems go.. */ return ret;}int __ptrace_may_access(struct task_struct *task, unsigned int mode){ /* 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; if (((current->uid != task->euid) || (current->uid != task->suid) || (current->uid != task->uid) || (current->gid != task->egid) || (current->gid != task->sgid) || (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE)) return -EPERM; smp_rmb(); if (task->mm) dumpable = get_dumpable(task->mm); if (!dumpable && !capable(CAP_SYS_PTRACE)) return -EPERM; return security_ptrace_may_access(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 ? true : false);}int ptrace_attach(struct task_struct *task){ int retval; unsigned long flags; audit_ptrace(task); retval = -EPERM; if (same_thread_group(task, current)) goto out;repeat: /* * Nasty, nasty. * * We want to hold both the task-lock and the * tasklist_lock for writing at the same time. * But that's against the rules (tasklist_lock * is taken for reading by interrupts on other * cpu's that may have task_lock). */ task_lock(task); if (!write_trylock_irqsave(&tasklist_lock, flags)) { task_unlock(task); do { cpu_relax(); } while (!write_can_lock(&tasklist_lock)); goto repeat; } if (!task->mm) goto bad; /* the same process cannot be attached many times */ if (task->ptrace & PT_PTRACED) goto bad; retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH); if (retval) goto bad; /* Go */ task->ptrace |= PT_PTRACED; if (capable(CAP_SYS_PTRACE)) task->ptrace |= PT_PTRACE_CAP; __ptrace_link(task, current); send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);bad: write_unlock_irqrestore(&tasklist_lock, flags); task_unlock(task);out: return retval;}static inline void __ptrace_detach(struct task_struct *child, unsigned int data){ child->exit_code = data; /* .. re-parent .. */ __ptrace_unlink(child); /* .. and wake it up. */ if (child->exit_state != EXIT_ZOMBIE) wake_up_process(child);}int ptrace_detach(struct task_struct *child, unsigned int data){ 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); /* protect against de_thread()->release_task() */ if (child->ptrace) __ptrace_detach(child, data); write_unlock_irq(&tasklist_lock); return 0;}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){ int copied = 0; while (len > 0) { char buf[128]; int this_len, retval; this_len = (len > sizeof(buf)) ? sizeof(buf) : len; if (copy_from_user(buf, src, this_len)) return -EFAULT; retval = access_process_vm(tsk, dst, buf, this_len, 1); if (!retval) { if (copied) break; return -EIO; } copied += retval; src += retval; dst += retval; len -= retval; } return copied;}static int ptrace_setoptions(struct task_struct *child, long data){ child->ptrace &= ~PT_TRACE_MASK; if (data & PTRACE_O_TRACESYSGOOD) child->ptrace |= PT_TRACESYSGOOD; if (data & PTRACE_O_TRACEFORK) child->ptrace |= PT_TRACE_FORK; if (data & PTRACE_O_TRACEVFORK) child->ptrace |= PT_TRACE_VFORK; if (data & PTRACE_O_TRACECLONE) child->ptrace |= PT_TRACE_CLONE; if (data & PTRACE_O_TRACEEXEC) child->ptrace |= PT_TRACE_EXEC; if (data & PTRACE_O_TRACEVFORKDONE) child->ptrace |= PT_TRACE_VFORK_DONE; if (data & PTRACE_O_TRACEEXIT) child->ptrace |= PT_TRACE_EXIT; return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;}static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info){ int error = -ESRCH; read_lock(&tasklist_lock); if (likely(child->sighand != NULL)) { error = -EINVAL; spin_lock_irq(&child->sighand->siglock); if (likely(child->last_siginfo != NULL)) { *info = *child->last_siginfo; error = 0; } spin_unlock_irq(&child->sighand->siglock); } read_unlock(&tasklist_lock); return error;}static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info){ int error = -ESRCH; read_lock(&tasklist_lock); if (likely(child->sighand != NULL)) { error = -EINVAL; spin_lock_irq(&child->sighand->siglock); if (likely(child->last_siginfo != NULL)) { *child->last_siginfo = *info; error = 0; } spin_unlock_irq(&child->sighand->siglock); } read_unlock(&tasklist_lock); return error;}#ifdef PTRACE_SINGLESTEP#define is_singlestep(request) ((request) == PTRACE_SINGLESTEP)#else#define is_singlestep(request) 0#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -