SYSCALL_DEFINE4(ptrace, long, request, long, pid, long, addr, long, data) { struct task_struct *child; long ret; /* * This lock_kernel fixes a subtle race with suid exec */ lock_kernel(); if (request == PTRACE_TRACEME) { ret = ptrace_traceme(); if (!ret) arch_ptrace_attach(current); goto out; } child = ptrace_get_task_struct(pid); if (IS_ERR(child)) { ret = PTR_ERR(child); goto out; } if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); /* * Some architectures need to do book-keeping after * a ptrace attach. */ if (!ret) arch_ptrace_attach(child); goto out_put_task_struct; } ret = ptrace_check_attach(child, request == PTRACE_KILL); if (ret < 0) goto out_put_task_struct; if (gr_handle_ptrace(child, request)) { ret = -EPERM; goto out_put_task_struct; } ret = arch_ptrace(child, request, addr, data); if (ret < 0) goto out_put_task_struct; out_put_task_struct: put_task_struct(child); out: unlock_kernel(); return ret; }
asmlinkage long sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; long ret; /* * This lock_kernel fixes a subtle race with suid exec */ lock_kernel(); if (request == PTRACE_TRACEME) { ret = ptrace_traceme(); goto out; } child = ptrace_get_task_struct(pid); if (IS_ERR(child)) { ret = PTR_ERR(child); goto out; } if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); /* * Some architectures need to do book-keeping after * a ptrace attach. */ if (!ret) arch_ptrace_attach(child); goto out_put_task_struct; } ret = ptrace_check_attach(child, request == PTRACE_KILL); if (ret < 0) goto out_put_task_struct; ret = arch_ptrace(child, request, addr, data); if (ret < 0) goto out_put_task_struct; out_put_task_struct: put_task_struct(child); out: unlock_kernel(); return ret; }
SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, unsigned long, data) { struct task_struct *child; long ret; #ifdef CONFIG_CCSECURITY if (ccs_ptrace_permission(request, pid)) return -EPERM; #endif if (request == PTRACE_TRACEME) { ret = ptrace_traceme(); if (!ret) arch_ptrace_attach(current); goto out; } child = ptrace_get_task_struct(pid); if (IS_ERR(child)) { ret = PTR_ERR(child); goto out; } if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); /* * Some architectures need to do book-keeping after * a ptrace attach. */ if (!ret) arch_ptrace_attach(child); goto out_put_task_struct; } ret = ptrace_check_attach(child, request == PTRACE_KILL); if (ret < 0) goto out_put_task_struct; ret = arch_ptrace(child, request, addr, data); out_put_task_struct: put_task_struct(child); out: return ret; }
SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, unsigned long, data) { struct task_struct *child; long ret; if (request == PTRACE_TRACEME) { ret = ptrace_traceme(); if (!ret) arch_ptrace_attach(current); goto out; } child = ptrace_get_task_struct(pid); if (IS_ERR(child)) { ret = PTR_ERR(child); goto out; } if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); /* * Some architectures need to do book-keeping after * a ptrace attach. */ if (!ret) arch_ptrace_attach(child); goto out_put_task_struct; } ret = ptrace_check_attach(child, request == PTRACE_KILL); if (ret < 0) goto out_put_task_struct; ret = arch_ptrace(child, request, addr, data); if (ret || request != PTRACE_DETACH) ptrace_unfreeze_traced(child); out_put_task_struct: put_task_struct(child); out: return ret; }
asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid, compat_long_t addr, compat_long_t data) { struct task_struct *child; long ret; #ifdef CONFIG_CCSECURITY if (ccs_ptrace_permission(request, pid)) return -EPERM; #endif if (request == PTRACE_TRACEME) { ret = ptrace_traceme(); goto out; } child = ptrace_get_task_struct(pid); if (IS_ERR(child)) { ret = PTR_ERR(child); goto out; } if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); /* * Some architectures need to do book-keeping after * a ptrace attach. */ if (!ret) arch_ptrace_attach(child); goto out_put_task_struct; } ret = ptrace_check_attach(child, request == PTRACE_KILL); if (!ret) ret = compat_arch_ptrace(child, request, addr, data); out_put_task_struct: put_task_struct(child); out: return ret; }
asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid, compat_long_t addr, compat_long_t data) { struct task_struct *child; long ret; if (request == PTRACE_TRACEME) { ret = ptrace_traceme(); goto out; } child = ptrace_get_task_struct(pid); if (IS_ERR(child)) { ret = PTR_ERR(child); goto out; } if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); /* * Some architectures need to do book-keeping after * a ptrace attach. */ if (!ret) arch_ptrace_attach(child); goto out_put_task_struct; } ret = ptrace_check_attach(child, request == PTRACE_KILL); if (!ret) { ret = compat_arch_ptrace(child, request, addr, data); if (ret || request != PTRACE_DETACH) ptrace_unfreeze_traced(child); } out_put_task_struct: put_task_struct(child); out: return ret; }
static int ptrace_start(long pid, long request, struct task_struct **childp, struct utrace_attached_engine **enginep, struct ptrace_state **statep) { struct task_struct *child; struct utrace_attached_engine *engine; struct ptrace_state *state; int ret; NO_LOCKS; if (request == PTRACE_TRACEME) return ptrace_traceme(); ret = -ESRCH; read_lock(&tasklist_lock); child = find_task_by_pid(pid); if (child) get_task_struct(child); read_unlock(&tasklist_lock); pr_debug("ptrace pid %ld => %p\n", pid, child); if (!child) goto out; ret = -EPERM; if (pid == 1) /* you may not mess with init */ goto out_tsk; if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); goto out_tsk; } rcu_read_lock(); engine = utrace_attach(child, UTRACE_ATTACH_MATCH_OPS, &ptrace_utrace_ops, NULL); ret = -ESRCH; if (IS_ERR(engine) || engine == NULL) goto out_tsk_rcu; state = rcu_dereference(engine->data); if (state == NULL || state->parent != current) goto out_tsk_rcu; /* * Traditional ptrace behavior demands that the target already be * quiescent, but not dead. */ if (request != PTRACE_KILL && !(engine->flags & UTRACE_ACTION_QUIESCE)) { /* * If it's in job control stop, turn it into proper quiescence. */ struct sighand_struct *sighand; unsigned long flags; sighand = lock_task_sighand(child, &flags); if (likely(sighand != NULL)) { if (child->state == TASK_STOPPED) ret = 0; unlock_task_sighand(child, &flags); } if (ret == 0) { ret = ptrace_update(child, state, UTRACE_ACTION_QUIESCE, 0); if (unlikely(ret == -EALREADY)) ret = -ESRCH; if (unlikely(ret)) BUG_ON(ret != -ESRCH); } if (ret) { pr_debug("%d not stopped (%lu)\n", child->pid, child->state); goto out_tsk_rcu; } ret = -ESRCH; /* Return value for exit_state bail-out. */ } atomic_inc(&state->refcnt); rcu_read_unlock(); NO_LOCKS; /* * We do this for all requests to match traditional ptrace behavior. * If the machine state synchronization done at context switch time * includes e.g. writing back to user memory, we want to make sure * that has finished before a PTRACE_PEEKDATA can fetch the results. * On most machines, only regset data is affected by context switch * and calling utrace_regset later on will take care of that, so * this is superfluous. * * To do this purely in utrace terms, we could do: * (void) utrace_regset(child, engine, utrace_native_view(child), 0); */ if (request != PTRACE_KILL) { wait_task_inactive(child); while (child->state != TASK_TRACED && child->state != TASK_STOPPED) { if (child->exit_state) { __ptrace_state_free(state); goto out_tsk; } task_lock(child); if (child->mm && child->mm->core_waiters) { task_unlock(child); __ptrace_state_free(state); goto out_tsk; } task_unlock(child); /* * This is a dismal kludge, but it only comes up on ia64. * It might be blocked inside regset->writeback() called * from ptrace_report(), when it's on its way to quiescing * in TASK_TRACED real soon now. We actually need that * writeback call to have finished, before a PTRACE_PEEKDATA * here, for example. So keep waiting until it's really there. */ yield(); wait_task_inactive(child); } } wait_task_inactive(child); *childp = child; *enginep = engine; *statep = state; return -EIO; out_tsk_rcu: rcu_read_unlock(); out_tsk: NO_LOCKS; put_task_struct(child); out: return ret; }
//------------------------------------------------------------------------------ // Name: open // Desc: //------------------------------------------------------------------------------ bool DebuggerCore::open(const QString &path, const QString &cwd, const QList<QByteArray> &args, const QString &tty) { detach(); switch(pid_t pid = fork()) { case 0: // we are in the child now... // set ourselves (the child proc) up to be traced ptrace_traceme(); // redirect it's I/O if(!tty.isEmpty()) { FILE *const std_out = freopen(qPrintable(tty), "r+b", stdout); FILE *const std_in = freopen(qPrintable(tty), "r+b", stdin); FILE *const std_err = freopen(qPrintable(tty), "r+b", stderr); Q_UNUSED(std_out); Q_UNUSED(std_in); Q_UNUSED(std_err); } // do the actual exec execute_process(path, cwd, args); // we should never get here! abort(); break; case -1: // error! for some reason we couldn't fork reset(); return false; default: // parent do { reset(); int status; if(native::waitpid(pid, &status, __WALL) == -1) { return false; } // the very first event should be a STOP of type SIGTRAP if(!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP) { detach(); return false; } waited_threads_.insert(pid); // enable following clones (threads) if(ptrace_set_options(pid, PTRACE_O_TRACECLONE) == -1) { qDebug("[DebuggerCore] failed to set PTRACE_SETOPTIONS: %s", strerror(errno)); detach(); return false; } #ifdef PTRACE_O_EXITKILL if(ptrace_set_options(pid, PTRACE_O_EXITKILL) == -1) { qDebug("[DebuggerCore] failed to set PTRACE_SETOPTIONS: %s", strerror(errno)); detach(); return false; } #endif // setup the first event data for the primary thread waited_threads_.insert(pid); // create the process process_ = new PlatformProcess(this, pid); // the PID == primary TID auto newThread = std::make_shared<PlatformThread>(this, process_, pid); newThread->status_ = status; newThread->state_ = PlatformThread::Stopped; threads_[pid] = newThread; pid_ = pid; active_thread_ = pid; binary_info_ = edb::v1::get_binary_info(edb::v1::primary_code_region()); detectDebuggeeBitness(); return true; } while(0); break; } }