On 10/07, Roland McGrath wrote: > > > Currently, if a tracer does ptrace(DETACH, tracee, SIGXXX) > > and then another/same tracer does ptrace(ATTACH, tracee) > > then SIGXXX will not be reported to the new tracer. > > Correct.
Well almost... What if the tracee reports a signal and stops, the tracer detaches but does not wake it up because of SIGNAL_STOP_STOPPED ? In this case this signal will be visible after the next attach. OK, currently ptrace_detach() unconditionally wakes up the tracee. But we seem to agree this wakeup should be killed, it completely breaks the group-stop logic and it has other problems. > > Why? Should utrace-ptrace be 100% compatible here? > > The rationale I see is that the debugger observed the signal, decided it > wanted the tracee to experience the normal effects of the signal and be > done with it after that. If it's a termination signal, the debugger really > expects that the tracee can do nothing but die. (Given the tortured > historic logic of the ptrace code paths, this applies only to signal stops > and only when it either passes back the same signal number or at least > another one that is not blocked.) Now replace "debugger" with "monitor" or > (dismal) "security-checking syscall tracing product", or just shudder while > trying not to imagine what all inane things ptrace might already be in use > for. It could be important to someone out there e.g. that some other > process of the tracee's UID cannot race in there and rescue it from dying. If "monitor" really wants to kill the tracee it should use SIGKILL ;) Any other fatal signal can be ignored by the tracee's sub-thread after detach but before the tracee returns from ptrace_signal(). This leads to another isssue I forgot to mention when I sent [PATCH 71] RFC, fix ptrace(PTRACE_DETACH, signr) logic https://www.redhat.com/archives/utrace-devel/2009-October/msg00043.html Since detach_signal() actually sends the signal, ptrace(DETACH, SIGKILL) really works: it wakes up SIGNAL_STOP_STOPPED tracee. > > I do not think there is a "real life" application that does > > ATTACH + DETACH and relies on fact it must not see this sig. > > We can hope, but we cannot assume. Oh, OK. Lets discuss how we can implement detach to be compatible... We already discussed detached_ops. So, we either change engine->ops (my choice) or add the new engine with ->ops = detached_ops and detach the ptrace engine. We need detached_report_signal() callback which fixups siginfo if signr != info->si_signo and returns UTRACE_DETACH | UTRACE_SIGNAL_DELIVER. How can detached_report_signal() set info->si_pid correctly? the tracer has gone away. This probably means this info should be prepared during detach, the tracer should fill context->siginfo. But the real problem, I can't see how can we teach ptrace_report_signal() to not play with parting signals. Do you have any idea? perhaps we can introduce UTRACE_SIGNAL_DELIVER_I_AM_DETACHED_ENGINE, but this is ugly. Oh, I'd like to avoid detached_ops. Perhaps the new attach can re-use the old engine/context? ptrace(DETACH, SIGNR) does: context->detached = true; // checked in ptrace_report_signal() return utrace_control(INTERRUPT); Now, how ptrace(ATTACH) can clear ->detached and re-use this engine without utrace->lock ? IOW, will you agree to abuse utrace->lock for this? OR. Perhaps we can introduce UTRACE_ATTACH_MATCH_FILTER_DATA ? static bool engine_matches(struct utrace_engine *engine, int flags, const struct utrace_engine_ops *ops, void *data) { if ((flags & UTRACE_ATTACH_MATCH_OPS) && engine->ops != ops) return false; if ((flags & UTRACE_ATTACH_MATCH_FILTER_DATA)) { bool (*filter)(void*) = data; if (!filter(engine->data)) return false; } if ((flags & UTRACE_ATTACH_MATCH_DATA) && engine->data != data) return false; return engine->ops && engine->ops != &utrace_detached_ops; } Now, PTRACE_ATTACH does: bool ptrace_filter(void *engine) { struct ptrace_context *context = ptrace_context(engine); if (!context->detached) return false; context->detached = false; // re-use return true; } ptrace_attach_task(...) { ... engine = utrace_attach_task(MATCH_OPS | MATCH_FILTER_DATA, ptrace_utrace_ops, ptrace_filter); if (!IS_ERR(engine)) { // We re-used the old engine, but perhaps it is // going to return UTRACE_DETACH ? utrace_barrier(engine); if (engine->ops == utrace_detached_ops) engine = -EANY; } if (IS_ERR(engine)) engine = utrace_attach_task(CREATE | EXCLUSIVE, ptrace_utrace_ops, context); ... } What do you think? In any case, I'd like to think more. Somehow I am so sleepy today, perhaps I can invent something else tomorrow... But actually I hope you can find a better solution ;) Oleg.