Release 4.15 kernel/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/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>
/*
* 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric W. Biedermann | 119 | 100.00% | 1 | 100.00% |
Total | 119 | 100.00% | 1 | 100.00% |
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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ingo Molnar | 38 | 62.30% | 2 | 33.33% |
Eric W. Biedermann | 16 | 26.23% | 2 | 33.33% |
Roland McGrath | 4 | 6.56% | 1 | 16.67% |
Eric Sesterhenn / Snakebyte | 3 | 4.92% | 1 | 16.67% |
Total | 61 | 100.00% | 6 | 100.00% |
/*
* 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)
{
rcu_read_lock();
__ptrace_link(child, new_parent, __task_cred(new_parent));
rcu_read_unlock();
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric W. Biedermann | 33 | 97.06% | 2 | 66.67% |
Ingo Molnar | 1 | 2.94% | 1 | 33.33% |
Total | 34 | 100.00% | 3 | 100.00% |
/**
* __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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Roland McGrath | 56 | 31.46% | 5 | 26.32% |
Tejun Heo | 44 | 24.72% | 5 | 26.32% |
Oleg Nesterov | 32 | 17.98% | 4 | 21.05% |
Eric W. Biedermann | 23 | 12.92% | 1 | 5.26% |
Ingo Molnar | 13 | 7.30% | 2 | 10.53% |
Ales Novak | 7 | 3.93% | 1 | 5.26% |
Matthew Wilcox | 3 | 1.69% | 1 | 5.26% |
Total | 178 | 100.00% | 19 | 100.00% |
/* 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Oleg Nesterov | 76 | 100.00% | 1 | 100.00% |
Total | 76 | 100.00% | 1 | 100.00% |
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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Oleg Nesterov | 75 | 87.21% | 1 | 50.00% |
Ben Segall | 11 | 12.79% | 1 | 50.00% |
Total | 86 | 100.00% | 2 | 100.00% |
/**
* 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Roland McGrath | 41 | 37.27% | 3 | 25.00% |
Linus Torvalds | 31 | 28.18% | 2 | 16.67% |
Oleg Nesterov | 30 | 27.27% | 3 | 25.00% |
Tejun Heo | 5 | 4.55% | 2 | 16.67% |
Matthew Wilcox | 2 | 1.82% | 1 | 8.33% |
David Howells | 1 | 0.91% | 1 | 8.33% |
Total | 110 | 100.00% | 12 | 100.00% |
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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric Paris | 42 | 100.00% | 1 | 100.00% |
Total | 42 | 100.00% | 1 | 100.00% |
/* 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();
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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jann Horn | 78 | 30.59% | 1 | 5.88% |
Eric W. Biedermann | 50 | 19.61% | 4 | 23.53% |
Linus Torvalds | 36 | 14.12% | 1 | 5.88% |
David Howells | 34 | 13.33% | 2 | 11.76% |
Serge E. Hallyn | 21 | 8.24% | 1 | 5.88% |
Miklos Szeredi | 16 | 6.27% | 1 | 5.88% |
Stephen D. Smalley | 7 | 2.75% | 1 | 5.88% |
Eric Paris | 5 | 1.96% | 2 | 11.76% |
Mark A. Grondona | 4 | 1.57% | 1 | 5.88% |
Kees Cook | 2 | 0.78% | 1 | 5.88% |
Ingo Molnar | 1 | 0.39% | 1 | 5.88% |
Tetsuo Handa | 1 | 0.39% | 1 | 5.88% |
Total | 255 | 100.00% | 17 | 100.00% |
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Miklos Szeredi | 31 | 77.50% | 1 | 50.00% |
Stephen D. Smalley | 9 | 22.50% | 1 | 50.00% |
Total | 40 | 100.00% | 2 | 100.00% |
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_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_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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Tejun Heo | 93 | 27.51% | 7 | 20.59% |
Oleg Nesterov | 71 | 21.01% | 7 | 20.59% |
Denys Vlasenko | 53 | 15.68% | 2 | 5.88% |
Linus Torvalds | 44 | 13.02% | 5 | 14.71% |
Miklos Szeredi | 22 | 6.51% | 1 | 2.94% |
David Howells | 19 | 5.62% | 2 | 5.88% |
Vladimir Zapolskiy | 9 | 2.66% | 1 | 2.94% |
Greg Kroah-Hartman | 6 | 1.78% | 2 | 5.88% |
Eric W. Biedermann | 6 | 1.78% | 2 | 5.88% |
Al Viro | 5 | 1.48% | 1 | 2.94% |
Pavel Emelyanov | 4 | 1.18% | 1 | 2.94% |
Motohiro Kosaki | 4 | 1.18% | 1 | 2.94% |
Jann Horn | 1 | 0.30% | 1 | 2.94% |
Ingo Molnar | 1 | 0.30% | 1 | 2.94% |
Total | 338 | 100.00% | 34 | 100.00% |
/**
* 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Oleg Nesterov | 80 | 97.56% | 2 | 50.00% |
Linus Torvalds | 1 | 1.22% | 1 | 25.00% |
Eric W. Biedermann | 1 | 1.22% | 1 | 25.00% |
Total | 82 | 100.00% | 4 | 100.00% |
/*
* 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Oleg Nesterov | 69 | 100.00% | 1 | 100.00% |
Total | 69 | 100.00% | 1 | 100.00% |
/*
* 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Oleg Nesterov | 115 | 100.00% | 3 | 100.00% |
Total | 115 | 100.00% | 3 | 100.00% |
static int ptrace_detach(struct task_struct *child, unsigned int data)
{
if (!valid_signal(data))
return -EIO;
/* Architecture-specific hardware disable .. */
ptrace_disable(child);
write_lock_irq(&tasklist_lock);
/*
* We rely on ptrace_freeze_traced(). It can't be killed and
* untraced by another thread, it can't be a zombie.
*/
WARN_ON(!child->ptrace || child->exit_state);
/*
* tasklist_lock avoids the race with wait_task_stopped(), see
* the comment in ptrace_resume().
*/
child->exit_code = data;
__ptrace_detach(current, child);
write_unlock_irq(&tasklist_lock);
proc_ptrace_connector(child, PTRACE_DETACH);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 47 | 57.32% | 2 | 25.00% |
Oleg Nesterov | 24 | 29.27% | 4 | 50.00% |
Vladimir Zapolskiy | 7 | 8.54% | 1 | 12.50% |
Jesper Juhl | 4 | 4.88% | 1 | 12.50% |
Total | 82 | 100.00% | 8 | 100.00% |
/*
* Detach all tasks we were using ptrace on. Called with tasklist held
* for writing.
*/
void exit_ptrace(struct task_struct *tracer, struct list_head *dead)
{
struct task_struct *p, *n;
list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) {
if (unlikely(p->ptrace & PT_EXITKILL))
send_sig_info(SIGKILL, SEND_SIG_FORCED, p);
if (__ptrace_detach(tracer, p))
list_add(&p->ptrace_entry, dead);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Oleg Nesterov | 74 | 100.00% | 3 | 100.00% |
Total | 74 | 100.00% | 3 | 100.00% |
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 = ptrace_access_vm(tsk, src, buf, this_len, FOLL_FORCE);
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 126 | 97.67% | 2 | 40.00% |
Linus Torvalds | 1 | 0.78% | 1 | 20.00% |
Lorenzo Stoakes | 1 | 0.78% | 1 | 20.00% |
Eric W. Biedermann | 1 | 0.78% | 1 | 20.00% |
Total | 129 | 100.00% | 5 | 100.00% |
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 = ptrace_access_vm(tsk, dst, buf, this_len,
FOLL_FORCE | FOLL_WRITE);
if (!retval) {
if (copied)
break;
return -EIO;
}
copied += retval;
src += retval;
dst += retval;
len -= retval;
}
return copied;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 126 | 96.18% | 2 | 40.00% |
Lorenzo Stoakes | 3 | 2.29% | 1 | 20.00% |
Eric W. Biedermann | 1 | 0.76% | 1 | 20.00% |
Linus Torvalds | 1 | 0.76% | 1 | 20.00% |
Total | 131 | 100.00% | 5 | 100.00% |
static int ptrace_setoptions(struct task_struct *child, unsigned long data)
{
unsigned flags;
if (data & ~(unsigned long)PTRACE_O_MASK)
return -EINVAL;
if (unlikely(data & PTRACE_O_SUSPEND_SECCOMP)) {
if (!IS_ENABLED(CONFIG_CHECKPOINT_RESTORE) ||
!IS_ENABLED(CONFIG_SECCOMP))
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (seccomp_mode(¤t->seccomp) != SECCOMP_MODE_DISABLED ||
current->ptrace & PT_SUSPEND_SECCOMP)
return -EPERM;
}
/* Avoid intermediate state when all opts are cleared */
flags = child->ptrace;
flags &= ~(PTRACE_O_MASK << PT_OPT_FLAG_SHIFT);
flags |= (data << PT_OPT_FLAG_SHIFT);
child->ptrace = flags;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Tycho Andersen | 61 | 47.29% | 1 | 11.11% |
Denys Vlasenko | 35 | 27.13% | 2 | 22.22% |
Daniel Jacobowitz | 30 | 23.26% | 4 | 44.44% |
Masahiro Yamada | 2 | 1.55% | 1 | 11.11% |
Namhyung Kim | 1 | 0.78% | 1 | 11.11% |
Total | 129 | 100.00% | 9 | 100.00% |
static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info)
{
unsigned long flags;
int error = -ESRCH;
if (lock_task_sighand(child, &flags)) {
error = -EINVAL;
if (likely(child->last_siginfo != NULL)) {
*info = *child->last_siginfo;
error = 0;
}
unlock_task_sighand(child, &flags);
}
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Prasanna Meda | 41 | 52.56% | 2 | 28.57% |
Daniel Jacobowitz | 17 | 21.79% | 1 | 14.29% |
Oleg Nesterov | 12 | 15.38% | 1 | 14.29% |
Roland McGrath | 6 | 7.69% | 2 | 28.57% |
Linus Torvalds | 2 | 2.56% | 1 | 14.29% |
Total | 78 | 100.00% | 7 | 100.00% |
static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info)
{
unsigned long flags;
int error = -ESRCH;
if (lock_task_sighand(child, &flags)) {
error = -EINVAL;
if (likely(child->last_siginfo != NULL)) {
*child->last_siginfo = *info;
error = 0;
}
unlock_task_sighand(child, &flags);
}
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Prasanna Meda | 42 | 53.16% | 2 | 33.33% |
Daniel Jacobowitz | 19 | 24.05% | 1 | 16.67% |
Oleg Nesterov | 12 | 15.19% | 1 | 16.67% |
Roland McGrath | 4 | 5.06% | 1 | 16.67% |
Linus Torvalds | 2 | 2.53% | 1 | 16.67% |
Total | 79 | 100.00% | 6 | 100.00% |
static int ptrace_peek_siginfo(struct task_struct *child,
unsigned long addr,
unsigned long data)
{
struct ptrace_peeksiginfo_args arg;
struct sigpending *pending;
struct sigqueue *q;
int ret, i;
ret = copy_from_user(&arg, (void __user *) addr,
sizeof(struct ptrace_peeksiginfo_args));
if (ret)
return -EFAULT;
if (arg.flags & ~PTRACE_PEEKSIGINFO_SHARED)
return -EINVAL; /* unknown flags */
if (arg.nr < 0)
return -EINVAL;
if (arg.flags & PTRACE_PEEKSIGINFO_SHARED)
pending = &child->signal->shared_pending;
else
pending = &child->pending;
for (i = 0; i < arg.nr; ) {
siginfo_t info;
s32 off = arg.off + i;
spin_lock_irq(&child->sighand->siglock);
list_for_each_entry(q, &pending->list, list) {
if (!off--) {
copy_siginfo(&info, &q->info);
break;
}
}
spin_unlock_irq(&child->sighand->siglock);
if (off >= 0) /* beyond the end of the list */
break;
#ifdef CONFIG_COMPAT
if (unlikely(in_compat_syscall())) {
compat_siginfo_t __user *uinfo = compat_ptr(data);
if (copy_siginfo_to_user32(uinfo, &info)) {
ret = -EFAULT;
break;
}
} else
#endif
{
siginfo_t __user *uinfo = (siginfo_t __user *) data;
if (copy_siginfo_to_user(uinfo, &info)) {
ret = -EFAULT;
break;
}
}
data += sizeof(siginfo_t);
i++;
if (signal_pending(current))
break;
cond_resched();
}
if (i > 0)
return i;
return ret;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrey Vagin | 298 | 95.51% | 1 | 33.33% |
Mathieu Desnoyers | 13 | 4.17% | 1 | 33.33% |
Andrew Lutomirski | 1 | 0.32% | 1 | 33.33% |
Total | 312 | 100.00% | 3 | 100.00% |
#ifdef PTRACE_SINGLESTEP
#define is_singlestep(request) ((request) == PTRACE_SINGLESTEP)
#else
#