l4-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Task server thread and task allocation/deallocation interfaces propo


From: Matthieu Lemerre
Subject: Re: Task server thread and task allocation/deallocation interfaces proposal
Date: Thu, 10 Mar 2005 00:04:39 +0000
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.0.50 (gnu/linux)

> At Wed, 09 Mar 2005 16:03:39 +0000,
> Matthieu Lemerre <address@hidden> wrote:
> >
> > Marcus Brinkmann <address@hidden> writes:
> > >
> > > I disagree.  We need task groups, so we can kill off child tasks which
> > > did not register themselves with the proc server.  This is not only
> > > important for sub-hurds like boot, or similar applications where a
> > > process doesn't register child tasks with the proc server, but also to
> > > clean up after a partial fork() or exec(), which otherwise could leave
> > > behind unregistered tasks in a broken state.
> > >
> > > The task group ID can just be a protected word that can only be set by
> > > a privileged server, for example proc, and which is inherited at task
> > > creation.  proc could set this word to something as simple as the PID.
> > >
> > > Killing a task should kill all tasks with the same task group ID, too.
> > > This ensures that no stranded unregistered tasks are left behind.
> >
> > Shouldn't we instead pass a flag to tell if we want to destroy all
> > tasks with that group ID, or just this one?  If I think of a
> > particuliar application which create a new task and then wants to
> > destroy it, it must contact the proc server just to change the task id
> > of that task and make sure the task server won't destroy itself.
> 
> Yes, good point.  Total agreement.
>  
> > Eventually, tasks within the same group ID could be organized in a
> > tree structure, and when upon task destruction the task server would
> > kill all of its sons, recursively.  So killing the root of this tree
> > would kill all tasks.
> 
> I considered this, but uhm, I am unsure.  I don't think it should be
> the burden of proc (or anybody else) to figure out what the root of
> the tree should be.  Consider you have three tasks in your ps output,
> only one is registered with proc, the others have the same task group
> id, but the one registered with proc is _not_ the one that is the root
> of the tree.  If you want to kill all three you would then need to try
> them in turns.

I think I found a way so that the proc server has always a task control
capability on the root of the tree.

> 
> Or you can offer all three options: kill one task, kill whole task
> id group, kill hierarchy.  But I question the usefulness of the last
> feature: Who wants to kill all tasks and its (unregistered)
> children?  If you need to do that internally in your task id group,
> you can manage the hierarchy yourself just as well according to your
> own policies.
> 
> I think for our system code, we only need the task id group feature.
> If I am missing something, let me know.


I've been thinking a bit more on this, and I can write more clearly
the two proposals of interfaces:

(Let's call the calling task A, the created task B, the task server
TASK and the proc server PROC).

*With a group ID:

**Tasks are organised into lists, all of them have the same group ID.

**Process registration is done just by asking a proc handle to the
  proc server (passing a task info or control capability on B as its
  argument).
  
  Then PROC calls TASK, passing its manager capability , B's task_id,
  and a process ID.  TASK put B in a separate group, using the process
  ID as the group ID.

I don't know how PROC can check that A has a task control capability
on B.  Maybe A should copy its task control capability on B to proc.

**Process destruction happens like this:

The group ID is indeed useful because not every task is registered as
a process.  So when proc has a task control over a task, it can just
ask TASK to destroy all tasks in the group.  But, when it does not
have it (because the task registered does not exist anymore), it has
to request it from the task server (or just use its task manager
capability passing the group ID to ask for destruction of all tasks in
the group).

So in any case, TASK has to have something which makes a
correspondance between the group ID and the list of task_t in that
group ID.  Surely an ihash.

*Without a group ID:

**Tasks are organised into trees, there is parent-son relation between
  them.

**Destroying a task will always destroy all of its unregistred
  children tasks.  This way, once the proc server has control over the
  root of a task tree, it always has the possibility to destroy all
  the tasks in the tree. (If it was possible to just destroy a task,
  the root of a task tree could be destroyed alone and the other tasks
  would continue to exist, and the proc server would have no control
  over it).

**Process registration happens like this:

Task A calls PROC to register task B as a new process. PROC uses its
manager capability on TASK to set B as the root of a tree. TASK
returns to PROC a task control capability on B.  PROC associates that
task control capability with a new PID, and gives a proc handle to A.

(Again, maybe A has to copy task control capability on B to PROC.)

This ensure that for every task in a task tree, PROC has a control
capability on its root, and so can destroy it.

The advantages I see with this second scheme is that there is no need
for group ID, nor for ihash.

There is also less RPCs to implement: task destruction is the same for
normal tasks and for the proc server, task manager capability is only
used to set a task as a a root of a task tree.

Also, this solve the setuid exec problem.

Maybe using a group ID is simpler than what I thought, but it seems to
me that using trees would finally be easier to write.

> 
> ? It would also solve the problem below, without
> > changing the exec protocol.  On the implementation, I don't think that
> > it will make things more complicated, since I think that killing every
> > tasks on a group ID already require that the task_t are linked
> > together as a linked list.
> >
> > I also wonder why is the group ID word useful.  Isn't links between
> > task_t nodes sufficient for storing relationships between the tasks?
> 
> Certainly, if you never want to export the relationship, then just
> keeping it internal is sufficient, and only indirect operations are
> necessary (create new task group for this task, etc).  But I think it
> is useful to see the relationship clearly in the output of the ps
> program.  So, somehow we need to export the information which tasks
> belong together.  You are right that this could be done in more
> indirect ways than naming them.  It's worth considering.
> 

I think that the proc server can just associate its PID with the task
control capability of the root task of the process's task tree. So,
proc name the process, not task.

I also think that any task holding a task info capability on a task
should be able to know its hierarchy (or, in the case of group IDs,
knowing all the tasks in a group).  So proc could get the informations
like this.

> 
> > task_set_group_id could be replaced by
> >
> > task_reparent (l4_thread_id_t task_server,
> >                hurd_cap_handle_t parent_or_privileged_capability,
> >                hurd_cap_handle_t task_to_reparent)
> >
> > and the new parent of TASK_TO_REPARENT would be set to
> > PARENT_OR_PRIVILEGED_CAPABILITY in case of it beeing a normal task
> > control capability, or there would be no parent if
> > PARENT_OR_PRIVILEGED_CAPABILITY is the proc control capability.
> > (This may be a bad interface).
> 
> Actually, it's similar the same I came up with when writing the reply
> to your last paragraph ;) I would phrase it a bit differently, though.
> First, this is not about parent and child relationship (my previous
> mails may have been misleading), so it should be task_set_group.
> Also, I think your arguments are quite confused.  As I said earlier,
> RPCs should always go to a task object (in this case,
> task_to_reparent) and not to the "task server", which is not an
> object.  What may be hitting you here is that I didn't write the
> libhurd-cap-client library yet that wraps this up.
> 

In fact, I was writing a pseudo task-user interface.  Last time I
didn't wrote the l4_thread_id_t argument, and Neal pointed that out,
so... :)

So in fact I made the RPC on parent_or_privileged_capability, which
wasn't good also.  (In fact I did that because I would have put the
task manager capability and the task control capability in different
classes, so that the task server does not call the same function for
both capabilities, thus acting differently.  But I don't know
libhurd-cap-server very well, so I'm sure this can be done nicerly as
you proposed :))

> 
> /* Set the task group of task TASK to the same as the one of task
> GROUP_SPECIFIER, unless GROUP_SPECIFIER is HURD_CAP_NULL, in which
> case a new group is created for this task.  */ task_set_group
> (task_t task, task_t group_specifier);
> 
> > Killing all tasks in a group ID also introduces the following problem:
> > if a tasks create a new task and give it to a filesystem, the latter,
> > if malicious, could kill both tasks (the passed one and the calling
> > one).  To avoid that, we should complete the exec protocol like this:
> > before giving a task control capability to the filesystem for a newly
> > created task, we have to contact the proc server to change the group
> > id of this task.
> 
> Not necessarily, if killing all tasks in the same group is a
> privileged operation only allowed for the proc server.  But you have a
> good point, it's something we have to consider and address somehow.
> If we want to allow anybody to kill whole task groups based on their
> task group relationship, we need to make sure that this doesn't
> influence fork/exec in the way you describe.  We have to look in more
> detail into it.  For now I am slightly in favor of permitting the
> kill-all operation to proc only.
>
> > > BTW, one important thing to consider is how suid exec will work.  If
> > > we want proper accounting, suid tasks should be donated by the starter
> > > to the filesystem, but the filesystem needs total control over it.  So
> > > some revoke-and-reparent operation will be needed: revoke to remove
> > > access by the original starter, and reparent to associate the donated
> > > task to the filesystem doing the suid exec.  So, the task_set_group_id
> > > should also be permitted for non-privileged users in the special case
> > > that you want to set the group ID to the group ID of your own task
> > > (rather than any arbitrary group ID).  Ie, you claim a task you have
> > > control over (you have the task capability) as belonging to your own
> > > task group (there are no security problems with this I think - there
> > > _are_ security issues with donating your own tasks to somebody else's
> > > task group).
> > >
> >
> > I think I found one of them ("a malicious filesystem could kill the
> > calling task."), and maybe a way to avoid it (the tree structure), but
> > I may miss many others.  Could you please explain a bit more on this?
> 
> Actually, you are far ahead of me - you found a potential security
> problem where I saw none (mainly because I never really integrated
> task groups into my design from ground up).
> 
> What I meant is something different: If you allow somebody to regroup
> a task to some arbitrary other task group, somebody could always hide
> their unregistered tasks into foreign task groups, for example the
> task group of a long-living system task.  This is a mild security
> problem because it means that your unregistered task is a bit more
> hidden, but also because the other task just might not expect that
> foreign tasks enter its group in some uncontrolled fashion.

So, it is actually more a security issue to accept a task than to
donate one, right?
> 
> Thanks, Marcus

Thanks,
Matthieu




reply via email to

[Prev in Thread] Current Thread [Next in Thread]