On Mon, 2004-01-05 at 12:56, Volkmar Uhlig wrote:
I think there is some misconception around about L4's thread ids. In L4 V4 (X.2 is going to become V4 after maturing) threads and address spaces are completely independent of each other....
Volkmar:
I believe that I have now asked twice for a URL to this specification. Can you send me a URL? Otherwise we will continue to waste each other's time. Please accept my apologies that, in the absence of this document, I may have done so.
If it was on one of the L4 web sites, I failed to see it.
Threads execute in an address spaces, but they can migrate. A thread ID is a communication-endpoint place holder. If you have, say, 100 threads in an address space and you want to hide that fact you can use a single thread serving as a distributer. Since we have local IPC the distribution can completely take place in user land without a kernel invocation. The worker can impersonate the distributer for the reply.
This sound like a very good thing, and it almost certainly addresses the issue of "revealed thread ids".
While I do not advocate any particular implementation strategy, let me give an *example* of one that might suffice: a simple hash table. Instead of using the PCB address as the process ID, the kernel could use H(sender kernel ID, requested-recipient-ID) as an index into a hash table and perform a single indirection (and possible hash bucket chasing) to find the process address. This is fundamentally the design proposed by Trent several years ago.
You immediately run into replacement policy issues.
If there is a faster way, great! But protection is not merely a desire. It is a hard requirement. *Every* mechanism I know about for achieving a protected interface carries a fundamental cost somewhere. You can do it with address space traversal, or you can accept the need for a replacement policy, or you can do something else, but you are going to pay *somewhere*.
My problem is that performance cannot be used to justify fundamental insecurity. Speed at the cost of correctness is simply unacceptable.
In general, I like the map/grant model very much, but in some systems it is necessary for the manager to know who has what mappings.
In these systems, having applications perform mappings directly to
each
other creates a consistency problem.
Here the argument is around IPC redirection (which is arguably expensive). By sticking an interceptor in between you know which mappings go in and out. That way you can control it. But I suspect that fine-grain control over mapping privileges makes sense. The question is what this granularity should be--page or mapping in general.
From a security perspective, the problem is with the presence of mapping
operations at all. Mapping establishes a high-speed, mutable channel, so it tends to undermine reference monitors.
My preferred model is that performing a send with a mapping on a disabled descriptor would be treated as an access violation, and a suitable exception would be delivered to fault handler. I don't want to pay any indirection cost for the legal (non-mapping) IPCs.
The problem with the first method is that it requires protected recipient descriptors.
You can transparently drop the mapping if it is not allowed. Since the guarantees whether a page is mapped or not come from the pager hierarchy it is only as strong as all combined pagers. By adding a non-guaranteeing pager in the hierarchiy you are set.
From a security perspective, failing silently is *always* a mistake. It
also makes virtualization difficult if somebody wishes to be backward compatible with the interface later. You are describing a situation in which a sender has done a known-illegal operation, but no fault is generated? This doesn't seem like a good design.
The problem with implementing only the second method is: what thread id should a page fault handler receive from a faulting thread? Restricted or non-restricted? I can argue for either depending on what
kind of system I am trying to build.
That raises the question whether the pager is privileged or not. Since the pager can be set by the thread the pager can be changed and the thread could elevate its privileges. Or did I misunderstand you?
It is a big problem for the pager to be settable by the thread. There are some systems in which this can be permitted, and other systems in which it **must not** be permitted. Because there exists systems in which this must not be permitted, the operation must (at a minimum) involve invocation of a trusted party.
Perhaps there is some third design possibility that I have not considered carefully enough, but it appears to me that a high-assurance system constructed on top of L4 must be built exclusively using trusted pagers. While it is possible, in abstract, to use untrusted pagers in a descriptor-based system (as EROS does), the L4 map/grant operations don't appear to provide enough book-keeping controls to be trusted.
shap