bug-hurd
[Top][All Lists]
Advanced

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

Re: emulating no-senders notifications in L4?


From: Niels Möller
Subject: Re: emulating no-senders notifications in L4?
Date: 20 Dec 2001 11:35:48 +0100
User-agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.1

Marcus Brinkmann <Marcus.Brinkmann@ruhr-uni-bochum.de> writes:

> I think basically, you have described that we need a seperate port server
> that maintains port rights.  I guess so much was already clear.

Perhaps I should state some more of my assumptions: (i) L4 provides no
security features that can be used to directly implement the
capability-like access control needed for Hurd rpc, and (ii) for most
Hurd rpc, the separate message and reply rpc:s aren't essential, and
neither is the queueing of messages (I can expand on these points, but
I'm doing my best to be brief, this message is quite long already).

> Well, at least nobody offered other ideas so far, although you risk
> to loose all the performance gain this way.

I'm thinking about the performance, and what matters is what happens
in the critical path for the majority of rpc:s. If some features, like
no-senders notification and transfer of port-rights between tasks, is
slower, that is of secondary importance.

> For example, you have to destroy all port rights if the port receive
> right is destroyed, and you can't meet such requirements with a
> fixed size linear array.

It can (and should) of course be a dynamically sized array. The
important thing is that it is *linear*. If you also need the inverse
mapping from each receive right to all the corresponding send rights,
you can use any auxillary data structures for that (a separate array,
or extra link fields the linear port-rights array), but it won't be
used by the crucial rpc-forwarding code.

What put me on the track to the design was this idea: The port-right
server should not need to do much work when forwarding an rpc.
Therefore, the sending task/thread must help the port-rights-server.
It should include, in the rpc, all information that is needed by the
port-rights server to *quickly* validate the security constraints. And
an index into a global array of all port-rigths seems to be the
information that minimizes the amount of work needed.

> There are other requirements, like if you insert a port right into a
> task, the port right must have the same number if a compatible right
> for the same port already exists (like, if you get a send right for
> a receive right, it is the same port name number). Such requirements
> make it necessary to include some reference counting.

This is part of the port-right management and transfer rpc:s, so as
long as it's practical at all, it's performance is not terribly
important. I think it might be possible to do the reference counting
outside of the port-rights server, in the task owning the right(s);
IIRC, there's already code in libports to do something similar.

> However, this is only the beginning, and as you illustrate, message
> forwardingis achievable (btw, the mach ipc/ stuff should also have all
> the port name translation stuff etc you did not cover).

What is port-name translation, and what is it needed for?

> The problem you face with no-sender notifications (and, to some extend I
> would gess, also with send-once rights), is that the task termination must
> destroy the port rights, and when that happens, you have to make all the
> right guarantees for no-sender notifications and send-once rights.

Again, the performance of these mechanisms are not terribly important,
but they have to be reliable. Either L4 (or some task-management
server) must be hacked to notify the port-rights server on task death,
or the port-rights server could prod all tasks once in a while to see
if they are dead or alive. Some task-death notification mechanism is
needed, but the details seems orthogonal to the rpc and port-rights
mechanisms. 

> > The port-rights server can be quite simple. It has two essential data
> > structures:
> > 
> >   struct port
> >   {
> >     /* Perhaps the owner field should not be a task_id, but instead  
> >      * a thread_id corresponding to a dedicated thread in the owner task.
> >      */
> >     task_t owner;
> >   };
> 
> Port rights are task based.

And the problem is that in L4, rpc happens between threads, right?
Having a dedicated thread (perhaps spawned by libports), rather than a
task, that owns some or all of a tasks port-rights, might make the
security checks faster. But if there's no performance penalty,
identifying the "owner" with a task is clearly better.
 
> In Mach, every task has an ipc space
> (and the kernel has one, too).  The port rights are managed in these
> ipc spaces.  The port itself is not owned by a task (in Mach anyway), but
> the task which ownes the receive right can be considered as the owner of the
> port.  I guess this is just the orthogonal way of seeing it, but there might
> be subtle problems shaking these relationships around.

I think this is mostly a matter of nomenclature. One could do away
with the ports array completely if one instead identifies a port with
its receive right (there can be only one, right?), and gives it its
own type. I.e.

  struct port_right
  {
    enum { receive, send, send_once, invalid };
    task_t owner;
    /* For send rights, the id of the correspondign receive right */
    unsigned port;
  };

When you mention that, I think that's simpler and better.

> You choose to ignore a main difference between Mach IPC and L4 IPC here.
> IIRC, IPC in L4 always involves sending a message, blocking, and receiving a
> reply.  In Mach, the send and receive are always seperate.

This is an important difference (probably more fundamental then the
queueing of messages or lack thereof). As I said, I believe the
separation is non-essential for *most* Hurd rpc.

> To repeat, all send rights to a port in one task have the same port name.
> I guess you were not careful enough to distinguish between ports,
> port-rights and port names (a mistake easy enough to make, as the
> documentation and the source often say "port" for "port name" etc).

I confess my ignorance here. I have no idea what a "port name" is, and
if we identify a port with its receive right, I don't see any use for
the "port" abstraction either.

> > 1. It checks that the port-right is not out-of range, and that the 
> > originator of the rpc matches the owner of the
> >    port right.
> > 
> >      if (pr >= MAX_PORT_RIGHTS
> >          || sender != all_port_rights[pr])
> >        fail;
> 
> In Mach this would be: It checks that the port name in the tasks ipc space
> is valid and denotes a receive right.  I don't see a good reason to do it
> differently in L4.

I'm not sure you're solving the same problem. Assuming the uselessness
of the builtin L4 security, any task could rpc the port-rights server
saying "I'd like to exercise the port-right 4711, please comply". So
the first thing the port-rights server has to do is to check that a
port-right with the index 4711 exists, and that the *owner* attribute
equals the originator of the rpc. IIRC, L4 *does* provide for reliable
identification of the originator of an rpc.

The whole point of this is that a lookup using a single id as key (and
then checking two numbers for equality) should be faster than a lookup
using a pair <task-id, task-specific-port-right-id> as the key.

> > Is this service good enough? Does it provide the kind of port-right
> > abstraction that the Hurd needs?
> 
> I think here the answer has to be no.

Any examples of how it fails?

> BTW, one interesting question for L4
> is:  How do you prevent that a task sends its own, faked messages to the
> server, circumventing the port right server.

The receiving task should make sure that it never ever processes any
rpc except those sent by the port-rigths server. Either by comparing
the sender of each rpc to the id of the port-rights server, or by
using some currently non-existant kernel feature to do the same thing.

Regards,
/Niels



reply via email to

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