2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* linux/kernel/ptrace.c
|
|
|
|
*
|
|
|
|
* (C) Copyright 1999 Linus Torvalds
|
|
|
|
*
|
|
|
|
* Common interfaces for "ptrace()" which we do not want
|
|
|
|
* to continually duplicate across every architecture.
|
|
|
|
*/
|
|
|
|
|
2006-01-12 04:17:46 +08:00
|
|
|
#include <linux/capability.h>
|
2011-05-24 02:51:41 +08:00
|
|
|
#include <linux/export.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/sched.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>
|
2005-05-01 23:59:14 +08:00
|
|
|
#include <linux/signal.h>
|
2013-05-08 07:19:08 +08:00
|
|
|
#include <linux/uio.h>
|
2007-03-21 01:58:35 +08:00
|
|
|
#include <linux/audit.h>
|
2007-10-19 14:40:14 +08:00
|
|
|
#include <linux/pid_namespace.h>
|
2008-02-06 17:36:44 +08:00
|
|
|
#include <linux/syscalls.h>
|
2009-04-08 14:21:06 +08:00
|
|
|
#include <linux/uaccess.h>
|
2010-02-12 03:51:00 +08:00
|
|
|
#include <linux/regset.h>
|
2011-04-07 22:53:20 +08:00
|
|
|
#include <linux/hw_breakpoint.h>
|
2011-07-16 01:45:18 +08:00
|
|
|
#include <linux/cn_proc.h>
|
2013-05-01 06:27:59 +08:00
|
|
|
#include <linux/compat.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-12-19 22:10:24 +08:00
|
|
|
|
2011-06-02 17:14:00 +08:00
|
|
|
static int ptrace_trapping_sleep_fn(void *flags)
|
|
|
|
{
|
|
|
|
schedule();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08: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.
|
|
|
|
*/
|
2006-07-03 15:25:41 +08:00
|
|
|
void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-03-25 09:36:23 +08:00
|
|
|
BUG_ON(!list_empty(&child->ptrace_entry));
|
|
|
|
list_add(&child->ptrace_entry, &new_parent->ptraced);
|
2005-04-17 06:20:36 +08:00
|
|
|
child->parent = new_parent;
|
|
|
|
}
|
2009-04-08 14:21:06 +08:00
|
|
|
|
2011-03-23 17:37:01 +08:00
|
|
|
/**
|
|
|
|
* __ptrace_unlink - unlink ptracee and restore its execution state
|
|
|
|
* @child: ptracee to be unlinked
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
ptrace: Always put ptracee into appropriate execution state
Currently, __ptrace_unlink() wakes up the tracee iff it's in
TASK_TRACED. For unlinking from PTRACE_DETACH, this is correct as the
tracee is guaranteed to be in TASK_TRACED or dead; however, unlinking
also happens when the ptracer exits and in this case the ptracee can
be in any state and ptrace might be left running even if the group it
belongs to is stopped.
This patch updates __ptrace_unlink() such that GROUP_STOP_PENDING is
reinstated regardless of the ptracee's current state as long as it's
alive and makes sure that signal_wake_up() is called if execution
state transition is necessary.
Test case follows.
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
static const struct timespec ts1s = { .tv_sec = 1 };
int main(void)
{
pid_t tracee;
siginfo_t si;
tracee = fork();
if (tracee == 0) {
while (1) {
nanosleep(&ts1s, NULL);
write(1, ".", 1);
}
}
ptrace(PTRACE_ATTACH, tracee, NULL, NULL);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
write(1, "exiting", 7);
return 0;
}
Before the patch, after the parent process exits, the child is left
running and prints out "." every second.
exiting..... (continues)
After the patch, the group stop initiated by the implied SIGSTOP from
PTRACE_ATTACH is re-established when the parent exits.
exiting
Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Oleg Nesterov <oleg@redhat.com>
2011-03-23 17:37:01 +08:00
|
|
|
* 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.
|
2011-03-23 17:37:01 +08:00
|
|
|
*
|
|
|
|
* CONTEXT:
|
|
|
|
* write_lock_irq(tasklist_lock)
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2006-07-03 15:25:41 +08:00
|
|
|
void __ptrace_unlink(struct task_struct *child)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-02-16 03:50:10 +08:00
|
|
|
BUG_ON(!child->ptrace);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
child->ptrace = 0;
|
2008-03-25 09:36:23 +08:00
|
|
|
child->parent = child->real_parent;
|
|
|
|
list_del_init(&child->ptrace_entry);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
spin_lock(&child->sighand->siglock);
|
ptrace: Always put ptracee into appropriate execution state
Currently, __ptrace_unlink() wakes up the tracee iff it's in
TASK_TRACED. For unlinking from PTRACE_DETACH, this is correct as the
tracee is guaranteed to be in TASK_TRACED or dead; however, unlinking
also happens when the ptracer exits and in this case the ptracee can
be in any state and ptrace might be left running even if the group it
belongs to is stopped.
This patch updates __ptrace_unlink() such that GROUP_STOP_PENDING is
reinstated regardless of the ptracee's current state as long as it's
alive and makes sure that signal_wake_up() is called if execution
state transition is necessary.
Test case follows.
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
static const struct timespec ts1s = { .tv_sec = 1 };
int main(void)
{
pid_t tracee;
siginfo_t si;
tracee = fork();
if (tracee == 0) {
while (1) {
nanosleep(&ts1s, NULL);
write(1, ".", 1);
}
}
ptrace(PTRACE_ATTACH, tracee, NULL, NULL);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
write(1, "exiting", 7);
return 0;
}
Before the patch, after the parent process exits, the child is left
running and prints out "." every second.
exiting..... (continues)
After the patch, the group stop initiated by the implied SIGSTOP from
PTRACE_ATTACH is re-established when the parent exits.
exiting
Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Oleg Nesterov <oleg@redhat.com>
2011-03-23 17:37:01 +08:00
|
|
|
|
2011-06-14 17:20:14 +08:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
ptrace: Always put ptracee into appropriate execution state
Currently, __ptrace_unlink() wakes up the tracee iff it's in
TASK_TRACED. For unlinking from PTRACE_DETACH, this is correct as the
tracee is guaranteed to be in TASK_TRACED or dead; however, unlinking
also happens when the ptracer exits and in this case the ptracee can
be in any state and ptrace might be left running even if the group it
belongs to is stopped.
This patch updates __ptrace_unlink() such that GROUP_STOP_PENDING is
reinstated regardless of the ptracee's current state as long as it's
alive and makes sure that signal_wake_up() is called if execution
state transition is necessary.
Test case follows.
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
static const struct timespec ts1s = { .tv_sec = 1 };
int main(void)
{
pid_t tracee;
siginfo_t si;
tracee = fork();
if (tracee == 0) {
while (1) {
nanosleep(&ts1s, NULL);
write(1, ".", 1);
}
}
ptrace(PTRACE_ATTACH, tracee, NULL, NULL);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
write(1, "exiting", 7);
return 0;
}
Before the patch, after the parent process exits, the child is left
running and prints out "." every second.
exiting..... (continues)
After the patch, the group stop initiated by the implied SIGSTOP from
PTRACE_ATTACH is re-established when the parent exits.
exiting
Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Oleg Nesterov <oleg@redhat.com>
2011-03-23 17:37:01 +08:00
|
|
|
/*
|
2011-06-02 17:13:59 +08:00
|
|
|
* Reinstate JOBCTL_STOP_PENDING if group stop is in effect and
|
ptrace: Always put ptracee into appropriate execution state
Currently, __ptrace_unlink() wakes up the tracee iff it's in
TASK_TRACED. For unlinking from PTRACE_DETACH, this is correct as the
tracee is guaranteed to be in TASK_TRACED or dead; however, unlinking
also happens when the ptracer exits and in this case the ptracee can
be in any state and ptrace might be left running even if the group it
belongs to is stopped.
This patch updates __ptrace_unlink() such that GROUP_STOP_PENDING is
reinstated regardless of the ptracee's current state as long as it's
alive and makes sure that signal_wake_up() is called if execution
state transition is necessary.
Test case follows.
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
static const struct timespec ts1s = { .tv_sec = 1 };
int main(void)
{
pid_t tracee;
siginfo_t si;
tracee = fork();
if (tracee == 0) {
while (1) {
nanosleep(&ts1s, NULL);
write(1, ".", 1);
}
}
ptrace(PTRACE_ATTACH, tracee, NULL, NULL);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
write(1, "exiting", 7);
return 0;
}
Before the patch, after the parent process exits, the child is left
running and prints out "." every second.
exiting..... (continues)
After the patch, the group stop initiated by the implied SIGSTOP from
PTRACE_ATTACH is re-established when the parent exits.
exiting
Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Oleg Nesterov <oleg@redhat.com>
2011-03-23 17:37:01 +08:00
|
|
|
* @child isn't dead.
|
|
|
|
*/
|
|
|
|
if (!(child->flags & PF_EXITING) &&
|
|
|
|
(child->signal->flags & SIGNAL_STOP_STOPPED ||
|
2012-01-05 00:29:20 +08:00
|
|
|
child->signal->group_stop_count)) {
|
2011-06-02 17:13:59 +08:00
|
|
|
child->jobctl |= JOBCTL_STOP_PENDING;
|
ptrace: Always put ptracee into appropriate execution state
Currently, __ptrace_unlink() wakes up the tracee iff it's in
TASK_TRACED. For unlinking from PTRACE_DETACH, this is correct as the
tracee is guaranteed to be in TASK_TRACED or dead; however, unlinking
also happens when the ptracer exits and in this case the ptracee can
be in any state and ptrace might be left running even if the group it
belongs to is stopped.
This patch updates __ptrace_unlink() such that GROUP_STOP_PENDING is
reinstated regardless of the ptracee's current state as long as it's
alive and makes sure that signal_wake_up() is called if execution
state transition is necessary.
Test case follows.
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
static const struct timespec ts1s = { .tv_sec = 1 };
int main(void)
{
pid_t tracee;
siginfo_t si;
tracee = fork();
if (tracee == 0) {
while (1) {
nanosleep(&ts1s, NULL);
write(1, ".", 1);
}
}
ptrace(PTRACE_ATTACH, tracee, NULL, NULL);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
write(1, "exiting", 7);
return 0;
}
Before the patch, after the parent process exits, the child is left
running and prints out "." every second.
exiting..... (continues)
After the patch, the group stop initiated by the implied SIGSTOP from
PTRACE_ATTACH is re-established when the parent exits.
exiting
Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Oleg Nesterov <oleg@redhat.com>
2011-03-23 17:37:01 +08:00
|
|
|
|
2012-01-05 00:29:20 +08:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
ptrace: Always put ptracee into appropriate execution state
Currently, __ptrace_unlink() wakes up the tracee iff it's in
TASK_TRACED. For unlinking from PTRACE_DETACH, this is correct as the
tracee is guaranteed to be in TASK_TRACED or dead; however, unlinking
also happens when the ptracer exits and in this case the ptracee can
be in any state and ptrace might be left running even if the group it
belongs to is stopped.
This patch updates __ptrace_unlink() such that GROUP_STOP_PENDING is
reinstated regardless of the ptracee's current state as long as it's
alive and makes sure that signal_wake_up() is called if execution
state transition is necessary.
Test case follows.
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
static const struct timespec ts1s = { .tv_sec = 1 };
int main(void)
{
pid_t tracee;
siginfo_t si;
tracee = fork();
if (tracee == 0) {
while (1) {
nanosleep(&ts1s, NULL);
write(1, ".", 1);
}
}
ptrace(PTRACE_ATTACH, tracee, NULL, NULL);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
write(1, "exiting", 7);
return 0;
}
Before the patch, after the parent process exits, the child is left
running and prints out "." every second.
exiting..... (continues)
After the patch, the group stop initiated by the implied SIGSTOP from
PTRACE_ATTACH is re-established when the parent exits.
exiting
Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Oleg Nesterov <oleg@redhat.com>
2011-03-23 17:37:01 +08:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2011-06-02 17:13:59 +08:00
|
|
|
if (child->jobctl & JOBCTL_STOP_PENDING || task_is_traced(child))
|
2013-01-22 03:47:41 +08:00
|
|
|
ptrace_signal_wake_up(child, true);
|
ptrace: Always put ptracee into appropriate execution state
Currently, __ptrace_unlink() wakes up the tracee iff it's in
TASK_TRACED. For unlinking from PTRACE_DETACH, this is correct as the
tracee is guaranteed to be in TASK_TRACED or dead; however, unlinking
also happens when the ptracer exits and in this case the ptracee can
be in any state and ptrace might be left running even if the group it
belongs to is stopped.
This patch updates __ptrace_unlink() such that GROUP_STOP_PENDING is
reinstated regardless of the ptracee's current state as long as it's
alive and makes sure that signal_wake_up() is called if execution
state transition is necessary.
Test case follows.
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
static const struct timespec ts1s = { .tv_sec = 1 };
int main(void)
{
pid_t tracee;
siginfo_t si;
tracee = fork();
if (tracee == 0) {
while (1) {
nanosleep(&ts1s, NULL);
write(1, ".", 1);
}
}
ptrace(PTRACE_ATTACH, tracee, NULL, NULL);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
waitid(P_PID, tracee, &si, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, (void *)(long)si.si_status);
write(1, "exiting", 7);
return 0;
}
Before the patch, after the parent process exits, the child is left
running and prints out "." every second.
exiting..... (continues)
After the patch, the group stop initiated by the implied SIGSTOP from
PTRACE_ATTACH is re-established when the parent exits.
exiting
Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Oleg Nesterov <oleg@redhat.com>
2011-03-23 17:37:01 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_unlock(&child->sighand->siglock);
|
|
|
|
}
|
|
|
|
|
2013-01-22 03:48:00 +08: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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ptrace_unfreeze_traced(struct task_struct *task)
|
|
|
|
{
|
|
|
|
if (task->state != __TASK_TRACED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
WARN_ON(!task->ptrace || task->parent != current);
|
|
|
|
|
|
|
|
spin_lock_irq(&task->sighand->siglock);
|
|
|
|
if (__fatal_signal_pending(task))
|
|
|
|
wake_up_state(task, __TASK_TRACED);
|
|
|
|
else
|
|
|
|
task->state = TASK_TRACED;
|
|
|
|
spin_unlock_irq(&task->sighand->siglock);
|
|
|
|
}
|
|
|
|
|
2011-06-02 17:13:59 +08: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.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2013-01-21 03:25:47 +08:00
|
|
|
static int ptrace_check_attach(struct task_struct *child, bool ignore_state)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
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);
|
2013-01-22 03:48:00 +08:00
|
|
|
if (child->ptrace && child->parent == current) {
|
|
|
|
WARN_ON(child->state == __TASK_TRACED);
|
2008-02-08 20:19:00 +08:00
|
|
|
/*
|
|
|
|
* child->sighand can't be NULL, release_task()
|
|
|
|
* does ptrace_unlink() before __exit_signal().
|
|
|
|
*/
|
2013-01-22 03:48:00 +08:00
|
|
|
if (ignore_state || ptrace_freeze_traced(child))
|
2011-04-02 02:13:01 +08:00
|
|
|
ret = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
read_unlock(&tasklist_lock);
|
|
|
|
|
2013-01-22 03:48:00 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-01-04 01:25:15 +08: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);
|
|
|
|
}
|
|
|
|
|
2012-07-31 19:37:00 +08:00
|
|
|
/* Returns 0 on success, -errno on denial. */
|
|
|
|
static int __ptrace_may_access(struct task_struct *task, unsigned int mode)
|
2005-09-07 06:18:24 +08:00
|
|
|
{
|
2008-11-14 07:39:19 +08:00
|
|
|
const struct cred *cred = current_cred(), *tcred;
|
2008-11-14 07:39:16 +08:00
|
|
|
|
2006-06-26 15:25:59 +08:00
|
|
|
/* 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;
|
2008-11-14 07:39:19 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
tcred = __task_cred(task);
|
2012-03-04 12:21:47 +08:00
|
|
|
if (uid_eq(cred->uid, tcred->euid) &&
|
|
|
|
uid_eq(cred->uid, tcred->suid) &&
|
|
|
|
uid_eq(cred->uid, tcred->uid) &&
|
|
|
|
gid_eq(cred->gid, tcred->egid) &&
|
|
|
|
gid_eq(cred->gid, tcred->sgid) &&
|
|
|
|
gid_eq(cred->gid, tcred->gid))
|
2011-03-24 07:43:20 +08:00
|
|
|
goto ok;
|
2011-11-17 15:15:31 +08:00
|
|
|
if (ptrace_has_cap(tcred->user_ns, mode))
|
2011-03-24 07:43:20 +08:00
|
|
|
goto ok;
|
|
|
|
rcu_read_unlock();
|
|
|
|
return -EPERM;
|
|
|
|
ok:
|
2008-11-14 07:39:19 +08:00
|
|
|
rcu_read_unlock();
|
2005-09-07 06:18:24 +08:00
|
|
|
smp_rmb();
|
2006-06-26 15:25:59 +08:00
|
|
|
if (task->mm)
|
2007-07-19 16:48:27 +08:00
|
|
|
dumpable = get_dumpable(task->mm);
|
2012-07-26 20:05:21 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
if (!dumpable && !ptrace_has_cap(__task_cred(task)->user_ns, mode)) {
|
|
|
|
rcu_read_unlock();
|
2005-09-07 06:18:24 +08:00
|
|
|
return -EPERM;
|
2012-07-26 20:05:21 +08:00
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
2005-09-07 06:18:24 +08:00
|
|
|
|
2009-05-07 17:26:19 +08:00
|
|
|
return security_ptrace_access_check(task, mode);
|
2005-09-07 06:18:24 +08:00
|
|
|
}
|
|
|
|
|
Security: split proc ptrace checking into read vs. attach
Enable security modules to distinguish reading of process state via
proc from full ptrace access by renaming ptrace_may_attach to
ptrace_may_access and adding a mode argument indicating whether only
read access or full attach access is requested. This allows security
modules to permit access to reading process state without granting
full ptrace access. The base DAC/capability checking remains unchanged.
Read access to /proc/pid/mem continues to apply a full ptrace attach
check since check_mem_permission() already requires the current task
to already be ptracing the target. The other ptrace checks within
proc for elements like environ, maps, and fds are changed to pass the
read mode instead of attach.
In the SELinux case, we model such reading of process state as a
reading of a proc file labeled with the target process' label. This
enables SELinux policy to permit such reading of process state without
permitting control or manipulation of the target process, as there are
a number of cases where programs probe for such information via proc
but do not need to be able to control the target (e.g. procps,
lsof, PolicyKit, ConsoleKit). At present we have to choose between
allowing full ptrace in policy (more permissive than required/desired)
or breaking functionality (or in some cases just silencing the denials
via dontaudit rules but this can hide genuine attacks).
This version of the patch incorporates comments from Casey Schaufler
(change/replace existing ptrace_may_attach interface, pass access
mode), and Chris Wright (provide greater consistency in the checking).
Note that like their predecessors __ptrace_may_attach and
ptrace_may_attach, the __ptrace_may_access and ptrace_may_access
interfaces use different return value conventions from each other (0
or -errno vs. 1 or 0). I retained this difference to avoid any
changes to the caller logic but made the difference clearer by
changing the latter interface to return a bool rather than an int and
by adding a comment about it to ptrace.h for any future callers.
Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
Acked-by: Chris Wright <chrisw@sous-sol.org>
Signed-off-by: James Morris <jmorris@namei.org>
2008-05-19 20:32:49 +08:00
|
|
|
bool ptrace_may_access(struct task_struct *task, unsigned int mode)
|
2005-09-07 06:18:24 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
task_lock(task);
|
Security: split proc ptrace checking into read vs. attach
Enable security modules to distinguish reading of process state via
proc from full ptrace access by renaming ptrace_may_attach to
ptrace_may_access and adding a mode argument indicating whether only
read access or full attach access is requested. This allows security
modules to permit access to reading process state without granting
full ptrace access. The base DAC/capability checking remains unchanged.
Read access to /proc/pid/mem continues to apply a full ptrace attach
check since check_mem_permission() already requires the current task
to already be ptracing the target. The other ptrace checks within
proc for elements like environ, maps, and fds are changed to pass the
read mode instead of attach.
In the SELinux case, we model such reading of process state as a
reading of a proc file labeled with the target process' label. This
enables SELinux policy to permit such reading of process state without
permitting control or manipulation of the target process, as there are
a number of cases where programs probe for such information via proc
but do not need to be able to control the target (e.g. procps,
lsof, PolicyKit, ConsoleKit). At present we have to choose between
allowing full ptrace in policy (more permissive than required/desired)
or breaking functionality (or in some cases just silencing the denials
via dontaudit rules but this can hide genuine attacks).
This version of the patch incorporates comments from Casey Schaufler
(change/replace existing ptrace_may_attach interface, pass access
mode), and Chris Wright (provide greater consistency in the checking).
Note that like their predecessors __ptrace_may_attach and
ptrace_may_attach, the __ptrace_may_access and ptrace_may_access
interfaces use different return value conventions from each other (0
or -errno vs. 1 or 0). I retained this difference to avoid any
changes to the caller logic but made the difference clearer by
changing the latter interface to return a bool rather than an int and
by adding a comment about it to ptrace.h for any future callers.
Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
Acked-by: Chris Wright <chrisw@sous-sol.org>
Signed-off-by: James Morris <jmorris@namei.org>
2008-05-19 20:32:49 +08:00
|
|
|
err = __ptrace_may_access(task, mode);
|
2005-09-07 06:18:24 +08:00
|
|
|
task_unlock(task);
|
2009-04-08 14:21:06 +08:00
|
|
|
return !err;
|
2005-09-07 06:18:24 +08:00
|
|
|
}
|
|
|
|
|
ptrace: implement PTRACE_SEIZE
PTRACE_ATTACH implicitly issues SIGSTOP on attach which has side
effects on tracee signal and job control states. This patch
implements a new ptrace request PTRACE_SEIZE which attaches a tracee
without trapping it or affecting its signal and job control states.
The usage is the same with PTRACE_ATTACH but it takes PTRACE_SEIZE_*
flags in @data. Currently, the only defined flag is
PTRACE_SEIZE_DEVEL which is a temporary flag to enable PTRACE_SEIZE.
PTRACE_SEIZE will change ptrace behaviors outside of attach itself.
The changes will be implemented gradually and the DEVEL flag is to
prevent programs which expect full SEIZE behavior from using it before
all the behavior modifications are complete while allowing unit
testing. The flag will be removed once SEIZE behaviors are completely
implemented.
* PTRACE_SEIZE, unlike ATTACH, doesn't force tracee to trap. After
attaching tracee continues to run unless a trap condition occurs.
* PTRACE_SEIZE doesn't affect signal or group stop state.
* If PTRACE_SEIZE'd, group stop uses PTRACE_EVENT_STOP trap which uses
exit_code of (signr | PTRACE_EVENT_STOP << 8) where signr is one of
the stopping signals if group stop is in effect or SIGTRAP
otherwise, and returns usual trap siginfo on PTRACE_GETSIGINFO
instead of NULL.
Seizing sets PT_SEIZED in ->ptrace of the tracee. This flag will be
used to determine whether new SEIZE behaviors should be enabled.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive\n");
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
return 0;
}
When the above program is called w/o argument, tracee is seized while
running and remains running. When tracer exits, tracee continues to
run and print out messages.
# ./test-seize-simple
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
tracee: alive
tracee: alive
When called with an argument, tracee is seized from stopped state and
continued, and returns to stopped state when tracer exits.
# ./test-seize
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
# ps -el|grep test-seize
1 T 0 4720 1 0 80 0 - 941 signal ttyS0 00:00:00 test-seize
-v2: SEIZE doesn't schedule TRAP_STOP and leaves tracee running as Jan
suggested.
-v3: PTRACE_EVENT_STOP traps now report group stop state by signr. If
group stop is in effect the stop signal number is returned as
part of exit_code; otherwise, SIGTRAP. This was suggested by
Denys and Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
Cc: Denys Vlasenko <vda.linux@googlemail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:15 +08:00
|
|
|
static int ptrace_attach(struct task_struct *task, long request,
|
2012-03-24 06:02:42 +08:00
|
|
|
unsigned long addr,
|
ptrace: implement PTRACE_SEIZE
PTRACE_ATTACH implicitly issues SIGSTOP on attach which has side
effects on tracee signal and job control states. This patch
implements a new ptrace request PTRACE_SEIZE which attaches a tracee
without trapping it or affecting its signal and job control states.
The usage is the same with PTRACE_ATTACH but it takes PTRACE_SEIZE_*
flags in @data. Currently, the only defined flag is
PTRACE_SEIZE_DEVEL which is a temporary flag to enable PTRACE_SEIZE.
PTRACE_SEIZE will change ptrace behaviors outside of attach itself.
The changes will be implemented gradually and the DEVEL flag is to
prevent programs which expect full SEIZE behavior from using it before
all the behavior modifications are complete while allowing unit
testing. The flag will be removed once SEIZE behaviors are completely
implemented.
* PTRACE_SEIZE, unlike ATTACH, doesn't force tracee to trap. After
attaching tracee continues to run unless a trap condition occurs.
* PTRACE_SEIZE doesn't affect signal or group stop state.
* If PTRACE_SEIZE'd, group stop uses PTRACE_EVENT_STOP trap which uses
exit_code of (signr | PTRACE_EVENT_STOP << 8) where signr is one of
the stopping signals if group stop is in effect or SIGTRAP
otherwise, and returns usual trap siginfo on PTRACE_GETSIGINFO
instead of NULL.
Seizing sets PT_SEIZED in ->ptrace of the tracee. This flag will be
used to determine whether new SEIZE behaviors should be enabled.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive\n");
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
return 0;
}
When the above program is called w/o argument, tracee is seized while
running and remains running. When tracer exits, tracee continues to
run and print out messages.
# ./test-seize-simple
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
tracee: alive
tracee: alive
When called with an argument, tracee is seized from stopped state and
continued, and returns to stopped state when tracer exits.
# ./test-seize
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
# ps -el|grep test-seize
1 T 0 4720 1 0 80 0 - 941 signal ttyS0 00:00:00 test-seize
-v2: SEIZE doesn't schedule TRAP_STOP and leaves tracee running as Jan
suggested.
-v3: PTRACE_EVENT_STOP traps now report group stop state by signr. If
group stop is in effect the stop signal number is returned as
part of exit_code; otherwise, SIGTRAP. This was suggested by
Denys and Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
Cc: Denys Vlasenko <vda.linux@googlemail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:15 +08:00
|
|
|
unsigned long flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
ptrace: implement PTRACE_SEIZE
PTRACE_ATTACH implicitly issues SIGSTOP on attach which has side
effects on tracee signal and job control states. This patch
implements a new ptrace request PTRACE_SEIZE which attaches a tracee
without trapping it or affecting its signal and job control states.
The usage is the same with PTRACE_ATTACH but it takes PTRACE_SEIZE_*
flags in @data. Currently, the only defined flag is
PTRACE_SEIZE_DEVEL which is a temporary flag to enable PTRACE_SEIZE.
PTRACE_SEIZE will change ptrace behaviors outside of attach itself.
The changes will be implemented gradually and the DEVEL flag is to
prevent programs which expect full SEIZE behavior from using it before
all the behavior modifications are complete while allowing unit
testing. The flag will be removed once SEIZE behaviors are completely
implemented.
* PTRACE_SEIZE, unlike ATTACH, doesn't force tracee to trap. After
attaching tracee continues to run unless a trap condition occurs.
* PTRACE_SEIZE doesn't affect signal or group stop state.
* If PTRACE_SEIZE'd, group stop uses PTRACE_EVENT_STOP trap which uses
exit_code of (signr | PTRACE_EVENT_STOP << 8) where signr is one of
the stopping signals if group stop is in effect or SIGTRAP
otherwise, and returns usual trap siginfo on PTRACE_GETSIGINFO
instead of NULL.
Seizing sets PT_SEIZED in ->ptrace of the tracee. This flag will be
used to determine whether new SEIZE behaviors should be enabled.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive\n");
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
return 0;
}
When the above program is called w/o argument, tracee is seized while
running and remains running. When tracer exits, tracee continues to
run and print out messages.
# ./test-seize-simple
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
tracee: alive
tracee: alive
When called with an argument, tracee is seized from stopped state and
continued, and returns to stopped state when tracer exits.
# ./test-seize
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
# ps -el|grep test-seize
1 T 0 4720 1 0 80 0 - 941 signal ttyS0 00:00:00 test-seize
-v2: SEIZE doesn't schedule TRAP_STOP and leaves tracee running as Jan
suggested.
-v3: PTRACE_EVENT_STOP traps now report group stop state by signr. If
group stop is in effect the stop signal number is returned as
part of exit_code; otherwise, SIGTRAP. This was suggested by
Denys and Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
Cc: Denys Vlasenko <vda.linux@googlemail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:15 +08:00
|
|
|
bool seize = (request == PTRACE_SEIZE);
|
2005-04-17 06:20:36 +08:00
|
|
|
int retval;
|
2006-05-08 01:49:33 +08:00
|
|
|
|
ptrace: implement PTRACE_SEIZE
PTRACE_ATTACH implicitly issues SIGSTOP on attach which has side
effects on tracee signal and job control states. This patch
implements a new ptrace request PTRACE_SEIZE which attaches a tracee
without trapping it or affecting its signal and job control states.
The usage is the same with PTRACE_ATTACH but it takes PTRACE_SEIZE_*
flags in @data. Currently, the only defined flag is
PTRACE_SEIZE_DEVEL which is a temporary flag to enable PTRACE_SEIZE.
PTRACE_SEIZE will change ptrace behaviors outside of attach itself.
The changes will be implemented gradually and the DEVEL flag is to
prevent programs which expect full SEIZE behavior from using it before
all the behavior modifications are complete while allowing unit
testing. The flag will be removed once SEIZE behaviors are completely
implemented.
* PTRACE_SEIZE, unlike ATTACH, doesn't force tracee to trap. After
attaching tracee continues to run unless a trap condition occurs.
* PTRACE_SEIZE doesn't affect signal or group stop state.
* If PTRACE_SEIZE'd, group stop uses PTRACE_EVENT_STOP trap which uses
exit_code of (signr | PTRACE_EVENT_STOP << 8) where signr is one of
the stopping signals if group stop is in effect or SIGTRAP
otherwise, and returns usual trap siginfo on PTRACE_GETSIGINFO
instead of NULL.
Seizing sets PT_SEIZED in ->ptrace of the tracee. This flag will be
used to determine whether new SEIZE behaviors should be enabled.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive\n");
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
return 0;
}
When the above program is called w/o argument, tracee is seized while
running and remains running. When tracer exits, tracee continues to
run and print out messages.
# ./test-seize-simple
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
tracee: alive
tracee: alive
When called with an argument, tracee is seized from stopped state and
continued, and returns to stopped state when tracer exits.
# ./test-seize
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
# ps -el|grep test-seize
1 T 0 4720 1 0 80 0 - 941 signal ttyS0 00:00:00 test-seize
-v2: SEIZE doesn't schedule TRAP_STOP and leaves tracee running as Jan
suggested.
-v3: PTRACE_EVENT_STOP traps now report group stop state by signr. If
group stop is in effect the stop signal number is returned as
part of exit_code; otherwise, SIGTRAP. This was suggested by
Denys and Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
Cc: Denys Vlasenko <vda.linux@googlemail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:15 +08:00
|
|
|
retval = -EIO;
|
2012-03-24 06:02:42 +08:00
|
|
|
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;
|
|
|
|
}
|
ptrace: implement PTRACE_SEIZE
PTRACE_ATTACH implicitly issues SIGSTOP on attach which has side
effects on tracee signal and job control states. This patch
implements a new ptrace request PTRACE_SEIZE which attaches a tracee
without trapping it or affecting its signal and job control states.
The usage is the same with PTRACE_ATTACH but it takes PTRACE_SEIZE_*
flags in @data. Currently, the only defined flag is
PTRACE_SEIZE_DEVEL which is a temporary flag to enable PTRACE_SEIZE.
PTRACE_SEIZE will change ptrace behaviors outside of attach itself.
The changes will be implemented gradually and the DEVEL flag is to
prevent programs which expect full SEIZE behavior from using it before
all the behavior modifications are complete while allowing unit
testing. The flag will be removed once SEIZE behaviors are completely
implemented.
* PTRACE_SEIZE, unlike ATTACH, doesn't force tracee to trap. After
attaching tracee continues to run unless a trap condition occurs.
* PTRACE_SEIZE doesn't affect signal or group stop state.
* If PTRACE_SEIZE'd, group stop uses PTRACE_EVENT_STOP trap which uses
exit_code of (signr | PTRACE_EVENT_STOP << 8) where signr is one of
the stopping signals if group stop is in effect or SIGTRAP
otherwise, and returns usual trap siginfo on PTRACE_GETSIGINFO
instead of NULL.
Seizing sets PT_SEIZED in ->ptrace of the tracee. This flag will be
used to determine whether new SEIZE behaviors should be enabled.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive\n");
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
return 0;
}
When the above program is called w/o argument, tracee is seized while
running and remains running. When tracer exits, tracee continues to
run and print out messages.
# ./test-seize-simple
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
tracee: alive
tracee: alive
When called with an argument, tracee is seized from stopped state and
continued, and returns to stopped state when tracer exits.
# ./test-seize
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
# ps -el|grep test-seize
1 T 0 4720 1 0 80 0 - 941 signal ttyS0 00:00:00 test-seize
-v2: SEIZE doesn't schedule TRAP_STOP and leaves tracee running as Jan
suggested.
-v3: PTRACE_EVENT_STOP traps now report group stop state by signr. If
group stop is in effect the stop signal number is returned as
part of exit_code; otherwise, SIGTRAP. This was suggested by
Denys and Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
Cc: Denys Vlasenko <vda.linux@googlemail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:15 +08:00
|
|
|
|
2007-03-21 01:58:35 +08:00
|
|
|
audit_ptrace(task);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
retval = -EPERM;
|
2009-06-18 07:27:31 +08:00
|
|
|
if (unlikely(task->flags & PF_KTHREAD))
|
|
|
|
goto out;
|
2007-10-19 14:40:18 +08:00
|
|
|
if (same_thread_group(task, current))
|
2006-05-08 01:49:33 +08:00
|
|
|
goto out;
|
|
|
|
|
2009-06-18 07:27:32 +08:00
|
|
|
/*
|
|
|
|
* Protect exec's credential calculations against our interference;
|
2012-03-24 06:02:41 +08:00
|
|
|
* SUID, SGID and LSM creds get determined differently
|
2009-05-08 20:55:22 +08:00
|
|
|
* under ptrace.
|
CRED: Inaugurate COW credentials
Inaugurate copy-on-write credentials management. This uses RCU to manage the
credentials pointer in the task_struct with respect to accesses by other tasks.
A process may only modify its own credentials, and so does not need locking to
access or modify its own credentials.
A mutex (cred_replace_mutex) is added to the task_struct to control the effect
of PTRACE_ATTACHED on credential calculations, particularly with respect to
execve().
With this patch, the contents of an active credentials struct may not be
changed directly; rather a new set of credentials must be prepared, modified
and committed using something like the following sequence of events:
struct cred *new = prepare_creds();
int ret = blah(new);
if (ret < 0) {
abort_creds(new);
return ret;
}
return commit_creds(new);
There are some exceptions to this rule: the keyrings pointed to by the active
credentials may be instantiated - keyrings violate the COW rule as managing
COW keyrings is tricky, given that it is possible for a task to directly alter
the keys in a keyring in use by another task.
To help enforce this, various pointers to sets of credentials, such as those in
the task_struct, are declared const. The purpose of this is compile-time
discouragement of altering credentials through those pointers. Once a set of
credentials has been made public through one of these pointers, it may not be
modified, except under special circumstances:
(1) Its reference count may incremented and decremented.
(2) The keyrings to which it points may be modified, but not replaced.
The only safe way to modify anything else is to create a replacement and commit
using the functions described in Documentation/credentials.txt (which will be
added by a later patch).
This patch and the preceding patches have been tested with the LTP SELinux
testsuite.
This patch makes several logical sets of alteration:
(1) execve().
This now prepares and commits credentials in various places in the
security code rather than altering the current creds directly.
(2) Temporary credential overrides.
do_coredump() and sys_faccessat() now prepare their own credentials and
temporarily override the ones currently on the acting thread, whilst
preventing interference from other threads by holding cred_replace_mutex
on the thread being dumped.
This will be replaced in a future patch by something that hands down the
credentials directly to the functions being called, rather than altering
the task's objective credentials.
(3) LSM interface.
A number of functions have been changed, added or removed:
(*) security_capset_check(), ->capset_check()
(*) security_capset_set(), ->capset_set()
Removed in favour of security_capset().
(*) security_capset(), ->capset()
New. This is passed a pointer to the new creds, a pointer to the old
creds and the proposed capability sets. It should fill in the new
creds or return an error. All pointers, barring the pointer to the
new creds, are now const.
(*) security_bprm_apply_creds(), ->bprm_apply_creds()
Changed; now returns a value, which will cause the process to be
killed if it's an error.
(*) security_task_alloc(), ->task_alloc_security()
Removed in favour of security_prepare_creds().
(*) security_cred_free(), ->cred_free()
New. Free security data attached to cred->security.
(*) security_prepare_creds(), ->cred_prepare()
New. Duplicate any security data attached to cred->security.
(*) security_commit_creds(), ->cred_commit()
New. Apply any security effects for the upcoming installation of new
security by commit_creds().
(*) security_task_post_setuid(), ->task_post_setuid()
Removed in favour of security_task_fix_setuid().
(*) security_task_fix_setuid(), ->task_fix_setuid()
Fix up the proposed new credentials for setuid(). This is used by
cap_set_fix_setuid() to implicitly adjust capabilities in line with
setuid() changes. Changes are made to the new credentials, rather
than the task itself as in security_task_post_setuid().
(*) security_task_reparent_to_init(), ->task_reparent_to_init()
Removed. Instead the task being reparented to init is referred
directly to init's credentials.
NOTE! This results in the loss of some state: SELinux's osid no
longer records the sid of the thread that forked it.
(*) security_key_alloc(), ->key_alloc()
(*) security_key_permission(), ->key_permission()
Changed. These now take cred pointers rather than task pointers to
refer to the security context.
(4) sys_capset().
This has been simplified and uses less locking. The LSM functions it
calls have been merged.
(5) reparent_to_kthreadd().
This gives the current thread the same credentials as init by simply using
commit_thread() to point that way.
(6) __sigqueue_alloc() and switch_uid()
__sigqueue_alloc() can't stop the target task from changing its creds
beneath it, so this function gets a reference to the currently applicable
user_struct which it then passes into the sigqueue struct it returns if
successful.
switch_uid() is now called from commit_creds(), and possibly should be
folded into that. commit_creds() should take care of protecting
__sigqueue_alloc().
(7) [sg]et[ug]id() and co and [sg]et_current_groups.
The set functions now all use prepare_creds(), commit_creds() and
abort_creds() to build and check a new set of credentials before applying
it.
security_task_set[ug]id() is called inside the prepared section. This
guarantees that nothing else will affect the creds until we've finished.
The calling of set_dumpable() has been moved into commit_creds().
Much of the functionality of set_user() has been moved into
commit_creds().
The get functions all simply access the data directly.
(8) security_task_prctl() and cap_task_prctl().
security_task_prctl() has been modified to return -ENOSYS if it doesn't
want to handle a function, or otherwise return the return value directly
rather than through an argument.
Additionally, cap_task_prctl() now prepares a new set of credentials, even
if it doesn't end up using it.
(9) Keyrings.
A number of changes have been made to the keyrings code:
(a) switch_uid_keyring(), copy_keys(), exit_keys() and suid_keys() have
all been dropped and built in to the credentials functions directly.
They may want separating out again later.
(b) key_alloc() and search_process_keyrings() now take a cred pointer
rather than a task pointer to specify the security context.
(c) copy_creds() gives a new thread within the same thread group a new
thread keyring if its parent had one, otherwise it discards the thread
keyring.
(d) The authorisation key now points directly to the credentials to extend
the search into rather pointing to the task that carries them.
(e) Installing thread, process or session keyrings causes a new set of
credentials to be created, even though it's not strictly necessary for
process or session keyrings (they're shared).
(10) Usermode helper.
The usermode helper code now carries a cred struct pointer in its
subprocess_info struct instead of a new session keyring pointer. This set
of credentials is derived from init_cred and installed on the new process
after it has been cloned.
call_usermodehelper_setup() allocates the new credentials and
call_usermodehelper_freeinfo() discards them if they haven't been used. A
special cred function (prepare_usermodeinfo_creds()) is provided
specifically for call_usermodehelper_setup() to call.
call_usermodehelper_setkeys() adjusts the credentials to sport the
supplied keyring as the new session keyring.
(11) SELinux.
SELinux has a number of changes, in addition to those to support the LSM
interface changes mentioned above:
(a) selinux_setprocattr() no longer does its check for whether the
current ptracer can access processes with the new SID inside the lock
that covers getting the ptracer's SID. Whilst this lock ensures that
the check is done with the ptracer pinned, the result is only valid
until the lock is released, so there's no point doing it inside the
lock.
(12) is_single_threaded().
This function has been extracted from selinux_setprocattr() and put into
a file of its own in the lib/ directory as join_session_keyring() now
wants to use it too.
The code in SELinux just checked to see whether a task shared mm_structs
with other tasks (CLONE_VM), but that isn't good enough. We really want
to know if they're part of the same thread group (CLONE_THREAD).
(13) nfsd.
The NFS server daemon now has to use the COW credentials to set the
credentials it is going to use. It really needs to pass the credentials
down to the functions it calls, but it can't do that until other patches
in this series have been applied.
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: James Morris <jmorris@namei.org>
Signed-off-by: James Morris <jmorris@namei.org>
2008-11-14 07:39:23 +08:00
|
|
|
*/
|
2009-07-06 03:08:26 +08:00
|
|
|
retval = -ERESTARTNOINTR;
|
2010-10-28 06:34:08 +08:00
|
|
|
if (mutex_lock_interruptible(&task->signal->cred_guard_mutex))
|
CRED: Inaugurate COW credentials
Inaugurate copy-on-write credentials management. This uses RCU to manage the
credentials pointer in the task_struct with respect to accesses by other tasks.
A process may only modify its own credentials, and so does not need locking to
access or modify its own credentials.
A mutex (cred_replace_mutex) is added to the task_struct to control the effect
of PTRACE_ATTACHED on credential calculations, particularly with respect to
execve().
With this patch, the contents of an active credentials struct may not be
changed directly; rather a new set of credentials must be prepared, modified
and committed using something like the following sequence of events:
struct cred *new = prepare_creds();
int ret = blah(new);
if (ret < 0) {
abort_creds(new);
return ret;
}
return commit_creds(new);
There are some exceptions to this rule: the keyrings pointed to by the active
credentials may be instantiated - keyrings violate the COW rule as managing
COW keyrings is tricky, given that it is possible for a task to directly alter
the keys in a keyring in use by another task.
To help enforce this, various pointers to sets of credentials, such as those in
the task_struct, are declared const. The purpose of this is compile-time
discouragement of altering credentials through those pointers. Once a set of
credentials has been made public through one of these pointers, it may not be
modified, except under special circumstances:
(1) Its reference count may incremented and decremented.
(2) The keyrings to which it points may be modified, but not replaced.
The only safe way to modify anything else is to create a replacement and commit
using the functions described in Documentation/credentials.txt (which will be
added by a later patch).
This patch and the preceding patches have been tested with the LTP SELinux
testsuite.
This patch makes several logical sets of alteration:
(1) execve().
This now prepares and commits credentials in various places in the
security code rather than altering the current creds directly.
(2) Temporary credential overrides.
do_coredump() and sys_faccessat() now prepare their own credentials and
temporarily override the ones currently on the acting thread, whilst
preventing interference from other threads by holding cred_replace_mutex
on the thread being dumped.
This will be replaced in a future patch by something that hands down the
credentials directly to the functions being called, rather than altering
the task's objective credentials.
(3) LSM interface.
A number of functions have been changed, added or removed:
(*) security_capset_check(), ->capset_check()
(*) security_capset_set(), ->capset_set()
Removed in favour of security_capset().
(*) security_capset(), ->capset()
New. This is passed a pointer to the new creds, a pointer to the old
creds and the proposed capability sets. It should fill in the new
creds or return an error. All pointers, barring the pointer to the
new creds, are now const.
(*) security_bprm_apply_creds(), ->bprm_apply_creds()
Changed; now returns a value, which will cause the process to be
killed if it's an error.
(*) security_task_alloc(), ->task_alloc_security()
Removed in favour of security_prepare_creds().
(*) security_cred_free(), ->cred_free()
New. Free security data attached to cred->security.
(*) security_prepare_creds(), ->cred_prepare()
New. Duplicate any security data attached to cred->security.
(*) security_commit_creds(), ->cred_commit()
New. Apply any security effects for the upcoming installation of new
security by commit_creds().
(*) security_task_post_setuid(), ->task_post_setuid()
Removed in favour of security_task_fix_setuid().
(*) security_task_fix_setuid(), ->task_fix_setuid()
Fix up the proposed new credentials for setuid(). This is used by
cap_set_fix_setuid() to implicitly adjust capabilities in line with
setuid() changes. Changes are made to the new credentials, rather
than the task itself as in security_task_post_setuid().
(*) security_task_reparent_to_init(), ->task_reparent_to_init()
Removed. Instead the task being reparented to init is referred
directly to init's credentials.
NOTE! This results in the loss of some state: SELinux's osid no
longer records the sid of the thread that forked it.
(*) security_key_alloc(), ->key_alloc()
(*) security_key_permission(), ->key_permission()
Changed. These now take cred pointers rather than task pointers to
refer to the security context.
(4) sys_capset().
This has been simplified and uses less locking. The LSM functions it
calls have been merged.
(5) reparent_to_kthreadd().
This gives the current thread the same credentials as init by simply using
commit_thread() to point that way.
(6) __sigqueue_alloc() and switch_uid()
__sigqueue_alloc() can't stop the target task from changing its creds
beneath it, so this function gets a reference to the currently applicable
user_struct which it then passes into the sigqueue struct it returns if
successful.
switch_uid() is now called from commit_creds(), and possibly should be
folded into that. commit_creds() should take care of protecting
__sigqueue_alloc().
(7) [sg]et[ug]id() and co and [sg]et_current_groups.
The set functions now all use prepare_creds(), commit_creds() and
abort_creds() to build and check a new set of credentials before applying
it.
security_task_set[ug]id() is called inside the prepared section. This
guarantees that nothing else will affect the creds until we've finished.
The calling of set_dumpable() has been moved into commit_creds().
Much of the functionality of set_user() has been moved into
commit_creds().
The get functions all simply access the data directly.
(8) security_task_prctl() and cap_task_prctl().
security_task_prctl() has been modified to return -ENOSYS if it doesn't
want to handle a function, or otherwise return the return value directly
rather than through an argument.
Additionally, cap_task_prctl() now prepares a new set of credentials, even
if it doesn't end up using it.
(9) Keyrings.
A number of changes have been made to the keyrings code:
(a) switch_uid_keyring(), copy_keys(), exit_keys() and suid_keys() have
all been dropped and built in to the credentials functions directly.
They may want separating out again later.
(b) key_alloc() and search_process_keyrings() now take a cred pointer
rather than a task pointer to specify the security context.
(c) copy_creds() gives a new thread within the same thread group a new
thread keyring if its parent had one, otherwise it discards the thread
keyring.
(d) The authorisation key now points directly to the credentials to extend
the search into rather pointing to the task that carries them.
(e) Installing thread, process or session keyrings causes a new set of
credentials to be created, even though it's not strictly necessary for
process or session keyrings (they're shared).
(10) Usermode helper.
The usermode helper code now carries a cred struct pointer in its
subprocess_info struct instead of a new session keyring pointer. This set
of credentials is derived from init_cred and installed on the new process
after it has been cloned.
call_usermodehelper_setup() allocates the new credentials and
call_usermodehelper_freeinfo() discards them if they haven't been used. A
special cred function (prepare_usermodeinfo_creds()) is provided
specifically for call_usermodehelper_setup() to call.
call_usermodehelper_setkeys() adjusts the credentials to sport the
supplied keyring as the new session keyring.
(11) SELinux.
SELinux has a number of changes, in addition to those to support the LSM
interface changes mentioned above:
(a) selinux_setprocattr() no longer does its check for whether the
current ptracer can access processes with the new SID inside the lock
that covers getting the ptracer's SID. Whilst this lock ensures that
the check is done with the ptracer pinned, the result is only valid
until the lock is released, so there's no point doing it inside the
lock.
(12) is_single_threaded().
This function has been extracted from selinux_setprocattr() and put into
a file of its own in the lib/ directory as join_session_keyring() now
wants to use it too.
The code in SELinux just checked to see whether a task shared mm_structs
with other tasks (CLONE_VM), but that isn't good enough. We really want
to know if they're part of the same thread group (CLONE_THREAD).
(13) nfsd.
The NFS server daemon now has to use the COW credentials to set the
credentials it is going to use. It really needs to pass the credentials
down to the functions it calls, but it can't do that until other patches
in this series have been applied.
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: James Morris <jmorris@namei.org>
Signed-off-by: James Morris <jmorris@namei.org>
2008-11-14 07:39:23 +08:00
|
|
|
goto out;
|
2006-05-08 01:49:33 +08:00
|
|
|
|
2009-06-18 07:27:33 +08:00
|
|
|
task_lock(task);
|
Security: split proc ptrace checking into read vs. attach
Enable security modules to distinguish reading of process state via
proc from full ptrace access by renaming ptrace_may_attach to
ptrace_may_access and adding a mode argument indicating whether only
read access or full attach access is requested. This allows security
modules to permit access to reading process state without granting
full ptrace access. The base DAC/capability checking remains unchanged.
Read access to /proc/pid/mem continues to apply a full ptrace attach
check since check_mem_permission() already requires the current task
to already be ptracing the target. The other ptrace checks within
proc for elements like environ, maps, and fds are changed to pass the
read mode instead of attach.
In the SELinux case, we model such reading of process state as a
reading of a proc file labeled with the target process' label. This
enables SELinux policy to permit such reading of process state without
permitting control or manipulation of the target process, as there are
a number of cases where programs probe for such information via proc
but do not need to be able to control the target (e.g. procps,
lsof, PolicyKit, ConsoleKit). At present we have to choose between
allowing full ptrace in policy (more permissive than required/desired)
or breaking functionality (or in some cases just silencing the denials
via dontaudit rules but this can hide genuine attacks).
This version of the patch incorporates comments from Casey Schaufler
(change/replace existing ptrace_may_attach interface, pass access
mode), and Chris Wright (provide greater consistency in the checking).
Note that like their predecessors __ptrace_may_attach and
ptrace_may_attach, the __ptrace_may_access and ptrace_may_access
interfaces use different return value conventions from each other (0
or -errno vs. 1 or 0). I retained this difference to avoid any
changes to the caller logic but made the difference clearer by
changing the latter interface to return a bool rather than an int and
by adding a comment about it to ptrace.h for any future callers.
Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
Acked-by: Chris Wright <chrisw@sous-sol.org>
Signed-off-by: James Morris <jmorris@namei.org>
2008-05-19 20:32:49 +08:00
|
|
|
retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
|
2009-06-18 07:27:33 +08:00
|
|
|
task_unlock(task);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (retval)
|
2009-06-18 07:27:33 +08:00
|
|
|
goto unlock_creds;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-06-18 07:27:33 +08:00
|
|
|
write_lock_irq(&tasklist_lock);
|
2009-06-18 07:27:31 +08:00
|
|
|
retval = -EPERM;
|
|
|
|
if (unlikely(task->exit_state))
|
2009-06-18 07:27:33 +08:00
|
|
|
goto unlock_tasklist;
|
2009-06-18 07:27:32 +08:00
|
|
|
if (task->ptrace)
|
2009-06-18 07:27:33 +08:00
|
|
|
goto unlock_tasklist;
|
2009-06-18 07:27:31 +08:00
|
|
|
|
ptrace: implement PTRACE_SEIZE
PTRACE_ATTACH implicitly issues SIGSTOP on attach which has side
effects on tracee signal and job control states. This patch
implements a new ptrace request PTRACE_SEIZE which attaches a tracee
without trapping it or affecting its signal and job control states.
The usage is the same with PTRACE_ATTACH but it takes PTRACE_SEIZE_*
flags in @data. Currently, the only defined flag is
PTRACE_SEIZE_DEVEL which is a temporary flag to enable PTRACE_SEIZE.
PTRACE_SEIZE will change ptrace behaviors outside of attach itself.
The changes will be implemented gradually and the DEVEL flag is to
prevent programs which expect full SEIZE behavior from using it before
all the behavior modifications are complete while allowing unit
testing. The flag will be removed once SEIZE behaviors are completely
implemented.
* PTRACE_SEIZE, unlike ATTACH, doesn't force tracee to trap. After
attaching tracee continues to run unless a trap condition occurs.
* PTRACE_SEIZE doesn't affect signal or group stop state.
* If PTRACE_SEIZE'd, group stop uses PTRACE_EVENT_STOP trap which uses
exit_code of (signr | PTRACE_EVENT_STOP << 8) where signr is one of
the stopping signals if group stop is in effect or SIGTRAP
otherwise, and returns usual trap siginfo on PTRACE_GETSIGINFO
instead of NULL.
Seizing sets PT_SEIZED in ->ptrace of the tracee. This flag will be
used to determine whether new SEIZE behaviors should be enabled.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive\n");
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
return 0;
}
When the above program is called w/o argument, tracee is seized while
running and remains running. When tracer exits, tracee continues to
run and print out messages.
# ./test-seize-simple
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
tracee: alive
tracee: alive
When called with an argument, tracee is seized from stopped state and
continued, and returns to stopped state when tracer exits.
# ./test-seize
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
# ps -el|grep test-seize
1 T 0 4720 1 0 80 0 - 941 signal ttyS0 00:00:00 test-seize
-v2: SEIZE doesn't schedule TRAP_STOP and leaves tracee running as Jan
suggested.
-v3: PTRACE_EVENT_STOP traps now report group stop state by signr. If
group stop is in effect the stop signal number is returned as
part of exit_code; otherwise, SIGTRAP. This was suggested by
Denys and Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
Cc: Denys Vlasenko <vda.linux@googlemail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:15 +08:00
|
|
|
if (seize)
|
2012-03-24 06:02:42 +08:00
|
|
|
flags |= PT_SEIZED;
|
2012-07-26 20:05:21 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
if (ns_capable(__task_cred(task)->user_ns, CAP_SYS_PTRACE))
|
2012-03-24 06:02:42 +08:00
|
|
|
flags |= PT_PTRACE_CAP;
|
2012-07-26 20:05:21 +08:00
|
|
|
rcu_read_unlock();
|
2012-03-24 06:02:42 +08:00
|
|
|
task->ptrace = flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
__ptrace_link(task, current);
|
ptrace: implement PTRACE_SEIZE
PTRACE_ATTACH implicitly issues SIGSTOP on attach which has side
effects on tracee signal and job control states. This patch
implements a new ptrace request PTRACE_SEIZE which attaches a tracee
without trapping it or affecting its signal and job control states.
The usage is the same with PTRACE_ATTACH but it takes PTRACE_SEIZE_*
flags in @data. Currently, the only defined flag is
PTRACE_SEIZE_DEVEL which is a temporary flag to enable PTRACE_SEIZE.
PTRACE_SEIZE will change ptrace behaviors outside of attach itself.
The changes will be implemented gradually and the DEVEL flag is to
prevent programs which expect full SEIZE behavior from using it before
all the behavior modifications are complete while allowing unit
testing. The flag will be removed once SEIZE behaviors are completely
implemented.
* PTRACE_SEIZE, unlike ATTACH, doesn't force tracee to trap. After
attaching tracee continues to run unless a trap condition occurs.
* PTRACE_SEIZE doesn't affect signal or group stop state.
* If PTRACE_SEIZE'd, group stop uses PTRACE_EVENT_STOP trap which uses
exit_code of (signr | PTRACE_EVENT_STOP << 8) where signr is one of
the stopping signals if group stop is in effect or SIGTRAP
otherwise, and returns usual trap siginfo on PTRACE_GETSIGINFO
instead of NULL.
Seizing sets PT_SEIZED in ->ptrace of the tracee. This flag will be
used to determine whether new SEIZE behaviors should be enabled.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive\n");
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
return 0;
}
When the above program is called w/o argument, tracee is seized while
running and remains running. When tracer exits, tracee continues to
run and print out messages.
# ./test-seize-simple
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
tracee: alive
tracee: alive
When called with an argument, tracee is seized from stopped state and
continued, and returns to stopped state when tracer exits.
# ./test-seize
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
# ps -el|grep test-seize
1 T 0 4720 1 0 80 0 - 941 signal ttyS0 00:00:00 test-seize
-v2: SEIZE doesn't schedule TRAP_STOP and leaves tracee running as Jan
suggested.
-v3: PTRACE_EVENT_STOP traps now report group stop state by signr. If
group stop is in effect the stop signal number is returned as
part of exit_code; otherwise, SIGTRAP. This was suggested by
Denys and Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
Cc: Denys Vlasenko <vda.linux@googlemail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:15 +08:00
|
|
|
|
|
|
|
/* SEIZE doesn't trap tracee on attach */
|
|
|
|
if (!seize)
|
|
|
|
send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
|
2009-06-18 07:27:31 +08:00
|
|
|
|
2011-03-23 17:37:00 +08:00
|
|
|
spin_lock(&task->sighand->siglock);
|
|
|
|
|
|
|
|
/*
|
2011-06-14 17:20:14 +08:00
|
|
|
* If the task is already STOPPED, set JOBCTL_TRAP_STOP and
|
2011-03-23 17:37:00 +08:00
|
|
|
* 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.
|
|
|
|
*/
|
2011-06-02 17:14:00 +08:00
|
|
|
if (task_is_stopped(task) &&
|
2011-06-14 17:20:14 +08:00
|
|
|
task_set_jobctl_pending(task, JOBCTL_TRAP_STOP | JOBCTL_TRAPPING))
|
2013-01-22 03:47:41 +08:00
|
|
|
signal_wake_up_state(task, __TASK_STOPPED);
|
2011-03-23 17:37:00 +08:00
|
|
|
|
|
|
|
spin_unlock(&task->sighand->siglock);
|
|
|
|
|
2009-06-18 07:27:31 +08:00
|
|
|
retval = 0;
|
2009-06-18 07:27:33 +08:00
|
|
|
unlock_tasklist:
|
|
|
|
write_unlock_irq(&tasklist_lock);
|
|
|
|
unlock_creds:
|
2010-10-28 06:34:08 +08:00
|
|
|
mutex_unlock(&task->signal->cred_guard_mutex);
|
2006-05-08 01:49:33 +08:00
|
|
|
out:
|
2011-07-16 01:45:18 +08:00
|
|
|
if (!retval) {
|
2011-06-02 17:14:00 +08:00
|
|
|
wait_on_bit(&task->jobctl, JOBCTL_TRAPPING_BIT,
|
|
|
|
ptrace_trapping_sleep_fn, TASK_UNINTERRUPTIBLE);
|
2011-07-16 01:45:18 +08:00
|
|
|
proc_ptrace_connector(task, PTRACE_ATTACH);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2009-06-18 07:27:32 +08:00
|
|
|
/**
|
|
|
|
* ptrace_traceme -- helper for PTRACE_TRACEME
|
|
|
|
*
|
|
|
|
* Performs checks and sets PT_PTRACED.
|
|
|
|
* Should be used by all ptrace implementations for PTRACE_TRACEME.
|
|
|
|
*/
|
2011-03-05 01:23:30 +08:00
|
|
|
static int ptrace_traceme(void)
|
2009-06-18 07:27:32 +08:00
|
|
|
{
|
|
|
|
int ret = -EPERM;
|
|
|
|
|
2009-06-18 07:27:33 +08:00
|
|
|
write_lock_irq(&tasklist_lock);
|
|
|
|
/* Are we already being traced? */
|
2009-06-18 07:27:32 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2009-06-18 07:27:33 +08:00
|
|
|
write_unlock_irq(&tasklist_lock);
|
|
|
|
|
2009-06-18 07:27:32 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-04-03 07:58:18 +08: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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
ptrace: __ptrace_detach: do __wake_up_parent() if we reap the tracee
The bug is old, it wasn't cause by recent changes.
Test case:
static void *tfunc(void *arg)
{
int pid = (long)arg;
assert(ptrace(PTRACE_ATTACH, pid, NULL, NULL) == 0);
kill(pid, SIGKILL);
sleep(1);
return NULL;
}
int main(void)
{
pthread_t th;
long pid = fork();
if (!pid)
pause();
signal(SIGCHLD, SIG_IGN);
assert(pthread_create(&th, NULL, tfunc, (void*)pid) == 0);
int r = waitpid(-1, NULL, __WNOTHREAD);
printf("waitpid: %d %m\n", r);
return 0;
}
Before the patch this program hangs, after this patch waitpid() correctly
fails with errno == -ECHILD.
The problem is, __ptrace_detach() reaps the EXIT_ZOMBIE tracee if its
->real_parent is our sub-thread and we ignore SIGCHLD. But in this case
we should wake up other threads which can sleep in do_wait().
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Cc: Roland McGrath <roland@redhat.com>
Cc: Vitaly Mayatskikh <vmayatsk@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-09-24 06:56:44 +08:00
|
|
|
* 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().
|
2009-04-03 07:58:18 +08:00
|
|
|
*/
|
|
|
|
static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
|
|
|
|
{
|
2011-06-23 05:08:53 +08:00
|
|
|
bool dead;
|
|
|
|
|
2009-04-03 07:58:18 +08:00
|
|
|
__ptrace_unlink(p);
|
|
|
|
|
2011-06-23 05:08:53 +08:00
|
|
|
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;
|
2009-04-03 07:58:18 +08:00
|
|
|
}
|
|
|
|
}
|
2011-06-23 05:08:53 +08:00
|
|
|
/* Mark it as in the process of being reaped. */
|
|
|
|
if (dead)
|
|
|
|
p->exit_state = EXIT_DEAD;
|
|
|
|
return dead;
|
2009-04-03 07:58:18 +08:00
|
|
|
}
|
|
|
|
|
2011-03-05 01:23:30 +08:00
|
|
|
static int ptrace_detach(struct task_struct *child, unsigned int data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-04-03 07:58:18 +08:00
|
|
|
bool dead = false;
|
2009-04-03 07:58:14 +08:00
|
|
|
|
2005-05-01 23:59:14 +08:00
|
|
|
if (!valid_signal(data))
|
2006-02-16 03:50:10 +08:00
|
|
|
return -EIO;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Architecture-specific hardware disable .. */
|
|
|
|
ptrace_disable(child);
|
2007-09-05 18:05:56 +08:00
|
|
|
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
|
ptrace: PTRACE_DETACH should do flush_ptrace_hw_breakpoint(child)
Change ptrace_detach() to call flush_ptrace_hw_breakpoint(child). This
frees the slots for non-ptrace PERF_TYPE_BREAKPOINT users, and this
ensures that the tracee won't be killed by SIGTRAP triggered by the
active breakpoints.
Test-case:
unsigned long encode_dr7(int drnum, int enable, unsigned int type, unsigned int len)
{
unsigned long dr7;
dr7 = ((len | type) & 0xf)
<< (DR_CONTROL_SHIFT + drnum * DR_CONTROL_SIZE);
if (enable)
dr7 |= (DR_GLOBAL_ENABLE << (drnum * DR_ENABLE_SIZE));
return dr7;
}
int write_dr(int pid, int dr, unsigned long val)
{
return ptrace(PTRACE_POKEUSER, pid,
offsetof (struct user, u_debugreg[dr]),
val);
}
void func(void)
{
}
int main(void)
{
int pid, stat;
unsigned long dr7;
pid = fork();
if (!pid) {
assert(ptrace(PTRACE_TRACEME, 0,0,0) == 0);
kill(getpid(), SIGHUP);
func();
return 0x13;
}
assert(pid == waitpid(-1, &stat, 0));
assert(WSTOPSIG(stat) == SIGHUP);
assert(write_dr(pid, 0, (long)func) == 0);
dr7 = encode_dr7(0, 1, DR_RW_EXECUTE, DR_LEN_1);
assert(write_dr(pid, 7, dr7) == 0);
assert(ptrace(PTRACE_DETACH, pid, 0,0) == 0);
assert(pid == waitpid(-1, &stat, 0));
assert(stat == 0x1300);
return 0;
}
Before this patch the child is killed after PTRACE_DETACH.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
Cc: Michael Neuling <mikey@neuling.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Prasad <prasad@linux.vnet.ibm.com>
Cc: Russell King <linux@arm.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-07-09 07:01:05 +08:00
|
|
|
flush_ptrace_hw_breakpoint(child);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-04-03 07:58:11 +08:00
|
|
|
write_lock_irq(&tasklist_lock);
|
2009-04-03 07:58:18 +08:00
|
|
|
/*
|
|
|
|
* This child can be already killed. Make sure de_thread() or
|
|
|
|
* our sub-thread doing do_wait() didn't do release_task() yet.
|
|
|
|
*/
|
2009-04-03 07:58:11 +08:00
|
|
|
if (child->ptrace) {
|
|
|
|
child->exit_code = data;
|
2009-04-03 07:58:14 +08:00
|
|
|
dead = __ptrace_detach(current, child);
|
2009-04-03 07:58:11 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
write_unlock_irq(&tasklist_lock);
|
|
|
|
|
2011-07-16 01:45:18 +08:00
|
|
|
proc_ptrace_connector(child, PTRACE_DETACH);
|
2009-04-03 07:58:14 +08:00
|
|
|
if (unlikely(dead))
|
|
|
|
release_task(child);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-04-03 07:58:18 +08:00
|
|
|
/*
|
2010-08-11 09:03:07 +08:00
|
|
|
* Detach all tasks we were using ptrace on. Called with tasklist held
|
|
|
|
* for writing, and returns with it held too. But note it can release
|
|
|
|
* and reacquire the lock.
|
2009-04-03 07:58:18 +08:00
|
|
|
*/
|
|
|
|
void exit_ptrace(struct task_struct *tracer)
|
2010-10-28 06:33:44 +08:00
|
|
|
__releases(&tasklist_lock)
|
|
|
|
__acquires(&tasklist_lock)
|
2009-04-03 07:58:18 +08:00
|
|
|
{
|
|
|
|
struct task_struct *p, *n;
|
|
|
|
LIST_HEAD(ptrace_dead);
|
|
|
|
|
2010-08-11 09:03:07 +08:00
|
|
|
if (likely(list_empty(&tracer->ptraced)))
|
|
|
|
return;
|
|
|
|
|
2009-04-03 07:58:18 +08:00
|
|
|
list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) {
|
2012-12-18 08:03:07 +08:00
|
|
|
if (unlikely(p->ptrace & PT_EXITKILL))
|
|
|
|
send_sig_info(SIGKILL, SEND_SIG_FORCED, p);
|
|
|
|
|
2009-04-03 07:58:18 +08:00
|
|
|
if (__ptrace_detach(tracer, p))
|
|
|
|
list_add(&p->ptrace_entry, &ptrace_dead);
|
|
|
|
}
|
|
|
|
|
2010-08-11 09:03:07 +08:00
|
|
|
write_unlock_irq(&tasklist_lock);
|
2009-04-03 07:58:18 +08:00
|
|
|
BUG_ON(!list_empty(&tracer->ptraced));
|
|
|
|
|
|
|
|
list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) {
|
|
|
|
list_del_init(&p->ptrace_entry);
|
|
|
|
release_task(p);
|
|
|
|
}
|
2010-08-11 09:03:07 +08:00
|
|
|
|
|
|
|
write_lock_irq(&tasklist_lock);
|
2009-04-03 07:58:18 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08: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 = 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;
|
2009-04-08 14:21:06 +08:00
|
|
|
len -= retval;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
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;
|
2009-04-08 14:21:06 +08:00
|
|
|
len -= retval;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
return copied;
|
|
|
|
}
|
|
|
|
|
2010-10-28 06:33:45 +08:00
|
|
|
static int ptrace_setoptions(struct task_struct *child, unsigned long data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2012-03-24 06:02:41 +08:00
|
|
|
unsigned flags;
|
|
|
|
|
ptrace: don't modify flags on PTRACE_SETOPTIONS failure
On ptrace(PTRACE_SETOPTIONS, pid, 0, <opts>), we used to set those
option bits which are known, and then fail with -EINVAL if there are
some unknown bits in <opts>.
This is inconsistent with typical error handling, which does not change
any state if input is invalid.
This patch changes PTRACE_SETOPTIONS behavior so that in this case, we
return -EINVAL and don't change any bits in task->ptrace.
It's very unlikely that there is userspace code in the wild which will
be affected by this change: it should have the form
ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_BOGUSOPT)
where PTRACE_O_BOGUSOPT is a constant unknown to the kernel. But kernel
headers, naturally, don't contain any PTRACE_O_BOGUSOPTs, thus the only
way userspace can use one if it defines one itself. I can't see why
anyone would do such a thing deliberately.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Acked-by: Tejun Heo <tj@kernel.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Cc: Pedro Alves <palves@redhat.com>
Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-24 06:02:40 +08:00
|
|
|
if (data & ~(unsigned long)PTRACE_O_MASK)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2012-03-24 06:02:41 +08:00
|
|
|
/* 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;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
ptrace: don't modify flags on PTRACE_SETOPTIONS failure
On ptrace(PTRACE_SETOPTIONS, pid, 0, <opts>), we used to set those
option bits which are known, and then fail with -EINVAL if there are
some unknown bits in <opts>.
This is inconsistent with typical error handling, which does not change
any state if input is invalid.
This patch changes PTRACE_SETOPTIONS behavior so that in this case, we
return -EINVAL and don't change any bits in task->ptrace.
It's very unlikely that there is userspace code in the wild which will
be affected by this change: it should have the form
ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_BOGUSOPT)
where PTRACE_O_BOGUSOPT is a constant unknown to the kernel. But kernel
headers, naturally, don't contain any PTRACE_O_BOGUSOPTs, thus the only
way userspace can use one if it defines one itself. I can't see why
anyone would do such a thing deliberately.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Acked-by: Tejun Heo <tj@kernel.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Cc: Pedro Alves <palves@redhat.com>
Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-24 06:02:40 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-04-21 04:10:12 +08:00
|
|
|
static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-06-18 07:27:36 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
int error = -ESRCH;
|
|
|
|
|
2009-06-18 07:27:36 +08:00
|
|
|
if (lock_task_sighand(child, &flags)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
error = -EINVAL;
|
|
|
|
if (likely(child->last_siginfo != NULL)) {
|
2008-04-21 04:10:12 +08:00
|
|
|
*info = *child->last_siginfo;
|
2005-04-17 06:20:36 +08:00
|
|
|
error = 0;
|
|
|
|
}
|
2009-06-18 07:27:36 +08:00
|
|
|
unlock_task_sighand(child, &flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2008-04-21 04:10:12 +08:00
|
|
|
static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-06-18 07:27:36 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
int error = -ESRCH;
|
|
|
|
|
2009-06-18 07:27:36 +08:00
|
|
|
if (lock_task_sighand(child, &flags)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
error = -EINVAL;
|
|
|
|
if (likely(child->last_siginfo != NULL)) {
|
2008-04-21 04:10:12 +08:00
|
|
|
*child->last_siginfo = *info;
|
2005-04-17 06:20:36 +08:00
|
|
|
error = 0;
|
|
|
|
}
|
2009-06-18 07:27:36 +08:00
|
|
|
unlock_task_sighand(child, &flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2013-05-01 06:27:59 +08: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(is_compat_task())) {
|
|
|
|
compat_siginfo_t __user *uinfo = compat_ptr(data);
|
|
|
|
|
2013-06-28 21:49:46 +08:00
|
|
|
if (copy_siginfo_to_user32(uinfo, &info) ||
|
|
|
|
__put_user(info.si_code, &uinfo->si_code)) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-05-01 06:27:59 +08:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
siginfo_t __user *uinfo = (siginfo_t __user *) data;
|
|
|
|
|
2013-06-28 21:49:46 +08:00
|
|
|
if (copy_siginfo_to_user(uinfo, &info) ||
|
|
|
|
__put_user(info.si_code, &uinfo->si_code)) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
2013-05-01 06:27:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
data += sizeof(siginfo_t);
|
|
|
|
i++;
|
|
|
|
|
|
|
|
if (signal_pending(current))
|
|
|
|
break;
|
|
|
|
|
|
|
|
cond_resched();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i > 0)
|
|
|
|
return i;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2008-01-30 20:30:51 +08:00
|
|
|
|
|
|
|
#ifdef PTRACE_SINGLESTEP
|
|
|
|
#define is_singlestep(request) ((request) == PTRACE_SINGLESTEP)
|
|
|
|
#else
|
|
|
|
#define is_singlestep(request) 0
|
|
|
|
#endif
|
|
|
|
|
2008-01-30 20:30:53 +08:00
|
|
|
#ifdef PTRACE_SINGLEBLOCK
|
|
|
|
#define is_singleblock(request) ((request) == PTRACE_SINGLEBLOCK)
|
|
|
|
#else
|
|
|
|
#define is_singleblock(request) 0
|
|
|
|
#endif
|
|
|
|
|
2008-01-30 20:30:51 +08:00
|
|
|
#ifdef PTRACE_SYSEMU
|
|
|
|
#define is_sysemu_singlestep(request) ((request) == PTRACE_SYSEMU_SINGLESTEP)
|
|
|
|
#else
|
|
|
|
#define is_sysemu_singlestep(request) 0
|
|
|
|
#endif
|
|
|
|
|
2010-10-28 06:33:45 +08:00
|
|
|
static int ptrace_resume(struct task_struct *child, long request,
|
|
|
|
unsigned long data)
|
2008-01-30 20:30:51 +08:00
|
|
|
{
|
|
|
|
if (!valid_signal(data))
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
if (request == PTRACE_SYSCALL)
|
|
|
|
set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
|
|
|
|
else
|
|
|
|
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
|
|
|
|
|
|
|
|
#ifdef TIF_SYSCALL_EMU
|
|
|
|
if (request == PTRACE_SYSEMU || request == PTRACE_SYSEMU_SINGLESTEP)
|
|
|
|
set_tsk_thread_flag(child, TIF_SYSCALL_EMU);
|
|
|
|
else
|
|
|
|
clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
|
|
|
|
#endif
|
|
|
|
|
2008-01-30 20:30:53 +08:00
|
|
|
if (is_singleblock(request)) {
|
|
|
|
if (unlikely(!arch_has_block_step()))
|
|
|
|
return -EIO;
|
|
|
|
user_enable_block_step(child);
|
|
|
|
} else if (is_singlestep(request) || is_sysemu_singlestep(request)) {
|
2008-01-30 20:30:51 +08:00
|
|
|
if (unlikely(!arch_has_single_step()))
|
|
|
|
return -EIO;
|
|
|
|
user_enable_single_step(child);
|
2009-04-08 14:21:06 +08:00
|
|
|
} else {
|
2008-01-30 20:30:51 +08:00
|
|
|
user_disable_single_step(child);
|
2009-04-08 14:21:06 +08:00
|
|
|
}
|
2008-01-30 20:30:51 +08:00
|
|
|
|
|
|
|
child->exit_code = data;
|
ptrace: ptrace_resume() shouldn't wake up !TASK_TRACED thread
It is not clear why ptrace_resume() does wake_up_process(). Unless the
caller is PTRACE_KILL the tracee should be TASK_TRACED so we can use
wake_up_state(__TASK_TRACED). If sys_ptrace() races with SIGKILL we do
not need the extra and potentionally spurious wakeup.
If the caller is PTRACE_KILL, wake_up_process() is even more wrong.
The tracee can sleep in any state in any place, and if we have a buggy
code which doesn't handle a spurious wakeup correctly PTRACE_KILL can
be used to exploit it. For example:
int main(void)
{
int child, status;
child = fork();
if (!child) {
int ret;
assert(ptrace(PTRACE_TRACEME, 0,0,0) == 0);
ret = pause();
printf("pause: %d %m\n", ret);
return 0x23;
}
sleep(1);
assert(ptrace(PTRACE_KILL, child, 0,0) == 0);
assert(child == wait(&status));
printf("wait: %x\n", status);
return 0;
}
prints "pause: -1 Unknown error 514", -ERESTARTNOHAND leaks to the
userland. In this case sys_pause() is buggy as well and should be
fixed.
I do not know what was the original rationality behind PTRACE_KILL.
The man page is simply wrong and afaics it was always wrong. Imho
it should be deprecated, or may be it should do send_sig(SIGKILL)
as Denys suggests, but in any case I do not think that the current
behaviour was intentional.
Note: there is another problem, ptrace_resume() changes ->exit_code
and this can race with SIGKILL too. Eventually we should change ptrace
to not use ->exit_code.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
2011-05-26 01:20:21 +08:00
|
|
|
wake_up_state(child, __TASK_TRACED);
|
2008-01-30 20:30:51 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-02-12 03:51:00 +08:00
|
|
|
#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
|
|
|
|
|
|
|
|
static const struct user_regset *
|
|
|
|
find_regset(const struct user_regset_view *view, unsigned int type)
|
|
|
|
{
|
|
|
|
const struct user_regset *regset;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
for (n = 0; n < view->n; ++n) {
|
|
|
|
regset = view->regsets + n;
|
|
|
|
if (regset->core_note_type == type)
|
|
|
|
return regset;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ptrace_regset(struct task_struct *task, int req, unsigned int type,
|
|
|
|
struct iovec *kiov)
|
|
|
|
{
|
|
|
|
const struct user_regset_view *view = task_user_regset_view(task);
|
|
|
|
const struct user_regset *regset = find_regset(view, type);
|
|
|
|
int regset_no;
|
|
|
|
|
|
|
|
if (!regset || (kiov->iov_len % regset->size) != 0)
|
2010-02-23 06:51:32 +08:00
|
|
|
return -EINVAL;
|
2010-02-12 03:51:00 +08:00
|
|
|
|
|
|
|
regset_no = regset - view->regsets;
|
|
|
|
kiov->iov_len = min(kiov->iov_len,
|
|
|
|
(__kernel_size_t) (regset->n * regset->size));
|
|
|
|
|
|
|
|
if (req == PTRACE_GETREGSET)
|
|
|
|
return copy_regset_to_user(task, view, regset_no, 0,
|
|
|
|
kiov->iov_len, kiov->iov_base);
|
|
|
|
else
|
|
|
|
return copy_regset_from_user(task, view, regset_no, 0,
|
|
|
|
kiov->iov_len, kiov->iov_base);
|
|
|
|
}
|
|
|
|
|
2013-01-14 02:03:34 +08:00
|
|
|
/*
|
|
|
|
* 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);
|
2010-02-12 03:51:00 +08:00
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
int ptrace_request(struct task_struct *child, long request,
|
2010-10-28 06:33:45 +08:00
|
|
|
unsigned long addr, unsigned long data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
ptrace: implement PTRACE_INTERRUPT
Currently, there's no way to trap a running ptracee short of sending a
signal which has various side effects. This patch implements
PTRACE_INTERRUPT which traps ptracee without any signal or job control
related side effect.
The implementation is almost trivial. It uses the group stop trap -
SIGTRAP | PTRACE_EVENT_STOP << 8. A new trap flag
JOBCTL_TRAP_INTERRUPT is added, which is set on PTRACE_INTERRUPT and
cleared when any trap happens. As INTERRUPT should be useable
regardless of the current state of tracee, task_is_traced() test in
ptrace_check_attach() is skipped for INTERRUPT.
PTRACE_INTERRUPT is available iff tracee is attached with
PTRACE_SEIZE.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_INTERRUPT 0x4207
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive pid=%d\n", getpid());
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: INTERRUPT and DETACH\n");
ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL);
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_DETACH, tracee, NULL, NULL);
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
kill(tracee, SIGKILL);
return 0;
}
When called without argument, tracee is seized from running state,
interrupted and then detached back to running state.
# ./test-interrupt
tracee: alive pid=4546
tracee: alive pid=4546
tracee: alive pid=4546
tracer: INTERRUPT and DETACH
tracee: alive pid=4546
tracee: alive pid=4546
tracee: alive pid=4546
tracer: exiting
When called with argument, tracee is seized from stopped state,
continued, interrupted and then detached back to stopped state.
# ./test-interrupt 1
tracee: alive pid=4548
tracee: alive pid=4548
tracee: alive pid=4548
tracer: INTERRUPT and DETACH
tracer: exiting
Before PTRACE_INTERRUPT, once the tracee was running, there was no way
to trap tracee and do PTRACE_DETACH without causing side effect.
-v2: Updated to use task_set_jobctl_pending() so that it doesn't end
up scheduling TRAP_STOP if child is dying which may make the
child unkillable. Spotted by Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:16 +08:00
|
|
|
bool seized = child->ptrace & PT_SEIZED;
|
2005-04-17 06:20:36 +08:00
|
|
|
int ret = -EIO;
|
ptrace: implement PTRACE_LISTEN
The previous patch implemented async notification for ptrace but it
only worked while trace is running. This patch introduces
PTRACE_LISTEN which is suggested by Oleg Nestrov.
It's allowed iff tracee is in STOP trap and puts tracee into
quasi-running state - tracee never really runs but wait(2) and
ptrace(2) consider it to be running. While ptracer is listening,
tracee is allowed to re-enter STOP to notify an async event.
Listening state is cleared on the first notification. Ptracer can
also clear it by issuing INTERRUPT - tracee will re-trap into STOP
with listening state cleared.
This allows ptracer to monitor group stop state without running tracee
- use INTERRUPT to put tracee into STOP trap, issue LISTEN and then
wait(2) to wait for the next group stop event. When it happens,
PTRACE_GETSIGINFO provides information to determine the current state.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_INTERRUPT 0x4207
#define PTRACE_LISTEN 0x4208
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts1s = { .tv_sec = 1 };
int main(int argc, char **argv)
{
pid_t tracee, tracer;
int i;
tracee = fork();
if (!tracee)
while (1)
pause();
tracer = fork();
if (!tracer) {
siginfo_t si;
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL);
repeat:
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_GETSIGINFO, tracee, NULL, &si);
if (!si.si_code) {
printf("tracer: SIG %d\n", si.si_signo);
ptrace(PTRACE_CONT, tracee, NULL,
(void *)(unsigned long)si.si_signo);
goto repeat;
}
printf("tracer: stopped=%d signo=%d\n",
si.si_signo != SIGTRAP, si.si_signo);
if (si.si_signo != SIGTRAP)
ptrace(PTRACE_LISTEN, tracee, NULL, NULL);
else
ptrace(PTRACE_CONT, tracee, NULL, NULL);
goto repeat;
}
for (i = 0; i < 3; i++) {
nanosleep(&ts1s, NULL);
printf("mother: SIGSTOP\n");
kill(tracee, SIGSTOP);
nanosleep(&ts1s, NULL);
printf("mother: SIGCONT\n");
kill(tracee, SIGCONT);
}
nanosleep(&ts1s, NULL);
kill(tracer, SIGKILL);
kill(tracee, SIGKILL);
return 0;
}
This is identical to the program to test TRAP_NOTIFY except that
tracee is PTRACE_LISTEN'd instead of PTRACE_CONT'd when group stopped.
This allows ptracer to monitor when group stop ends without running
tracee.
# ./test-listen
tracer: stopped=0 signo=5
mother: SIGSTOP
tracer: SIG 19
tracer: stopped=1 signo=19
mother: SIGCONT
tracer: stopped=0 signo=5
tracer: SIG 18
mother: SIGSTOP
tracer: SIG 19
tracer: stopped=1 signo=19
mother: SIGCONT
tracer: stopped=0 signo=5
tracer: SIG 18
mother: SIGSTOP
tracer: SIG 19
tracer: stopped=1 signo=19
mother: SIGCONT
tracer: stopped=0 signo=5
tracer: SIG 18
-v2: Moved JOBCTL_LISTENING check in wait_task_stopped() into
task_stopped_code() as suggested by Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:18 +08:00
|
|
|
siginfo_t siginfo, *si;
|
2010-10-28 06:33:46 +08:00
|
|
|
void __user *datavp = (void __user *) data;
|
|
|
|
unsigned long __user *datalp = datavp;
|
ptrace: implement PTRACE_INTERRUPT
Currently, there's no way to trap a running ptracee short of sending a
signal which has various side effects. This patch implements
PTRACE_INTERRUPT which traps ptracee without any signal or job control
related side effect.
The implementation is almost trivial. It uses the group stop trap -
SIGTRAP | PTRACE_EVENT_STOP << 8. A new trap flag
JOBCTL_TRAP_INTERRUPT is added, which is set on PTRACE_INTERRUPT and
cleared when any trap happens. As INTERRUPT should be useable
regardless of the current state of tracee, task_is_traced() test in
ptrace_check_attach() is skipped for INTERRUPT.
PTRACE_INTERRUPT is available iff tracee is attached with
PTRACE_SEIZE.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_INTERRUPT 0x4207
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive pid=%d\n", getpid());
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: INTERRUPT and DETACH\n");
ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL);
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_DETACH, tracee, NULL, NULL);
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
kill(tracee, SIGKILL);
return 0;
}
When called without argument, tracee is seized from running state,
interrupted and then detached back to running state.
# ./test-interrupt
tracee: alive pid=4546
tracee: alive pid=4546
tracee: alive pid=4546
tracer: INTERRUPT and DETACH
tracee: alive pid=4546
tracee: alive pid=4546
tracee: alive pid=4546
tracer: exiting
When called with argument, tracee is seized from stopped state,
continued, interrupted and then detached back to stopped state.
# ./test-interrupt 1
tracee: alive pid=4548
tracee: alive pid=4548
tracee: alive pid=4548
tracer: INTERRUPT and DETACH
tracer: exiting
Before PTRACE_INTERRUPT, once the tracee was running, there was no way
to trap tracee and do PTRACE_DETACH without causing side effect.
-v2: Updated to use task_set_jobctl_pending() so that it doesn't end
up scheduling TRAP_STOP if child is dying which may make the
child unkillable. Spotted by Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:16 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
switch (request) {
|
2008-01-30 20:31:47 +08:00
|
|
|
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);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef PTRACE_OLDSETOPTIONS
|
|
|
|
case PTRACE_OLDSETOPTIONS:
|
|
|
|
#endif
|
|
|
|
case PTRACE_SETOPTIONS:
|
|
|
|
ret = ptrace_setoptions(child, data);
|
|
|
|
break;
|
|
|
|
case PTRACE_GETEVENTMSG:
|
2010-10-28 06:33:46 +08:00
|
|
|
ret = put_user(child->ptrace_message, datalp);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2008-04-21 04:10:12 +08:00
|
|
|
|
2013-05-01 06:27:59 +08:00
|
|
|
case PTRACE_PEEKSIGINFO:
|
|
|
|
ret = ptrace_peek_siginfo(child, addr, data);
|
|
|
|
break;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
case PTRACE_GETSIGINFO:
|
2008-04-21 04:10:12 +08:00
|
|
|
ret = ptrace_getsiginfo(child, &siginfo);
|
|
|
|
if (!ret)
|
2010-10-28 06:33:46 +08:00
|
|
|
ret = copy_siginfo_to_user(datavp, &siginfo);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2008-04-21 04:10:12 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
case PTRACE_SETSIGINFO:
|
2010-10-28 06:33:46 +08:00
|
|
|
if (copy_from_user(&siginfo, datavp, sizeof siginfo))
|
2008-04-21 04:10:12 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
else
|
|
|
|
ret = ptrace_setsiginfo(child, &siginfo);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2008-04-21 04:10:12 +08:00
|
|
|
|
2013-07-04 06:08:12 +08:00
|
|
|
case PTRACE_GETSIGMASK:
|
|
|
|
if (addr != sizeof(sigset_t)) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (copy_to_user(datavp, &child->blocked, 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
ptrace: implement PTRACE_INTERRUPT
Currently, there's no way to trap a running ptracee short of sending a
signal which has various side effects. This patch implements
PTRACE_INTERRUPT which traps ptracee without any signal or job control
related side effect.
The implementation is almost trivial. It uses the group stop trap -
SIGTRAP | PTRACE_EVENT_STOP << 8. A new trap flag
JOBCTL_TRAP_INTERRUPT is added, which is set on PTRACE_INTERRUPT and
cleared when any trap happens. As INTERRUPT should be useable
regardless of the current state of tracee, task_is_traced() test in
ptrace_check_attach() is skipped for INTERRUPT.
PTRACE_INTERRUPT is available iff tracee is attached with
PTRACE_SEIZE.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_INTERRUPT 0x4207
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive pid=%d\n", getpid());
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: INTERRUPT and DETACH\n");
ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL);
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_DETACH, tracee, NULL, NULL);
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
kill(tracee, SIGKILL);
return 0;
}
When called without argument, tracee is seized from running state,
interrupted and then detached back to running state.
# ./test-interrupt
tracee: alive pid=4546
tracee: alive pid=4546
tracee: alive pid=4546
tracer: INTERRUPT and DETACH
tracee: alive pid=4546
tracee: alive pid=4546
tracee: alive pid=4546
tracer: exiting
When called with argument, tracee is seized from stopped state,
continued, interrupted and then detached back to stopped state.
# ./test-interrupt 1
tracee: alive pid=4548
tracee: alive pid=4548
tracee: alive pid=4548
tracer: INTERRUPT and DETACH
tracer: exiting
Before PTRACE_INTERRUPT, once the tracee was running, there was no way
to trap tracee and do PTRACE_DETACH without causing side effect.
-v2: Updated to use task_set_jobctl_pending() so that it doesn't end
up scheduling TRAP_STOP if child is dying which may make the
child unkillable. Spotted by Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:16 +08:00
|
|
|
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;
|
|
|
|
|
ptrace: implement PTRACE_LISTEN
The previous patch implemented async notification for ptrace but it
only worked while trace is running. This patch introduces
PTRACE_LISTEN which is suggested by Oleg Nestrov.
It's allowed iff tracee is in STOP trap and puts tracee into
quasi-running state - tracee never really runs but wait(2) and
ptrace(2) consider it to be running. While ptracer is listening,
tracee is allowed to re-enter STOP to notify an async event.
Listening state is cleared on the first notification. Ptracer can
also clear it by issuing INTERRUPT - tracee will re-trap into STOP
with listening state cleared.
This allows ptracer to monitor group stop state without running tracee
- use INTERRUPT to put tracee into STOP trap, issue LISTEN and then
wait(2) to wait for the next group stop event. When it happens,
PTRACE_GETSIGINFO provides information to determine the current state.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_INTERRUPT 0x4207
#define PTRACE_LISTEN 0x4208
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts1s = { .tv_sec = 1 };
int main(int argc, char **argv)
{
pid_t tracee, tracer;
int i;
tracee = fork();
if (!tracee)
while (1)
pause();
tracer = fork();
if (!tracer) {
siginfo_t si;
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL);
repeat:
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_GETSIGINFO, tracee, NULL, &si);
if (!si.si_code) {
printf("tracer: SIG %d\n", si.si_signo);
ptrace(PTRACE_CONT, tracee, NULL,
(void *)(unsigned long)si.si_signo);
goto repeat;
}
printf("tracer: stopped=%d signo=%d\n",
si.si_signo != SIGTRAP, si.si_signo);
if (si.si_signo != SIGTRAP)
ptrace(PTRACE_LISTEN, tracee, NULL, NULL);
else
ptrace(PTRACE_CONT, tracee, NULL, NULL);
goto repeat;
}
for (i = 0; i < 3; i++) {
nanosleep(&ts1s, NULL);
printf("mother: SIGSTOP\n");
kill(tracee, SIGSTOP);
nanosleep(&ts1s, NULL);
printf("mother: SIGCONT\n");
kill(tracee, SIGCONT);
}
nanosleep(&ts1s, NULL);
kill(tracer, SIGKILL);
kill(tracee, SIGKILL);
return 0;
}
This is identical to the program to test TRAP_NOTIFY except that
tracee is PTRACE_LISTEN'd instead of PTRACE_CONT'd when group stopped.
This allows ptracer to monitor when group stop ends without running
tracee.
# ./test-listen
tracer: stopped=0 signo=5
mother: SIGSTOP
tracer: SIG 19
tracer: stopped=1 signo=19
mother: SIGCONT
tracer: stopped=0 signo=5
tracer: SIG 18
mother: SIGSTOP
tracer: SIG 19
tracer: stopped=1 signo=19
mother: SIGCONT
tracer: stopped=0 signo=5
tracer: SIG 18
mother: SIGSTOP
tracer: SIG 19
tracer: stopped=1 signo=19
mother: SIGCONT
tracer: stopped=0 signo=5
tracer: SIG 18
-v2: Moved JOBCTL_LISTENING check in wait_task_stopped() into
task_stopped_code() as suggested by Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:18 +08:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
ptrace: implement PTRACE_INTERRUPT
Currently, there's no way to trap a running ptracee short of sending a
signal which has various side effects. This patch implements
PTRACE_INTERRUPT which traps ptracee without any signal or job control
related side effect.
The implementation is almost trivial. It uses the group stop trap -
SIGTRAP | PTRACE_EVENT_STOP << 8. A new trap flag
JOBCTL_TRAP_INTERRUPT is added, which is set on PTRACE_INTERRUPT and
cleared when any trap happens. As INTERRUPT should be useable
regardless of the current state of tracee, task_is_traced() test in
ptrace_check_attach() is skipped for INTERRUPT.
PTRACE_INTERRUPT is available iff tracee is attached with
PTRACE_SEIZE.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_INTERRUPT 0x4207
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive pid=%d\n", getpid());
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: INTERRUPT and DETACH\n");
ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL);
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_DETACH, tracee, NULL, NULL);
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
kill(tracee, SIGKILL);
return 0;
}
When called without argument, tracee is seized from running state,
interrupted and then detached back to running state.
# ./test-interrupt
tracee: alive pid=4546
tracee: alive pid=4546
tracee: alive pid=4546
tracer: INTERRUPT and DETACH
tracee: alive pid=4546
tracee: alive pid=4546
tracee: alive pid=4546
tracer: exiting
When called with argument, tracee is seized from stopped state,
continued, interrupted and then detached back to stopped state.
# ./test-interrupt 1
tracee: alive pid=4548
tracee: alive pid=4548
tracee: alive pid=4548
tracer: INTERRUPT and DETACH
tracer: exiting
Before PTRACE_INTERRUPT, once the tracee was running, there was no way
to trap tracee and do PTRACE_DETACH without causing side effect.
-v2: Updated to use task_set_jobctl_pending() so that it doesn't end
up scheduling TRAP_STOP if child is dying which may make the
child unkillable. Spotted by Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:16 +08:00
|
|
|
if (likely(task_set_jobctl_pending(child, JOBCTL_TRAP_STOP)))
|
2013-01-22 03:47:41 +08:00
|
|
|
ptrace_signal_wake_up(child, child->jobctl & JOBCTL_LISTENING);
|
ptrace: implement PTRACE_LISTEN
The previous patch implemented async notification for ptrace but it
only worked while trace is running. This patch introduces
PTRACE_LISTEN which is suggested by Oleg Nestrov.
It's allowed iff tracee is in STOP trap and puts tracee into
quasi-running state - tracee never really runs but wait(2) and
ptrace(2) consider it to be running. While ptracer is listening,
tracee is allowed to re-enter STOP to notify an async event.
Listening state is cleared on the first notification. Ptracer can
also clear it by issuing INTERRUPT - tracee will re-trap into STOP
with listening state cleared.
This allows ptracer to monitor group stop state without running tracee
- use INTERRUPT to put tracee into STOP trap, issue LISTEN and then
wait(2) to wait for the next group stop event. When it happens,
PTRACE_GETSIGINFO provides information to determine the current state.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_INTERRUPT 0x4207
#define PTRACE_LISTEN 0x4208
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts1s = { .tv_sec = 1 };
int main(int argc, char **argv)
{
pid_t tracee, tracer;
int i;
tracee = fork();
if (!tracee)
while (1)
pause();
tracer = fork();
if (!tracer) {
siginfo_t si;
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL);
repeat:
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_GETSIGINFO, tracee, NULL, &si);
if (!si.si_code) {
printf("tracer: SIG %d\n", si.si_signo);
ptrace(PTRACE_CONT, tracee, NULL,
(void *)(unsigned long)si.si_signo);
goto repeat;
}
printf("tracer: stopped=%d signo=%d\n",
si.si_signo != SIGTRAP, si.si_signo);
if (si.si_signo != SIGTRAP)
ptrace(PTRACE_LISTEN, tracee, NULL, NULL);
else
ptrace(PTRACE_CONT, tracee, NULL, NULL);
goto repeat;
}
for (i = 0; i < 3; i++) {
nanosleep(&ts1s, NULL);
printf("mother: SIGSTOP\n");
kill(tracee, SIGSTOP);
nanosleep(&ts1s, NULL);
printf("mother: SIGCONT\n");
kill(tracee, SIGCONT);
}
nanosleep(&ts1s, NULL);
kill(tracer, SIGKILL);
kill(tracee, SIGKILL);
return 0;
}
This is identical to the program to test TRAP_NOTIFY except that
tracee is PTRACE_LISTEN'd instead of PTRACE_CONT'd when group stopped.
This allows ptracer to monitor when group stop ends without running
tracee.
# ./test-listen
tracer: stopped=0 signo=5
mother: SIGSTOP
tracer: SIG 19
tracer: stopped=1 signo=19
mother: SIGCONT
tracer: stopped=0 signo=5
tracer: SIG 18
mother: SIGSTOP
tracer: SIG 19
tracer: stopped=1 signo=19
mother: SIGCONT
tracer: stopped=0 signo=5
tracer: SIG 18
mother: SIGSTOP
tracer: SIG 19
tracer: stopped=1 signo=19
mother: SIGCONT
tracer: stopped=0 signo=5
tracer: SIG 18
-v2: Moved JOBCTL_LISTENING check in wait_task_stopped() into
task_stopped_code() as suggested by Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:18 +08:00
|
|
|
|
|
|
|
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;
|
2011-09-26 01:46:22 +08:00
|
|
|
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)
|
2013-01-22 03:47:41 +08:00
|
|
|
ptrace_signal_wake_up(child, true);
|
2011-09-26 01:46:22 +08:00
|
|
|
ret = 0;
|
|
|
|
}
|
ptrace: implement PTRACE_INTERRUPT
Currently, there's no way to trap a running ptracee short of sending a
signal which has various side effects. This patch implements
PTRACE_INTERRUPT which traps ptracee without any signal or job control
related side effect.
The implementation is almost trivial. It uses the group stop trap -
SIGTRAP | PTRACE_EVENT_STOP << 8. A new trap flag
JOBCTL_TRAP_INTERRUPT is added, which is set on PTRACE_INTERRUPT and
cleared when any trap happens. As INTERRUPT should be useable
regardless of the current state of tracee, task_is_traced() test in
ptrace_check_attach() is skipped for INTERRUPT.
PTRACE_INTERRUPT is available iff tracee is attached with
PTRACE_SEIZE.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_INTERRUPT 0x4207
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive pid=%d\n", getpid());
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: INTERRUPT and DETACH\n");
ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL);
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_DETACH, tracee, NULL, NULL);
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
kill(tracee, SIGKILL);
return 0;
}
When called without argument, tracee is seized from running state,
interrupted and then detached back to running state.
# ./test-interrupt
tracee: alive pid=4546
tracee: alive pid=4546
tracee: alive pid=4546
tracer: INTERRUPT and DETACH
tracee: alive pid=4546
tracee: alive pid=4546
tracee: alive pid=4546
tracer: exiting
When called with argument, tracee is seized from stopped state,
continued, interrupted and then detached back to stopped state.
# ./test-interrupt 1
tracee: alive pid=4548
tracee: alive pid=4548
tracee: alive pid=4548
tracer: INTERRUPT and DETACH
tracer: exiting
Before PTRACE_INTERRUPT, once the tracee was running, there was no way
to trap tracee and do PTRACE_DETACH without causing side effect.
-v2: Updated to use task_set_jobctl_pending() so that it doesn't end
up scheduling TRAP_STOP if child is dying which may make the
child unkillable. Spotted by Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:16 +08:00
|
|
|
unlock_task_sighand(child, &flags);
|
|
|
|
break;
|
|
|
|
|
2007-10-16 16:23:45 +08:00
|
|
|
case PTRACE_DETACH: /* detach a process that was attached. */
|
|
|
|
ret = ptrace_detach(child, data);
|
|
|
|
break;
|
2008-01-30 20:30:51 +08:00
|
|
|
|
2010-05-27 05:42:52 +08:00
|
|
|
#ifdef CONFIG_BINFMT_ELF_FDPIC
|
|
|
|
case PTRACE_GETFDPIC: {
|
2010-05-27 05:42:53 +08:00
|
|
|
struct mm_struct *mm = get_task_mm(child);
|
2010-05-27 05:42:52 +08:00
|
|
|
unsigned long tmp = 0;
|
|
|
|
|
2010-05-27 05:42:53 +08:00
|
|
|
ret = -ESRCH;
|
|
|
|
if (!mm)
|
|
|
|
break;
|
|
|
|
|
2010-05-27 05:42:52 +08:00
|
|
|
switch (addr) {
|
|
|
|
case PTRACE_GETFDPIC_EXEC:
|
2010-05-27 05:42:53 +08:00
|
|
|
tmp = mm->context.exec_fdpic_loadmap;
|
2010-05-27 05:42:52 +08:00
|
|
|
break;
|
|
|
|
case PTRACE_GETFDPIC_INTERP:
|
2010-05-27 05:42:53 +08:00
|
|
|
tmp = mm->context.interp_fdpic_loadmap;
|
2010-05-27 05:42:52 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2010-05-27 05:42:53 +08:00
|
|
|
mmput(mm);
|
2010-05-27 05:42:52 +08:00
|
|
|
|
2010-10-28 06:33:46 +08:00
|
|
|
ret = put_user(tmp, datalp);
|
2010-05-27 05:42:52 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-01-30 20:30:51 +08:00
|
|
|
#ifdef PTRACE_SINGLESTEP
|
|
|
|
case PTRACE_SINGLESTEP:
|
|
|
|
#endif
|
2008-01-30 20:30:53 +08:00
|
|
|
#ifdef PTRACE_SINGLEBLOCK
|
|
|
|
case PTRACE_SINGLEBLOCK:
|
|
|
|
#endif
|
2008-01-30 20:30:51 +08:00
|
|
|
#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:
|
|
|
|
if (child->exit_state) /* already dead */
|
|
|
|
return 0;
|
|
|
|
return ptrace_resume(child, request, SIGKILL);
|
|
|
|
|
2010-02-12 03:51:00 +08:00
|
|
|
#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
|
|
|
|
case PTRACE_GETREGSET:
|
2013-07-04 06:08:12 +08:00
|
|
|
case PTRACE_SETREGSET: {
|
2010-02-12 03:51:00 +08:00
|
|
|
struct iovec kiov;
|
2010-10-28 06:33:46 +08:00
|
|
|
struct iovec __user *uiov = datavp;
|
2010-02-12 03:51:00 +08:00
|
|
|
|
|
|
|
if (!access_ok(VERIFY_WRITE, 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;
|
|
|
|
}
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2005-11-07 16:59:47 +08:00
|
|
|
|
2009-06-18 07:27:34 +08:00
|
|
|
static struct task_struct *ptrace_get_task_struct(pid_t pid)
|
2006-01-08 17:02:33 +08:00
|
|
|
{
|
|
|
|
struct task_struct *child;
|
2005-11-07 16:59:47 +08:00
|
|
|
|
2009-06-18 07:27:34 +08:00
|
|
|
rcu_read_lock();
|
2007-10-19 14:40:16 +08:00
|
|
|
child = find_task_by_vpid(pid);
|
2005-11-07 16:59:47 +08:00
|
|
|
if (child)
|
|
|
|
get_task_struct(child);
|
2009-06-18 07:27:34 +08:00
|
|
|
rcu_read_unlock();
|
2006-09-29 17:00:07 +08:00
|
|
|
|
2005-11-07 16:59:47 +08:00
|
|
|
if (!child)
|
2006-01-08 17:02:33 +08:00
|
|
|
return ERR_PTR(-ESRCH);
|
|
|
|
return child;
|
2005-11-07 16:59:47 +08:00
|
|
|
}
|
|
|
|
|
2007-10-16 16:26:37 +08:00
|
|
|
#ifndef arch_ptrace_attach
|
|
|
|
#define arch_ptrace_attach(child) do { } while (0)
|
|
|
|
#endif
|
|
|
|
|
2010-10-28 06:33:45 +08:00
|
|
|
SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr,
|
|
|
|
unsigned long, data)
|
2005-11-07 16:59:47 +08:00
|
|
|
{
|
|
|
|
struct task_struct *child;
|
|
|
|
long ret;
|
|
|
|
|
2006-01-08 17:02:33 +08:00
|
|
|
if (request == PTRACE_TRACEME) {
|
|
|
|
ret = ptrace_traceme();
|
2007-11-27 20:02:40 +08:00
|
|
|
if (!ret)
|
|
|
|
arch_ptrace_attach(current);
|
2005-11-07 16:59:47 +08:00
|
|
|
goto out;
|
2006-01-08 17:02:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
child = ptrace_get_task_struct(pid);
|
|
|
|
if (IS_ERR(child)) {
|
|
|
|
ret = PTR_ERR(child);
|
|
|
|
goto out;
|
|
|
|
}
|
2005-11-07 16:59:47 +08:00
|
|
|
|
ptrace: implement PTRACE_SEIZE
PTRACE_ATTACH implicitly issues SIGSTOP on attach which has side
effects on tracee signal and job control states. This patch
implements a new ptrace request PTRACE_SEIZE which attaches a tracee
without trapping it or affecting its signal and job control states.
The usage is the same with PTRACE_ATTACH but it takes PTRACE_SEIZE_*
flags in @data. Currently, the only defined flag is
PTRACE_SEIZE_DEVEL which is a temporary flag to enable PTRACE_SEIZE.
PTRACE_SEIZE will change ptrace behaviors outside of attach itself.
The changes will be implemented gradually and the DEVEL flag is to
prevent programs which expect full SEIZE behavior from using it before
all the behavior modifications are complete while allowing unit
testing. The flag will be removed once SEIZE behaviors are completely
implemented.
* PTRACE_SEIZE, unlike ATTACH, doesn't force tracee to trap. After
attaching tracee continues to run unless a trap condition occurs.
* PTRACE_SEIZE doesn't affect signal or group stop state.
* If PTRACE_SEIZE'd, group stop uses PTRACE_EVENT_STOP trap which uses
exit_code of (signr | PTRACE_EVENT_STOP << 8) where signr is one of
the stopping signals if group stop is in effect or SIGTRAP
otherwise, and returns usual trap siginfo on PTRACE_GETSIGINFO
instead of NULL.
Seizing sets PT_SEIZED in ->ptrace of the tracee. This flag will be
used to determine whether new SEIZE behaviors should be enabled.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive\n");
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
return 0;
}
When the above program is called w/o argument, tracee is seized while
running and remains running. When tracer exits, tracee continues to
run and print out messages.
# ./test-seize-simple
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
tracee: alive
tracee: alive
When called with an argument, tracee is seized from stopped state and
continued, and returns to stopped state when tracer exits.
# ./test-seize
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
# ps -el|grep test-seize
1 T 0 4720 1 0 80 0 - 941 signal ttyS0 00:00:00 test-seize
-v2: SEIZE doesn't schedule TRAP_STOP and leaves tracee running as Jan
suggested.
-v3: PTRACE_EVENT_STOP traps now report group stop state by signr. If
group stop is in effect the stop signal number is returned as
part of exit_code; otherwise, SIGTRAP. This was suggested by
Denys and Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
Cc: Denys Vlasenko <vda.linux@googlemail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:15 +08:00
|
|
|
if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) {
|
2012-03-24 06:02:42 +08:00
|
|
|
ret = ptrace_attach(child, request, addr, data);
|
2007-10-16 16:26:37 +08:00
|
|
|
/*
|
|
|
|
* Some architectures need to do book-keeping after
|
|
|
|
* a ptrace attach.
|
|
|
|
*/
|
|
|
|
if (!ret)
|
|
|
|
arch_ptrace_attach(child);
|
2005-11-14 08:06:33 +08:00
|
|
|
goto out_put_task_struct;
|
2005-11-07 16:59:47 +08:00
|
|
|
}
|
|
|
|
|
ptrace: implement PTRACE_INTERRUPT
Currently, there's no way to trap a running ptracee short of sending a
signal which has various side effects. This patch implements
PTRACE_INTERRUPT which traps ptracee without any signal or job control
related side effect.
The implementation is almost trivial. It uses the group stop trap -
SIGTRAP | PTRACE_EVENT_STOP << 8. A new trap flag
JOBCTL_TRAP_INTERRUPT is added, which is set on PTRACE_INTERRUPT and
cleared when any trap happens. As INTERRUPT should be useable
regardless of the current state of tracee, task_is_traced() test in
ptrace_check_attach() is skipped for INTERRUPT.
PTRACE_INTERRUPT is available iff tracee is attached with
PTRACE_SEIZE.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_INTERRUPT 0x4207
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive pid=%d\n", getpid());
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: INTERRUPT and DETACH\n");
ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL);
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_DETACH, tracee, NULL, NULL);
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
kill(tracee, SIGKILL);
return 0;
}
When called without argument, tracee is seized from running state,
interrupted and then detached back to running state.
# ./test-interrupt
tracee: alive pid=4546
tracee: alive pid=4546
tracee: alive pid=4546
tracer: INTERRUPT and DETACH
tracee: alive pid=4546
tracee: alive pid=4546
tracee: alive pid=4546
tracer: exiting
When called with argument, tracee is seized from stopped state,
continued, interrupted and then detached back to stopped state.
# ./test-interrupt 1
tracee: alive pid=4548
tracee: alive pid=4548
tracee: alive pid=4548
tracer: INTERRUPT and DETACH
tracer: exiting
Before PTRACE_INTERRUPT, once the tracee was running, there was no way
to trap tracee and do PTRACE_DETACH without causing side effect.
-v2: Updated to use task_set_jobctl_pending() so that it doesn't end
up scheduling TRAP_STOP if child is dying which may make the
child unkillable. Spotted by Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:16 +08:00
|
|
|
ret = ptrace_check_attach(child, request == PTRACE_KILL ||
|
|
|
|
request == PTRACE_INTERRUPT);
|
2005-11-07 16:59:47 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto out_put_task_struct;
|
|
|
|
|
|
|
|
ret = arch_ptrace(child, request, addr, data);
|
2013-01-22 03:48:00 +08:00
|
|
|
if (ret || request != PTRACE_DETACH)
|
|
|
|
ptrace_unfreeze_traced(child);
|
2005-11-07 16:59:47 +08:00
|
|
|
|
|
|
|
out_put_task_struct:
|
|
|
|
put_task_struct(child);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
2007-07-17 19:03:43 +08:00
|
|
|
|
2010-10-28 06:33:45 +08:00
|
|
|
int generic_ptrace_peekdata(struct task_struct *tsk, unsigned long addr,
|
|
|
|
unsigned long data)
|
2007-07-17 19:03:43 +08:00
|
|
|
{
|
|
|
|
unsigned long tmp;
|
|
|
|
int copied;
|
|
|
|
|
|
|
|
copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
|
|
|
|
if (copied != sizeof(tmp))
|
|
|
|
return -EIO;
|
|
|
|
return put_user(tmp, (unsigned long __user *)data);
|
|
|
|
}
|
2007-07-17 19:03:44 +08:00
|
|
|
|
2010-10-28 06:33:45 +08:00
|
|
|
int generic_ptrace_pokedata(struct task_struct *tsk, unsigned long addr,
|
|
|
|
unsigned long data)
|
2007-07-17 19:03:44 +08:00
|
|
|
{
|
|
|
|
int copied;
|
|
|
|
|
|
|
|
copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
|
|
|
|
return (copied == sizeof(data)) ? 0 : -EIO;
|
|
|
|
}
|
2008-01-30 20:31:47 +08:00
|
|
|
|
2008-11-25 15:10:03 +08:00
|
|
|
#if defined CONFIG_COMPAT
|
2008-01-30 20:31:47 +08:00
|
|
|
#include <linux/compat.h>
|
|
|
|
|
|
|
|
int compat_ptrace_request(struct task_struct *child, compat_long_t request,
|
|
|
|
compat_ulong_t addr, compat_ulong_t data)
|
|
|
|
{
|
|
|
|
compat_ulong_t __user *datap = compat_ptr(data);
|
|
|
|
compat_ulong_t word;
|
2008-04-21 04:10:12 +08:00
|
|
|
siginfo_t siginfo;
|
2008-01-30 20:31:47 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
switch (request) {
|
|
|
|
case PTRACE_PEEKTEXT:
|
|
|
|
case PTRACE_PEEKDATA:
|
|
|
|
ret = access_process_vm(child, addr, &word, sizeof(word), 0);
|
|
|
|
if (ret != sizeof(word))
|
|
|
|
ret = -EIO;
|
|
|
|
else
|
|
|
|
ret = put_user(word, datap);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PTRACE_POKETEXT:
|
|
|
|
case PTRACE_POKEDATA:
|
|
|
|
ret = access_process_vm(child, addr, &data, sizeof(data), 1);
|
|
|
|
ret = (ret != sizeof(data) ? -EIO : 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PTRACE_GETEVENTMSG:
|
|
|
|
ret = put_user((compat_ulong_t) child->ptrace_message, datap);
|
|
|
|
break;
|
|
|
|
|
2008-04-21 04:10:12 +08:00
|
|
|
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:
|
|
|
|
memset(&siginfo, 0, sizeof siginfo);
|
|
|
|
if (copy_siginfo_from_user32(
|
|
|
|
&siginfo, (struct compat_siginfo __user *) datap))
|
|
|
|
ret = -EFAULT;
|
|
|
|
else
|
|
|
|
ret = ptrace_setsiginfo(child, &siginfo);
|
|
|
|
break;
|
2010-02-12 03:51:00 +08:00
|
|
|
#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(VERIFY_WRITE, uiov, sizeof(*uiov)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (__get_user(ptr, &uiov->iov_base) ||
|
|
|
|
__get_user(len, &uiov->iov_len))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
kiov.iov_base = compat_ptr(ptr);
|
|
|
|
kiov.iov_len = len;
|
|
|
|
|
|
|
|
ret = ptrace_regset(child, request, addr, &kiov);
|
|
|
|
if (!ret)
|
|
|
|
ret = __put_user(kiov.iov_len, &uiov->iov_len);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2008-04-21 04:10:12 +08:00
|
|
|
|
2008-01-30 20:31:47 +08:00
|
|
|
default:
|
|
|
|
ret = ptrace_request(child, request, addr, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2008-01-30 20:31:48 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
ptrace: implement PTRACE_SEIZE
PTRACE_ATTACH implicitly issues SIGSTOP on attach which has side
effects on tracee signal and job control states. This patch
implements a new ptrace request PTRACE_SEIZE which attaches a tracee
without trapping it or affecting its signal and job control states.
The usage is the same with PTRACE_ATTACH but it takes PTRACE_SEIZE_*
flags in @data. Currently, the only defined flag is
PTRACE_SEIZE_DEVEL which is a temporary flag to enable PTRACE_SEIZE.
PTRACE_SEIZE will change ptrace behaviors outside of attach itself.
The changes will be implemented gradually and the DEVEL flag is to
prevent programs which expect full SEIZE behavior from using it before
all the behavior modifications are complete while allowing unit
testing. The flag will be removed once SEIZE behaviors are completely
implemented.
* PTRACE_SEIZE, unlike ATTACH, doesn't force tracee to trap. After
attaching tracee continues to run unless a trap condition occurs.
* PTRACE_SEIZE doesn't affect signal or group stop state.
* If PTRACE_SEIZE'd, group stop uses PTRACE_EVENT_STOP trap which uses
exit_code of (signr | PTRACE_EVENT_STOP << 8) where signr is one of
the stopping signals if group stop is in effect or SIGTRAP
otherwise, and returns usual trap siginfo on PTRACE_GETSIGINFO
instead of NULL.
Seizing sets PT_SEIZED in ->ptrace of the tracee. This flag will be
used to determine whether new SEIZE behaviors should be enabled.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive\n");
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
return 0;
}
When the above program is called w/o argument, tracee is seized while
running and remains running. When tracer exits, tracee continues to
run and print out messages.
# ./test-seize-simple
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
tracee: alive
tracee: alive
When called with an argument, tracee is seized from stopped state and
continued, and returns to stopped state when tracer exits.
# ./test-seize
tracee: alive
tracee: alive
tracee: alive
tracer: exiting
# ps -el|grep test-seize
1 T 0 4720 1 0 80 0 - 941 signal ttyS0 00:00:00 test-seize
-v2: SEIZE doesn't schedule TRAP_STOP and leaves tracee running as Jan
suggested.
-v3: PTRACE_EVENT_STOP traps now report group stop state by signr. If
group stop is in effect the stop signal number is returned as
part of exit_code; otherwise, SIGTRAP. This was suggested by
Denys and Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Jan Kratochvil <jan.kratochvil@redhat.com>
Cc: Denys Vlasenko <vda.linux@googlemail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:15 +08:00
|
|
|
if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) {
|
2012-03-24 06:02:42 +08:00
|
|
|
ret = ptrace_attach(child, request, addr, data);
|
2008-01-30 20:31:48 +08:00
|
|
|
/*
|
|
|
|
* Some architectures need to do book-keeping after
|
|
|
|
* a ptrace attach.
|
|
|
|
*/
|
|
|
|
if (!ret)
|
|
|
|
arch_ptrace_attach(child);
|
|
|
|
goto out_put_task_struct;
|
|
|
|
}
|
|
|
|
|
ptrace: implement PTRACE_INTERRUPT
Currently, there's no way to trap a running ptracee short of sending a
signal which has various side effects. This patch implements
PTRACE_INTERRUPT which traps ptracee without any signal or job control
related side effect.
The implementation is almost trivial. It uses the group stop trap -
SIGTRAP | PTRACE_EVENT_STOP << 8. A new trap flag
JOBCTL_TRAP_INTERRUPT is added, which is set on PTRACE_INTERRUPT and
cleared when any trap happens. As INTERRUPT should be useable
regardless of the current state of tracee, task_is_traced() test in
ptrace_check_attach() is skipped for INTERRUPT.
PTRACE_INTERRUPT is available iff tracee is attached with
PTRACE_SEIZE.
Test program follows.
#define PTRACE_SEIZE 0x4206
#define PTRACE_INTERRUPT 0x4207
#define PTRACE_SEIZE_DEVEL 0x80000000
static const struct timespec ts100ms = { .tv_nsec = 100000000 };
static const struct timespec ts1s = { .tv_sec = 1 };
static const struct timespec ts3s = { .tv_sec = 3 };
int main(int argc, char **argv)
{
pid_t tracee;
tracee = fork();
if (tracee == 0) {
nanosleep(&ts100ms, NULL);
while (1) {
printf("tracee: alive pid=%d\n", getpid());
nanosleep(&ts1s, NULL);
}
}
if (argc > 1)
kill(tracee, SIGSTOP);
nanosleep(&ts100ms, NULL);
ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (argc > 1) {
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_CONT, tracee, NULL, NULL);
}
nanosleep(&ts3s, NULL);
printf("tracer: INTERRUPT and DETACH\n");
ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL);
waitid(P_PID, tracee, NULL, WSTOPPED);
ptrace(PTRACE_DETACH, tracee, NULL, NULL);
nanosleep(&ts3s, NULL);
printf("tracer: exiting\n");
kill(tracee, SIGKILL);
return 0;
}
When called without argument, tracee is seized from running state,
interrupted and then detached back to running state.
# ./test-interrupt
tracee: alive pid=4546
tracee: alive pid=4546
tracee: alive pid=4546
tracer: INTERRUPT and DETACH
tracee: alive pid=4546
tracee: alive pid=4546
tracee: alive pid=4546
tracer: exiting
When called with argument, tracee is seized from stopped state,
continued, interrupted and then detached back to stopped state.
# ./test-interrupt 1
tracee: alive pid=4548
tracee: alive pid=4548
tracee: alive pid=4548
tracer: INTERRUPT and DETACH
tracer: exiting
Before PTRACE_INTERRUPT, once the tracee was running, there was no way
to trap tracee and do PTRACE_DETACH without causing side effect.
-v2: Updated to use task_set_jobctl_pending() so that it doesn't end
up scheduling TRAP_STOP if child is dying which may make the
child unkillable. Spotted by Oleg.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
2011-06-14 17:20:16 +08:00
|
|
|
ret = ptrace_check_attach(child, request == PTRACE_KILL ||
|
|
|
|
request == PTRACE_INTERRUPT);
|
2013-01-22 03:48:00 +08:00
|
|
|
if (!ret) {
|
2008-01-30 20:31:48 +08:00
|
|
|
ret = compat_arch_ptrace(child, request, addr, data);
|
2013-01-22 03:48:00 +08:00
|
|
|
if (ret || request != PTRACE_DETACH)
|
|
|
|
ptrace_unfreeze_traced(child);
|
|
|
|
}
|
2008-01-30 20:31:48 +08:00
|
|
|
|
|
|
|
out_put_task_struct:
|
|
|
|
put_task_struct(child);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
2008-11-25 15:10:03 +08:00
|
|
|
#endif /* CONFIG_COMPAT */
|