l4-hurd
[Top][All Lists]
Advanced

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

Re: new exec server protocol


From: Marcus Brinkmann
Subject: Re: new exec server protocol
Date: Wed, 21 May 2003 14:37:57 +0200
User-agent: Mutt/1.5.3i

On Wed, May 21, 2003 at 10:04:55AM +0200, Niels Möller wrote:
> Marcus Brinkmann <address@hidden> writes:
> 
> > Such issues are answered when looking at the details.  For example, proc
> > assigns PIDs to every task.
> 
> It's a little hard to sort this out when we don't know what the proc
> server is like, but I guess we're now considering the model that the
> proc server keeps a one-to-one mapping between tasks and pids.

Yes, that's what is done currently, and everything that we didn't overthrow
yet stays as it is for the basis of a discussion :)

> Then the problem is that during the exec, we have two tasks
> representing the same process. Thought experiment: If you kill -9
> 4711, and happen to do that at a moment when process 4711 is in the
> middle of an exec, *both* tasks should be killed.

Right.  This is even mentioned in the exec server code:

      /* XXX there is a race here for SIGKILLing the process. -roland
         I don't think it matters.  -mib */
      if (! proc_task2proc (psrv, oldtask, &proc))
        {
          proc_reassign (proc, newtask);
          mach_port_deallocate (mach_task_self (), proc);
        }

So Roland saw the problem and Thomas said he doesn't care.  Now, as you
point out below, this is really similar to the boot + subhurd tasks case,
where SIGKILLing boot should kill all tasks in the subhurd as well.

So, as we are going to implement the accounting ID anyway, we can also fix
this race I guess.
 
> Having to allocate a new pid just to have two tasks for a short while,
> doesn't seem right to me.

It's definitely hogwash, but necessary currentyl a there are no accounting
IDs and you can not get at a task otherwise in the Hurd.

> But if we do that, does it matter exactly
> when the old pid is transferred to the new task? It may be simpler to
> do it early, like
> 
>   Old Task            New Task
>   Create new task
>   Swap pid with the new task
>   ...

You want to do such things as late as possible, when you have established
that the exec won't fail (or it is too late to recover).  I am not sure
if that's the point above.  In particular, if we hang in waiting for the
startup message, it would be nice to make this interruptible, so ^C can
recover from a hanging exec.  If we create a new task (and we seem to have
settled on that), then you can keep all state until you give your PID away,
so this should be the last step.

> Just to get something a little different, consider a task server
> associating accounting id:s to tasks, and voluntarily proc server
> registration (i.e. there's no one-2o-one mapping between tasks and
> pids).

Your example is good, I like it.  I keep it here for reference.
 
> A PID is an object handle to proc. Consider process 4711 (associated
> with the accounting id 4711 in the task server) performing an exec:
> 
>   Old task              New task        Comments
> 
>   Arrange handles and other
>     state to be transferred
>     to the new task
>   ...
> 
> | Create new task                       (the task inherits acounting id 4711)
> | Setup pager
> | Start task
> |                       Fault in
> | Grant stub page                       (besides code, the page should include
> |                                        the task id of the old task)
> |
> | Send startup          Receive startup (transfers object handles,
> |   message               message        including <proc, 4711>,

This would be more like

                                        Send startup message
  | Reply startup message
                                        Receive reply


> |                                        and a memory blob)
> | Suicide
>                         ... later, in startup code ...
>                         Register with proc
> 
>                         Use the handles and memory blob from
>                           the startup message to initialize libc
>                           (fd:s, cwd, etc).
> 
> In the last step, registering with proc, the task already has an
> object handle that gives it control over PID 4711, so it should send a
> registration message saying "I want to be a POSIX process, and I
> already have the PID 4711". The call could even block in proc until
> all other tasks that own a object handle for PID 4711 have died.

I am unsure.  I think it is possible to do it that way.  There are a couple
of things you want to work out, like what to do in the secure case.  

> Actually this proc registration is a little fuzzy to me. The
> POSIX-process object in the proc server already exists (during the
> exec, at any time it will have one or two references), and the new
> task already owns a handle to it, implying that the proc server
> already knows about it and has requested death notifications for it,
> etc, so what is the registration about?

A pid port itself only says you have control over the process it doesn't
mean you are the process.  However, I have not yet studied the proc server
in great detail, and there are certainly some crooks because of POSIX
requirements etc.

> Note that during this exchange, a kill -9 4711 at any time will work
> as expected. It sends a message to proc whch does access control, and
> then proc sends a message to the task server, "terminate all tasks
> with the accounting id 4711".

This is what I like best about the idea.  How exactly proc registration will
work out is a bit of a different issue.  For example, the old task already
has control over itself and over the new task.  So I wonder if you really
need to give the new task control over the old task.
 
> What I like about this exchange is that the exec handling proper (the
> section marked with | above) need not know about proc, it only needs to
> talk to the task server, and it needs to know the protocols for
> transfer of object handles.

Right, that's also nice.  I hope that with a clearer understanding about how
it should work in detail some things will fall in line naturally.  The
accounting ID thing turns out to be very useful here.

I am unsure about the whole proc registration issue.  I will have to read
some proc code before going on with that.

One important new issue (which forced me into some requirements for the
protocol) is that of holding a reference to task IDs.  For example, a
created task must automatically get a reference to the task ID of the task
that created it, so that it will know for sure that the startup message
will reach its intended target.  (How to get that thread ID of the creator
is another issue, I don't know where to put it - on the stack?  Maybe the
task server should provide a bootstrap handle - a simple uninterpretet word).

It should later drop that reference after completing the startup (it might
need to hold on the task ID anyway because of send rights it has to the old
task, in case the old task passes object handles to itself in its startup
reply - but if not, it should release the task ID).  This will ensure that
it sends the startup message to the correct thread, and not to a new thread
that happens to have the same ID numbers.

However, the other way round the same thing holds:  The creator has to get a
reference to the new tasks task ID before the first thread is activated.
I would say that the task handle itself could constitute such a reference.
That is especially important if we use the task ID as object ID, but that is
an implementation detail.

For the suid case, this is problematic, as the filesystem server can not
give the old task the task handle, but it also can not communicate a
reference to the old task without race or complicated upcalls (I pondered
several options and they all sucked).

A way out of the conflict is to let the old task create new task and moving
the task handle to the filesystem server after holding a reference to the ID.
The filesystem server would have to make sure that the user uses the same
task server as itself, and then can safely accept the handle.  It would then
call an operation in the task server that would terminate any other send
rights except the one the filesystem server is holding (ie, task_revoke as
io_revoke for I/O objects).

This is a bit more complicated, but the alternative is that the old task
provides all the data to the filesystem server, and this is slow and error
prone as there are potentially many object handles to move.

I don't think that the requirement to use the same task server is critical.
Because IPC is tightly bound to task and thread IDs, everybody has to use
the same task server anyway (rememebr that references to task IDs establish
that you can reliably ensure you always talk to the same partner).  So, if
any of the two, fs or user, use a proxy task server, they have to ensure
that in the file_exec case they pass and validate a task handle to the
originial root task server instead the intermediate ones.  If the user uses
a proxy, the proxy must then provide a "get underlying task object" call. 
If the server uses a proxy, it must provide a "wrap task handle into proxy
object" call if it wants to use that for the suid call.  Nothing unsolvable.

Thanks,
Marcus


-- 
`Rhubarb is no Egyptian god.' GNU      http://www.gnu.org    address@hidden
Marcus Brinkmann              The Hurd http://www.gnu.org/software/hurd/
address@hidden
http://www.marcus-brinkmann.de/




reply via email to

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