[PATCH 0/1] Was: utrace-cleanup branch

Oleg Nesterov oleg at redhat.com
Thu Nov 5 10:01:23 UTC 2009


On 11/03, Roland McGrath wrote:
>
> > 4. One of the changes in utrace_get_signal() doesn't look exactly right.
> >
> > 	if (utrace->resume < UTRACE_RESUME || utrace->signal_handler) {
> > 		...
> > 		if (resume > UTRACE_REPORT) {
> > 			report.action = resume;
> > 			finish_resume_report(&report);
> > 		}
> >
> >    Yes, we need finish_resume_report() here, but since report->takers
> >    is not set, finish_report_reset() will always call utrace_reset().
> >
> >    OTOH, we can't set ->takers before finish_resume_report(), we can
> >    miss UTRACE_DETACH request. utrace_control(DETACH)->utrace_do_stop()
> >    does not change utrace->resume != UTRACE_RESUME.
> >
> >    And since utrace_do_stop() never "upgrades" utrace->resume, we have
> >    another problem: UTRACE_STOP request can be lost here.
>
> Hmm.  Maybe this?
>
> diff --git a/kernel/utrace.c b/kernel/utrace.c
> index f46854b..0000000 100644
> --- a/kernel/utrace.c
> +++ b/kernel/utrace.c
> @@ -1740,8 +1740,6 @@ static void finish_resume_report(struct
>  				 struct task_struct *task,
>  				 struct utrace *utrace)
>  {
> -	finish_report_reset(task, utrace, report);
> -
>  	switch (report->action) {
>  	case UTRACE_STOP:
>  		utrace_stop(task, utrace, report->resume_action);
> @@ -1829,6 +1827,8 @@ void utrace_resume(struct task_struct *t
>  		report.action = UTRACE_RESUME;
>  		list_for_each_entry(engine, &utrace->attached, entry)
>  			start_callback(utrace, &report, engine, task, 0);
> +
> +		finish_report_reset(task, utrace, report);
>  	}
>
>  	/*
> @@ -2147,6 +2147,7 @@ int utrace_get_signal(struct task_struct
>  		 * as in utrace_resume(), above.  After we've dealt with that,
>  		 * our caller will relock and come back through here.
>  		 */
> +		finish_report_reset(task, utrace, &report);
>  		finish_resume_report(&report, task, utrace);
>
>  		if (unlikely(fatal_signal_pending(task))) {

Afaics this fixes nothing.

Yes, this avoids the unnecessary utrace_reset() in utrace_get_signal(),
but sometimes we need utrace_reset() here, now it is never called.

Suppose a tracee is stopped (TASK_TRACED). The tracer does
utrace_control(UTRACE_SINGLESTEP). This sets ->resume = UTRACE_SINGLESTEP
and wakes up the tracee.

Right after that, the tracer does utrace_control(UTRACE_DETACH). This
calls mark_engine_detached(), but utrace_do_stop() do not change ->resume,
it is still UTRACE_SINGLESTEP.

Now, the tracee enters utrace_get_signal(), notices ->resume < UTRACE_RESUME,
sets utrace->resume = UTRACE_RESUME and does

		if (resume > UTRACE_REPORT) {
			/*
			 * We only got here to process utrace->resume.
			 */
			report.action = resume;
			finish_resume_report(&report, task, utrace);
			return -1;
		}

Now that finish_resume_report() does not do finish_report_reset(),
we call user_enable_single_step() and return. Unless we have another
engine which triggers utrace_resume() eventually, UTRACE_DETACH can't
be completed, utrace_detached_ops engine is "lost".

UTRACE_STOP can be lost in the same way, with or without this change.
mark_engine_wants_stop() sets ENGINE_STOP, but since we reset ->resume
it can't be noticed until some engine sets ->resume = REPORT/INTERRUPT.

> > 5. utrace_resume() has the same problems.

Including the possibility to miss UTRACE_STOP or UTRACE_DETACH.

And,

> > 6. But! I think utrace_resume() was always wrong here. Because it
> >    calls start_callback(events => 0) and thus we nevet set ->takers.
>
> I think that change covers these too.  What do you think?

I think no. finish_report_reset() will always call utrace_reset()
because start_callback(event => 0) can never set takers.

If report.action != UTRACE_REPORT, then we don't call utrace_reset().
But, like above, sometime it is needed to complete UTRACE_DETACH.

Oleg.




More information about the utrace-devel mailing list