From: Oleg Nesterov <oleg@redhat.com>
To: Roland McGrath <roland@redhat.com>
Cc: Christoph Hellwig <hch@infradead.org>,
Ingo Molnar <mingo@elte.hu>,
linux-kernel@vger.kernel.org
Subject: PATCH? tracehook_report_clone: fix false positives
Date: Sat, 30 May 2009 20:52:12 +0200 [thread overview]
Message-ID: <20090530185212.GA10677@redhat.com> (raw)
In-Reply-To: <20090529122411.GC19812@redhat.com>
On 05/29, Oleg Nesterov wrote:
>
> I wonder if can kill "int trace" in do_fork/copy_process. Looks like we
> can...
Hmm. In fact, I think "int trace" is not always right.
tracehook_report_clone:
if (unlikely(trace) || unlikely(clone_flags & CLONE_PTRACE)) {
/*
* The child starts up with an immediate SIGSTOP.
*/
sigaddset(&child->pending.signal, SIGSTOP);
set_tsk_thread_flag(child, TIF_SIGPENDING);
}
Firtsly, I don't understand CLONE_PTRACE check. Suppose that untraced
task does clone(CLONE_PTRACE). In that case we create the untraced
child (this is correct) but still we send SIGSTOP.
I do not really know if this bug or not, but this doesn't look right.
At least this should be commented, imho. And, looking at 2.6.26, I think
the behaviour was different before tracehooks.
So, I assume this is bug for now.
But even the "trace != 0" check is not right, it can be false positive.
Suppose that PT_TRACE_FORK task P forks, tracehook_finish_clone() does
ptrace_link(). Then, the tracer exits and untraces both P and its new
child. In that case, I don't think it is right to send SIGSTOP.
Afaics, we can fix these problems (and kill "trace" arg):
static inline void tracehook_report_clone(struct pt_regs *regs,
unsigned long clone_flags,
pid_t pid, struct task_struct *child)
{
if (unlikely(task_ptrace(child)) {
/*
* The child starts up with an immediate SIGSTOP.
*/
sigaddset(&child->pending.signal, SIGSTOP);
set_tsk_thread_flag(child, TIF_SIGPENDING);
}
}
Is task_ptrace() check racy? Yes. But a) this race is harmless and
b) the current code is equally racy.
We have multiple scenarious, but for example, suppose that untraced task
does clone(CLONE_PTRACE), do_fork() returns the new untraced child. This
child C is already visible to user-space, so it is possible that another
tracer has already attached to C, or ptrace_attach() is in progress.
However, afaics in this case we are fine. We do not care if we race with
another tracer. Because this tracer will send or has alredy sent SIGSTOP
anyway. And given that wake_up_new_task() was not called yet, we should
not worry about untrace. The child can be untraced (if tracer exits) in
parallel, but the pending SIGSTOP won't go away in any case.
So, I am going to send the patch below. But this leads to another question:
should not we move these sigaddset() + set_tsk_thread_flag() into
ptrace_init_task() ?
I don't thinks this makes sense for 2.6.30, we need another check. But
with ->ptrace_ctx patches we can do
static inline void ptrace_init_task(struct task_struct *child)
{
INIT_LIST_HEAD(&child->ptraced);
if (unlikely(child->ptrace_ctx) && ptrace_tracer(current)) {
ptrace_link(child, task_ptrace(current),
ptrace_tracer(current));
sigaddset(&child->pending.signal, SIGSTOP);
set_tsk_thread_flag(child, TIF_SIGPENDING);
}
}
This way we optimize the fork() path a little bit, and it becomes more
clear. Yes, utrace-ptrace will likely change this code further anyway
and move the code from _init() to _report_clone() back, but in this case
I guess the whole tracehook_finish_clone() will go away, so this change
looks right anyway to me.
Oleg.
--- a/include/linux/tracehook.h
+++ b/include/linux/tracehook.h
@@ -259,14 +259,12 @@ static inline void tracehook_finish_clon
/**
* tracehook_report_clone - in parent, new child is about to start running
- * @trace: return value from tracehook_prepare_clone()
* @regs: parent's user register state
* @clone_flags: flags from parent's system call
* @pid: new child's PID in the parent's namespace
* @child: new child task
*
- * Called after a child is set up, but before it has been started
- * running. @trace is the value returned by tracehook_prepare_clone().
+ * Called after a child is set up, but before it has been started running.
* This is not a good place to block, because the child has not started
* yet. Suspend the child here if desired, and then block in
* tracehook_report_clone_complete(). This must prevent the child from
@@ -276,13 +274,14 @@ static inline void tracehook_finish_clon
*
* Called with no locks held, but the child cannot run until this returns.
*/
-static inline void tracehook_report_clone(int trace, struct pt_regs *regs,
+static inline void tracehook_report_clone(struct pt_regs *regs,
unsigned long clone_flags,
pid_t pid, struct task_struct *child)
{
- if (unlikely(trace) || unlikely(clone_flags & CLONE_PTRACE)) {
+ if (unlikely(task_ptrace(child))) {
/*
- * The child starts up with an immediate SIGSTOP.
+ * It doesn't matter who attached/attaching to this
+ * task, the pending SIGSTOP is right in any case.
*/
sigaddset(&child->pending.signal, SIGSTOP);
set_tsk_thread_flag(child, TIF_SIGPENDING);
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1409,7 +1409,7 @@ long do_fork(unsigned long clone_flags,
}
audit_finish_fork(p);
- tracehook_report_clone(trace, regs, clone_flags, nr, p);
+ tracehook_report_clone(regs, clone_flags, nr, p);
/*
* We set PF_STARTING at creation in case tracing wants to
next prev parent reply other threads:[~2009-05-30 18:57 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-05-28 11:36 [RFC PATCH 11/12 v2] ptrace: mv task_struct->ptrace_message ptrace_ctx->message Oleg Nesterov
2009-05-28 11:41 ` Oleg Nesterov
2009-05-28 21:24 ` Roland McGrath
2009-05-29 12:24 ` Oleg Nesterov
2009-05-30 18:52 ` Oleg Nesterov [this message]
2009-06-01 0:22 ` PATCH? tracehook_report_clone: fix false positives Roland McGrath
2009-06-01 20:07 ` Oleg Nesterov
2009-06-01 20:50 ` Roland McGrath
2009-06-01 21:34 ` Oleg Nesterov
2009-06-01 23:19 ` Roland McGrath
2009-06-02 0:14 ` Oleg Nesterov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20090530185212.GA10677@redhat.com \
--to=oleg@redhat.com \
--cc=hch@infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@elte.hu \
--cc=roland@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox