Q: utrace_prepare_examine() && get_task_struct(target)

Roland McGrath roland at redhat.com
Thu Jul 23 02:41:52 UTC 2009


> >  		engine->ops = NULL;
> > +		engine->flags = 0;
> >  		list_move(&engine->entry, &detached);
> 
> I think this makes sense regardless.

Agreed.

> > +	 * Make sure all engine->flags and engine->ops updates are done
> > +	 * before the current task_struct might really die.
> > +	 */
> > +	smp_wmb();
> 
> I don't think we need additional barriers here or in prepare/finish,
> rcu_call() should play correctly with rcu_read_lock().
> 
> IOW, rcu_read_lock() must have enough "barrierness" wrt rcu_call().

Right, OK.

> > The other calls use get_utrace_lock to implement this guarantee.  It checks
> > that your engine is attached before it looks at the task_struct, and this
> > is (supposedly) synchronized with detach and reap safely.  (You should be
> > checking on that supposition, Oleg. ;-)
> 
> I still think EXIT_DEAD check must die ;)

In get_utrace_lock, you mean?  Do you mean it's superfluous because we can
rely on utrace_reap having cleared engine->ops before it matters to us?

> Hmm. I think without CONFIG_PREEMPT_RCU schedule_debug() will complain
> if wait_task_inactive() blocks.

Ok, I thought something like that might be true.

> In any case, without CONFIG_PREEMPT_RCU we must not schedule() under
> rcu_read_lock(), because rcuclassic considers schedule() call as
> "->passed_quiesc = true".

That's exactly what I meant by "lets RCU stuff happen".  But I was
supposing it would work because after you come out of schedule() it's as if
you'd done rcu_read_unlock();rcu_read_lock(); before put_task_struct().

But anyway, I was just illustrating what might be the very best you can do
with what we have now, to say that it is clearly inadequate.

> > This suggests perhaps utrace_prepare_examine ought to keep a task ref to
> > be released by utrace_finish_examine.  But then it's a back-door way for
> > modules to use get/put_task_struct and leak task_structs.  Is that OK?
> 
> Oh, don't ask me ;)

Well, I was hoping we might get some other people into the discussion too.

> But I think this is very natural. Yes, with this change the caller must
> always do utrace_finish_examine() if _prepare_ succeeds, but a buggy
> utrace module can leak memory or just crash the kernel in many ways.

Sure.  Of course it can do get_task_struct() today, and just is guaranteed
to leak since it can't do put_task_struct().

> Or we can export __put_task_struct(), this means more work for utrace
> user.

Right, we should be finding the utrace API that makes it easiest for users
to get things right, that's primary.  I was just concerned that upstream
folks might say that modules can't do put_task_struct() and that's how they
want it, and that being so they don't want a backdoor method to do
get_task_struct()/put_task_struct() either because whatever bad modules
they are worried about would exploit it.

> Or. Actually, when I started read utrace.c I was surprised there is
> no "task_struct *tracee" in "struct utrace_engine". Perhaps engine
> itself should ping task_struct ? And engine->tracee can be very handy.

I don't think a detached struct utrace_engine should pin any task_struct.
That just makes utrace_engine_{get,put} a proxy for {get,put}_task_struct.
And if it doesn't, then it's not much different from what we have now, is
it?  That is, given unpredictable asynchronous detach because of SIGKILL or
mt-exec + parent wait or self-reaping.  Perhaps I'm wrong to want to avoid
keeping task_struct refs like that.  I'm open to opinions.

There are other API reasons that I never wanted a task_struct pointer in
struct utrace_engine.  But we can get into those later.  It's moot if we
concur with my idea that holding task_struct refs past detach is bad.


Thanks,
Roland




More information about the utrace-devel mailing list