[RFC PATCH 0/3] PTRACE_SEIZE && fork() fixes

July 08th, 2011 - 01:20 pm ET by Oleg Nesterov | Report spam
On 06/01, Oleg Nesterov wrote:


Just to remind, tracehook_report_clone() shouldn't send SIGSTOP if
the auto-attached tracee is PT_SEIZED.



I think it is the time to do this, everything else looks consistent.

The patches are simple but 3/3 needs the discussion and acks, this
API is new.

Oleg.

To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
email Follow the discussionReplies 4 repliesReplies Make a reply

Similar topics

Replies

#1 Oleg Nesterov
July 08th, 2011 - 01:20 pm ET | Report spam
The fake SIGSTOP during attach has numerous problems. PTRACE_SEIZE
is already fine, but we have basically the same problems is SIGSTOP
is sent on auto-attach, the tracer can't know if this signal signal
should be cancelled or not.

Change ptrace_event() to set JOBCTL_TRAP_STOP if the new child is
PT_SEIZED, this triggers the PTRACE_EVENT_STOP report.

Thereafter a PT_SEIZED task can never report the bogus SIGSTOP.

Test-case:

#define PTRACE_SEIZE 0x4206
#define PTRACE_SEIZE_DEVEL 0x80000000
#define PTRACE_EVENT_STOP 7
#define WEVENT(s) ((s & 0xFF0000) >> 16)

int main(void)
{
int child, grand_child, status;
long message;

child = fork();
if (!child) {
kill(getpid(), SIGSTOP);
fork();
assert(0);
return 0x23;
}

assert(ptrace(PTRACE_SEIZE, child, 0,PTRACE_SEIZE_DEVEL) == 0);
assert(wait(&status) == child);
assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);

assert(ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACEFORK) == 0);

assert(ptrace(PTRACE_CONT, child, 0,0) == 0);
assert(waitpid(child, &status, 0) == child);
assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP);
assert(WEVENT(status) == PTRACE_EVENT_FORK);

assert(ptrace(PTRACE_GETEVENTMSG, child, 0, &message) == 0);
grand_child = message;

assert(waitpid(grand_child, &status, 0) == grand_child);
assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP);
assert(WEVENT(status) == PTRACE_EVENT_STOP);

kill(child, SIGKILL);
kill(grand_child, SIGKILL);
return 0;
}

Signed-off-by: Oleg Nesterov


include/linux/ptrace.h | 13 +++++++
1 file changed, 7 insertions(+), 6 deletions(-)

ptrace/include/linux/ptrace.h~5_clone_no_stop_if_seized 2011-07-08 18:32:05.000000000 +0200
+++ ptrace/include/linux/ptrace.h 2011-07-08 18:32:53.000000000 +0200
@@ -228,7 +228,11 @@ static inline void ptrace_init_task(stru
child->ptrace = current->ptrace;
__ptrace_link(child, current->parent);

- sigaddset(&child->pending.signal, SIGSTOP);
+ if (child->ptrace & PT_SEIZED)
+ task_set_jobctl_pending(child, JOBCTL_TRAP_STOP);
+ else
+ sigaddset(&child->pending.signal, SIGSTOP);
+
set_tsk_thread_flag(child, TIF_SIGPENDING);
}
}

To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Replies Reply to this message
#2 Oleg Nesterov
July 08th, 2011 - 01:20 pm ET | Report spam
If the new child is traced, do_fork() adds the pending SIGSTOP.
It assumes that either it is traced because of auto-attach or the
tracer attached later, in both cases sigaddset/set_thread_flag is
correct even if SIGSTOP is already pending.

Now that we have PTRACE_SEIZE this is no longer right in the latter
case. If the tracer does PTRACE_SEIZE after copy_process() makes the
child visible the queued SIGSTOP is wrong.

We could check PT_SEIZED bit and change ptrace_attach() to set both
PT_PTRACED and PT_SEIZED bits simultaneously but see the next patch,
we need to know whether this child was auto-attached or not anyway.

So this patch simply moves this code to ptrace_init_task(), this
way we can never race with ptrace_attach().

Signed-off-by: Oleg Nesterov


include/linux/ptrace.h | 3 +++
kernel/fork.c | 12
2 files changed, 3 insertions(+), 12 deletions(-)

ptrace/include/linux/ptrace.h~4_send_stop_from_ptrace_init 2011-07-08 17:24:46.000000000 +0200
+++ ptrace/include/linux/ptrace.h 2011-07-08 18:32:05.000000000 +0200
@@ -227,6 +227,9 @@ static inline void ptrace_init_task(stru
if (unlikely(ptrace) && current->ptrace) {
child->ptrace = current->ptrace;
__ptrace_link(child, current->parent);
+
+ sigaddset(&child->pending.signal, SIGSTOP);
+ set_tsk_thread_flag(child, TIF_SIGPENDING);
}
}

ptrace/kernel/fork.c~4_send_stop_from_ptrace_init 2011-07-08 17:24:46.000000000 +0200
+++ ptrace/kernel/fork.c 2011-07-08 17:25:25.000000000 +0200
@@ -37,7 +37,6 @@
#include <linux/swap.h>
#include <linux/syscalls.h>
#include <linux/jiffies.h>
-#include <linux/tracehook.h>
#include <linux/futex.h>
#include <linux/compat.h>
#include <linux/kthread.h>
@@ -1522,17 +1521,6 @@ long do_fork(unsigned long clone_flags,
audit_finish_fork(p);

/*
- * Child is ready but hasn't started running yet. Queue
- * SIGSTOP if it's gonna be ptraced - it doesn't matter who
- * attached/attaching to this task, the pending SIGSTOP is
- * right in any case.
- */
- if (unlikely(p->ptrace)) {
- sigaddset(&p->pending.signal, SIGSTOP);
- set_tsk_thread_flag(p, TIF_SIGPENDING);
- }
-
- /*
* We set PF_STARTING at creation in case tracing wants to
* use this to distinguish a fully live task from one that
* hasn't finished SIGSTOP raising yet. Now we clear it

To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Replies Reply to this message
#3 Tejun Heo
July 13th, 2011 - 06:30 am ET | Report spam
On Fri, Jul 08, 2011 at 07:13:39PM +0200, Oleg Nesterov wrote:
new_child->jobctl is not initialized during the fork, it is copied
from parent->jobctl. Currently this is harmless, the forking task
is running and copy_process() can't succeed if signal_pending() is
true, so only JOBCTL_STOP_DEQUEUED can be copied. Still this is a
bit fragile, it would be more clean to set ->jobctl = 0 explicitly.

Also, check ->ptrace != 0 instead of PT_PTRACED, move the
CONFIG_HAVE_HW_BREAKPOINT code up.

Signed-off-by: Oleg Nesterov



Acked-by: Tejun Heo

Thanks.

tejun
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Replies Reply to this message
#4 Tejun Heo
July 13th, 2011 - 08:20 am ET | Report spam
Hello,

On Fri, Jul 08, 2011 at 07:14:17PM +0200, Oleg Nesterov wrote:
The fake SIGSTOP during attach has numerous problems. PTRACE_SEIZE
is already fine, but we have basically the same problems is SIGSTOP
is sent on auto-attach, the tracer can't know if this signal signal
should be cancelled or not.

Change ptrace_event() to set JOBCTL_TRAP_STOP if the new child is
PT_SEIZED, this triggers the PTRACE_EVENT_STOP report.

Thereafter a PT_SEIZED task can never report the bogus SIGSTOP.

Test-case:


...
Signed-off-by: Oleg Nesterov



Looks good to me. For 2 and 3,

Acked-by: Tejun Heo

So, yeah, with this change, we're almost there.

Thanks.

tejun
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
email Follow the discussion Replies Reply to this message
Help Create a new topicReplies Make a reply
Search Make your own search