// 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.
*/
/* * 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, unsignedlong addr, void *buf, int len, unsignedint gup_flags)
{ struct mm_struct *mm; int ret;
/* * 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.
*/ staticvoid 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)
{ conststruct cred *old_cred;
BUG_ON(!child->ptrace);
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;
/* * 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);
if (task_pid_vnr(task) == task->ptrace_message) returnfalse; /* * The tracee changed its pid but the PTRACE_EVENT_EXEC event * was not wait()'ed, most probably debugger targets the old * leader which was destroyed in de_thread().
*/ returntrue;
}
/* * Ensure that nothing can wake it up, even SIGKILL * * A task is switched to this state while a ptrace operation is in progress; * such that the ptrace operation is uninterruptible.
*/ staticbool 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) && !looks_like_a_spurious_pid(task) &&
!__fatal_signal_pending(task)) {
task->jobctl |= JOBCTL_PTRACE_FROZEN;
ret = true;
}
spin_unlock_irq(&task->sighand->siglock);
/* * The child may be awake and may have cleared * JOBCTL_PTRACE_FROZEN (see ptrace_resume). The child will * not set JOBCTL_PTRACE_FROZEN or enter __TASK_TRACED anew.
*/ if (lock_task_sighand(task, &flags)) {
task->jobctl &= ~JOBCTL_PTRACE_FROZEN; if (__fatal_signal_pending(task)) {
task->jobctl &= ~JOBCTL_TRACED;
wake_up_state(task, __TASK_TRACED);
}
unlock_task_sighand(task, &flags);
}
}
/** * 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.
*/ staticint 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) { /* * 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 &&
WARN_ON_ONCE(!wait_task_inactive(child, __TASK_TRACED|TASK_FROZEN)))
ret = -ESRCH;
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;
/* SEIZE doesn't trap tracee on attach */ if (!seize)
send_signal_locked(SIGSTOP, SEND_SIG_PRIV, task, PIDTYPE_PID); /* * 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)) {
task->jobctl &= ~JOBCTL_STOPPED;
signal_wake_up_state(task, __TASK_STOPPED);
}
}
staticint ptrace_attach(struct task_struct *task, long request, unsignedlong addr, unsignedlong flags)
{ bool seize = (request == PTRACE_SEIZE); int retval;
if (seize) { if (addr != 0) return -EIO; /* * This duplicates the check in check_ptrace_options() because * ptrace_attach() and ptrace_setoptions() have historically * used different error codes for unknown ptrace options.
*/ if (flags & ~(unsignedlong)PTRACE_O_MASK) return -EIO;
/* * 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 0;
}
/** * ptrace_traceme -- helper for PTRACE_TRACEME * * Performs checks and sets PT_PTRACED. * Should be used by all ptrace implementations for PTRACE_TRACEME.
*/ staticint 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.
*/ staticint 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().
*/ staticbool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
{ bool dead;
__ptrace_unlink(p);
if (p->exit_state != EXIT_ZOMBIE) returnfalse;
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); elseif (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;
}
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;
}
/* * 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_PRIV, p);
if (__ptrace_detach(tracer, p))
list_add(&p->ptrace_entry, dead);
}
}
int ptrace_readdata(struct task_struct *tsk, unsignedlong src, char __user *dst, int len)
{ int copied = 0;
while (len > 0) { char buf[128]; int this_len, retval;
if (is_singleblock(request)) { if (unlikely(!arch_has_block_step())) return -EIO;
user_enable_block_step(child);
} elseif (is_singlestep(request) || is_sysemu_singlestep(request)) { if (unlikely(!arch_has_single_step())) return -EIO;
user_enable_single_step(child);
} else {
user_disable_single_step(child);
}
/* * Change ->exit_code and ->state under siglock to avoid the race * with wait_task_stopped() in between; a non-zero ->exit_code will * wrongly look like another report from tracee. * * Note that we need siglock even if ->exit_code == data and/or this * status was not reported yet, the new status must not be cleared by * wait_task_stopped() after resume.
*/
spin_lock_irq(&child->sighand->siglock);
child->exit_code = data;
child->jobctl &= ~JOBCTL_TRACED;
wake_up_state(child, __TASK_TRACED);
spin_unlock_irq(&child->sighand->siglock);
/* * This is declared in linux/regset.h and defined in machine-dependent * code. We put the export here, near the primary machine-neutral use, * to ensure no machine forgets it.
*/
EXPORT_SYMBOL_GPL(task_user_regset_view);
info->entry.nr = syscall_get_nr(child, regs);
syscall_get_arguments(child, regs, args); for (i = 0; i < ARRAY_SIZE(args); i++)
info->entry.args[i] = args[i];
/* args is the last field in struct ptrace_syscall_info.entry */ return offsetofend(struct ptrace_syscall_info, entry.args);
}
staticunsignedlong
ptrace_get_syscall_info_seccomp(struct task_struct *child, struct pt_regs *regs, struct ptrace_syscall_info *info)
{ /* * As struct ptrace_syscall_info.entry is currently a subset * of struct ptrace_syscall_info.seccomp, it makes sense to * initialize that subset using ptrace_get_syscall_info_entry(). * This can be reconsidered in the future if these structures * diverge significantly enough.
*/
ptrace_get_syscall_info_entry(child, regs, info);
info->seccomp.ret_data = child->ptrace_message;
/* * ret_data is the last non-reserved field * in struct ptrace_syscall_info.seccomp
*/ return offsetofend(struct ptrace_syscall_info, seccomp.ret_data);
}
/* is_error is the last field in struct ptrace_syscall_info.exit */ return offsetofend(struct ptrace_syscall_info, exit.is_error);
}
staticint
ptrace_get_syscall_info_op(struct task_struct *child)
{ /* * This does not need lock_task_sighand() to access * child->last_siginfo because ptrace_freeze_traced() * called earlier by ptrace_check_attach() ensures that * the tracee cannot go away and clear its last_siginfo.
*/ switch (child->last_siginfo ? child->last_siginfo->si_code : 0) { case SIGTRAP | 0x80: switch (child->ptrace_message) { case PTRACE_EVENTMSG_SYSCALL_ENTRY: return PTRACE_SYSCALL_INFO_ENTRY; case PTRACE_EVENTMSG_SYSCALL_EXIT: return PTRACE_SYSCALL_INFO_EXIT; default: return PTRACE_SYSCALL_INFO_NONE;
} case SIGTRAP | (PTRACE_EVENT_SECCOMP << 8): return PTRACE_SYSCALL_INFO_SECCOMP; default: return PTRACE_SYSCALL_INFO_NONE;
}
}
staticint
ptrace_set_syscall_info_entry(struct task_struct *child, struct pt_regs *regs, struct ptrace_syscall_info *info)
{ unsignedlong args[ARRAY_SIZE(info->entry.args)]; int nr = info->entry.nr; int i;
/* * Check that the syscall number specified in info->entry.nr * is either a value of type "int" or a sign-extended value * of type "int".
*/ if (nr != info->entry.nr) return -ERANGE;
for (i = 0; i < ARRAY_SIZE(args); i++) {
args[i] = info->entry.args[i]; /* * Check that the syscall argument specified in * info->entry.args[i] is either a value of type * "unsigned long" or a sign-extended value of type "long".
*/ if (args[i] != info->entry.args[i]) return -ERANGE;
}
syscall_set_nr(child, regs, nr); /* * If the syscall number is set to -1, setting syscall arguments is not * just pointless, it would also clobber the syscall return value on * those architectures that share the same register both for the first * argument of syscall and its return value.
*/ if (nr != -1)
syscall_set_arguments(child, regs, args);
return 0;
}
staticint
ptrace_set_syscall_info_seccomp(struct task_struct *child, struct pt_regs *regs, struct ptrace_syscall_info *info)
{ /* * info->entry is currently a subset of info->seccomp, * info->seccomp.ret_data is currently ignored.
*/ return ptrace_set_syscall_info_entry(child, regs, info);
}
/* * Check that the return value specified in info->exit.rval * is either a value of type "long" or a sign-extended value * of type "long".
*/ if (rval != info->exit.rval) return -ERANGE;
/* * The compatibility is tracked by info.op and info.flags: if user-space * does not instruct us to use unknown extra bits from future versions * of ptrace_syscall_info, we are not going to read them either.
*/ if (copy_from_user(&info, datavp, sizeof(info))) return -EFAULT;
/* Reserved for future use. */ if (info.flags || info.reserved) return -EINVAL;
/* Changing the type of the system call stop is not supported yet. */ if (ptrace_get_syscall_info_op(child) != info.op) return -EINVAL;
switch (info.op) { case PTRACE_SYSCALL_INFO_ENTRY: return ptrace_set_syscall_info_entry(child, regs, &info); case PTRACE_SYSCALL_INFO_EXIT: return ptrace_set_syscall_info_exit(child, regs, &info); case PTRACE_SYSCALL_INFO_SECCOMP: return ptrace_set_syscall_info_seccomp(child, regs, &info); default: /* Other types of system call stops are not supported yet. */ return -EINVAL;
}
} #endif/* CONFIG_HAVE_ARCH_TRACEHOOK */
int ptrace_request(struct task_struct *child, long request, unsignedlong addr, unsignedlong data)
{ bool seized = child->ptrace & PT_SEIZED; int ret = -EIO;
kernel_siginfo_t siginfo, *si; void __user *datavp = (void __user *) data; unsignedlong __user *datalp = datavp; unsignedlong flags;
switch (request) { case PTRACE_PEEKTEXT: case PTRACE_PEEKDATA: return generic_ptrace_peekdata(child, addr, data); case PTRACE_POKETEXT: case PTRACE_POKEDATA: return generic_ptrace_pokedata(child, addr, data);
#ifdef PTRACE_OLDSETOPTIONS case PTRACE_OLDSETOPTIONS: #endif case PTRACE_SETOPTIONS:
ret = ptrace_setoptions(child, data); break; case PTRACE_GETEVENTMSG:
ret = put_user(child->ptrace_message, datalp); break;
case PTRACE_PEEKSIGINFO:
ret = ptrace_peek_siginfo(child, addr, data); break;
case PTRACE_GETSIGINFO:
ret = ptrace_getsiginfo(child, &siginfo); if (!ret)
ret = copy_siginfo_to_user(datavp, &siginfo); break;
case PTRACE_SETSIGINFO:
ret = copy_siginfo_from_user(&siginfo, datavp); if (!ret)
ret = ptrace_setsiginfo(child, &siginfo); break;
case PTRACE_GETSIGMASK: {
sigset_t *mask;
if (addr != sizeof(sigset_t)) {
ret = -EINVAL; break;
}
if (test_tsk_restore_sigmask(child))
mask = &child->saved_sigmask; else
mask = &child->blocked;
if (copy_to_user(datavp, mask, sizeof(sigset_t)))
ret = -EFAULT; else
ret = 0;
break;
}
case PTRACE_SETSIGMASK: {
sigset_t new_set;
if (addr != sizeof(sigset_t)) {
ret = -EINVAL; break;
}
if (copy_from_user(&new_set, datavp, sizeof(sigset_t))) {
ret = -EFAULT; break;
}
/* * Every thread does recalc_sigpending() after resume, so * retarget_shared_pending() and recalc_sigpending() are not * called here.
*/
spin_lock_irq(&child->sighand->siglock);
child->blocked = new_set;
spin_unlock_irq(&child->sighand->siglock);
clear_tsk_restore_sigmask(child);
ret = 0; break;
}
case PTRACE_INTERRUPT: /* * Stop tracee without any side-effect on signal or job * control. At least one trap is guaranteed to happen * after this request. If @child is already trapped, the * current trap is not disturbed and another trap will * happen after the current trap is ended with PTRACE_CONT. * * The actual trap might not be PTRACE_EVENT_STOP trap but * the pending condition is cleared regardless.
*/ if (unlikely(!seized || !lock_task_sighand(child, &flags))) break;
/* * INTERRUPT doesn't disturb existing trap sans one * exception. If ptracer issued LISTEN for the current * STOP, this INTERRUPT should clear LISTEN and re-trap * tracee into STOP.
*/ if (likely(task_set_jobctl_pending(child, JOBCTL_TRAP_STOP)))
ptrace_signal_wake_up(child, child->jobctl & JOBCTL_LISTENING);
unlock_task_sighand(child, &flags);
ret = 0; break;
case PTRACE_LISTEN: /* * Listen for events. Tracee must be in STOP. It's not * resumed per-se but is not considered to be in TRACED by * wait(2) or ptrace(2). If an async event (e.g. group * stop state change) happens, tracee will enter STOP trap * again. Alternatively, ptracer can issue INTERRUPT to * finish listening and re-trap tracee into STOP.
*/ if (unlikely(!seized || !lock_task_sighand(child, &flags))) break;
si = child->last_siginfo; if (likely(si && (si->si_code >> 8) == PTRACE_EVENT_STOP)) {
child->jobctl |= JOBCTL_LISTENING; /* * If NOTIFY is set, it means event happened between * start of this trap and now. Trigger re-trap.
*/ if (child->jobctl & JOBCTL_TRAP_NOTIFY)
ptrace_signal_wake_up(child, true);
ret = 0;
}
unlock_task_sighand(child, &flags); break;
case PTRACE_DETACH: /* detach a process that was attached. */
ret = ptrace_detach(child, data); break;
switch (addr) { case PTRACE_GETFDPIC_EXEC:
tmp = mm->context.exec_fdpic_loadmap; break; case PTRACE_GETFDPIC_INTERP:
tmp = mm->context.interp_fdpic_loadmap; break; default: break;
}
mmput(mm);
ret = put_user(tmp, datalp); break;
} #endif
case PTRACE_SINGLESTEP: #ifdef PTRACE_SINGLEBLOCK case PTRACE_SINGLEBLOCK: #endif #ifdef PTRACE_SYSEMU case PTRACE_SYSEMU: case PTRACE_SYSEMU_SINGLESTEP: #endif case PTRACE_SYSCALL: case PTRACE_CONT: return ptrace_resume(child, request, data);
case PTRACE_KILL:
send_sig_info(SIGKILL, SEND_SIG_NOINFO, child); return 0;
#ifdef CONFIG_HAVE_ARCH_TRACEHOOK case PTRACE_GETREGSET: case PTRACE_SETREGSET: { struct iovec kiov; struct iovec __user *uiov = datavp;
if (!access_ok(uiov, sizeof(*uiov))) return -EFAULT;
if (__get_user(kiov.iov_base, &uiov->iov_base) ||
__get_user(kiov.iov_len, &uiov->iov_len)) return -EFAULT;
ret = ptrace_regset(child, request, addr, &kiov); if (!ret)
ret = __put_user(kiov.iov_len, &uiov->iov_len); break;
}
case PTRACE_GET_SYSCALL_INFO:
ret = ptrace_get_syscall_info(child, addr, datavp); break;
case PTRACE_SET_SYSCALL_INFO:
ret = ptrace_set_syscall_info(child, addr, datavp); break; #endif
case PTRACE_SECCOMP_GET_FILTER:
ret = seccomp_get_filter(child, addr, datavp); break;
case PTRACE_SECCOMP_GET_METADATA:
ret = seccomp_get_metadata(child, addr, datavp); break;
#ifdef CONFIG_RSEQ case PTRACE_GET_RSEQ_CONFIGURATION:
ret = ptrace_get_rseq_configuration(child, addr, datavp); break; #endif
case PTRACE_SET_SYSCALL_USER_DISPATCH_CONFIG:
ret = syscall_user_dispatch_set_config(child, addr, datavp); break;
case PTRACE_GET_SYSCALL_USER_DISPATCH_CONFIG:
ret = syscall_user_dispatch_get_config(child, addr, datavp); break;
switch (request) { case PTRACE_PEEKTEXT: case PTRACE_PEEKDATA:
ret = ptrace_access_vm(child, addr, &word, sizeof(word),
FOLL_FORCE); if (ret != sizeof(word))
ret = -EIO; else
ret = put_user(word, datap); break;
case PTRACE_POKETEXT: case PTRACE_POKEDATA:
ret = ptrace_access_vm(child, addr, &data, sizeof(data),
FOLL_FORCE | FOLL_WRITE);
ret = (ret != sizeof(data) ? -EIO : 0); break;
case PTRACE_GETEVENTMSG:
ret = put_user((compat_ulong_t) child->ptrace_message, datap); break;
case PTRACE_GETSIGINFO:
ret = ptrace_getsiginfo(child, &siginfo); if (!ret)
ret = copy_siginfo_to_user32(
(struct compat_siginfo __user *) datap,
&siginfo); break;
case PTRACE_SETSIGINFO:
ret = copy_siginfo_from_user32(
&siginfo, (struct compat_siginfo __user *) datap); if (!ret)
ret = ptrace_setsiginfo(child, &siginfo); break; #ifdef CONFIG_HAVE_ARCH_TRACEHOOK case PTRACE_GETREGSET: case PTRACE_SETREGSET:
{ struct iovec kiov; struct compat_iovec __user *uiov =
(struct compat_iovec __user *) datap;
compat_uptr_t ptr;
compat_size_t len;
if (!access_ok(uiov, sizeof(*uiov))) return -EFAULT;
if (__get_user(ptr, &uiov->iov_base) ||
__get_user(len, &uiov->iov_len)) return -EFAULT;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.