Re: CAOS - A CApable OS? [was: Process Authentication Groups (PAGs)] Andrej Presern (andrejp@luz.fe.uni-lj.si)
Tue, 24 Mar 1998 10:44:21 +0100

Jonathan S. Shapiro wrote:
>
> > In the CAOS model I
> > presumed a starting process is put into a 'black box', a can-do-nothing
> > position. Since this means that process can't do anything at all, I
> > introduced this initial 'request', where the process can state the
> > resources that it will need in order to become usefull. It is asumed
> > that since the process has been considered by the system as valid to
> > start, the process is allowed to state its needs.
>
> There are several problems with this model.
>
> 1. I can think of lots of reasons to start a process with no intention
> of running it. Postmortem debugging is one. The assumption that the
> process will be run is therefore a mistake. It may be a template for
> future processes.
>
> 2. In order to request resources, the process must have sufficient
> authority to execute instructions and also sufficient authority to
> speak to the requestee. This implies, at a minimum:
>
> authority to some CPU schedule
> authority to an address space, and any necessary mappings
> therein
> authority to the requestee
>
> Pragmatically, it also will in common cases require:
>
> authority to take page faults, and therefore
> authority to acquire rights to storage
>
> This is quite a lot of authority. Actually, it's most of the
> authority that most programs will *ever* need.

The program does not have to be started, since the request is stored in the file header so the system will read it when it loads (or even prior to loading) the program. This will a) prevent the process from doing anything that we don't want it to, b) allow the process to state the cpu (or other, java for example) architecture and other resources that it requires to run, and thus c) the system will know if the process will be able to start prior to actually running it, thus saving resources that would be taken up by starting the process and then determining that its requests are not permissible.

Please note that this request does not contain actual values (such as how much memory the program will need for example) but really only the means to obtain resources (that is, "I will need a function to allocate some memory"). Quantitative issues are solved in the function that handles the resource (ie how much memory is the process allowed to allocate). This allows for quite a fine grained control of the resources used.

> My belief is that resource requirements are part of the documentation
> of a program. A sufficient set of requirements is known in advance to
> get the program started, and should be provided by the party who
> causes the process to be created.

Hm. How can P1 that will start P2 know what P2 will need to execute?

IMHO resource requirements should be provided by the developer (or/and the compiler) since it is the developer who should know what the program is intended to achieve (and the compiler will know what is actually being used to do it).

> > > The best way to keep hold of the capabilities that a process needs is
> > > to grant them from the beginning and ensure that they are never lost.
> >
> > This was the original idea. The 'request' above is the means a process
> > can obtain the needed capabilities. But since in the low level we always
> > come to the terms of functions, I translated 'capabilities' to
> > 'functions that perform specific actions on objects'.
> >
> > Do you think that this translation is a good thing? I see difficulties
> > in managing unnamed objects but at the time I felt that it's worth a
> > thought.
>
> I disagree that in the low level we always come to the terms of
> functions. I think that at the low level, we always come to a
> function performed on an object. A read call is performed on a file
> descriptor. An open call is performed on a directory passing a
> string. etc. etc.

>

> In fact, I cannot think of even one example function that is
> unconditionally safe on all objects, unless it is one that returns
> immutable state. The result of such a function never changes, and the
> function is therefore not useful.
>
> Even being able to ask an arbitrary object something very simple like
> it's size provides a communication channel between parties.
>
> Indeed, the fundamental security issue is not what functions can be
> performed, but what objects can be reached. Functions are very
> broadly classifiable into activities that provide read, write, or
> read-write access to an object. That plus the identity of the initial
> objects is sufficient to make arguments about information flow in the
> system, and therefore about the security of the system overall.
>
> Conceptually, then, it isn't the functions that make or break the
> security of the system; it's the object identities. From experience
> in the theoretical security work that Sam Weber and I did, I can tell
> you that the hard part of the security proofs is establishing the
> accessability and mutability graphs. The difficulty lies not in the
> access/mutate part, but in determining which objects lead to access to
> other objects and how.

Hm.

> > > In a capability system it cannot. The child either holds a capability
> > > or it doesn't. There is nothing in the capability that records where
> > > the capability came from. Once transferred, the sender has no control
> > > over how the receiver uses the capability.
> >
> > Mhm. So every capability can in every point in time have only one
> > holder?
>
> I'm not sure where you got that idea. Perhaps I should have said
> 'transferred or copied.'
>
> Think of pointers. If you and I each hold pointers to the same
> object, the pointers live at different locations but contain the same
> bits. The question is whether you wish to think of these as one
> pointer or two. Depending on the purposes of our discussion, either
> answer could be correct. To a garbage collector these are different
> pointers. For programmer purposes it's often sound to think of them
> as the same pointer.
>
> In the same way, if you and I hold separate copies of a capability to
> the same object conveying the same authority, we can speak in either
> sense correctly. Each capability has a unique holder, but
> pragmatically it is useful to think of two parties as holding the same
> capability.
>
> What I was really trying to say is that a capability does not record
> information about how it has been copied. The holder knows what
> capabilities they hold, in the sense that they can learn by experiment
> what those capabilities seem to do. The capability does not know its
> holder, and in most capability systems a capability invocation does
> not convey to the recipient any information about the calling process
> (such a design would be a hybrid protection model).
>
> This is part of what it means to say that holding a capability is a
> necessary *and sufficient* condition for performing the
> capability-authorized actions on the object designated by that
> capability.
>
> > > Observation: while you could build a modified capability system that
> > > used hierarchically constrained access rights, you would create to
> > > problems in doing so:
> > >
> > > 1. Undesired communication channels inherent in the access control
> > > hierarchy itself.
> > > 2. Variable-length capabilities.
> > >
> > > >From an implementation perspective, capabilities *really* want to be
> > > fixed size. Imagine programming a system with variable length
> > > pointers...
> >
> > Hm. Can you give an example of the two mentioned problems?
>
> Sure. Suppose you record hierarchy. You either must bound the depth
> of the process hierarchy so that you can bound the space required to
> record that hierarchy of transfers, or you must dynamically resize the
> capability. If you dynamically resize the capability then
> preallocating storage for it becomes a mess -- thus the analogy to
> variable size pointers.
>
> Worse, the space for the dynamically sized capability must come from
> somewhere, and the amount of available storage in the storage agent is
> a source of information transfer. By combining downward transfer
> (allocate storage) with deleting capabilities (free storage) and some
> error correction coding, a fairly high bandwidth channel can be
> constructed by querying the amount of space available from the storage
> manager (several hundred kilobits/second). The guy on the inside
> transmits data by moving the available storage up and down. The guy
> on the outside watches the changes in the water level.
>
> Actually, there's a worse problem. In general, hierarchical transfer
> isn't real desirable. You want any two communicating processes to be
> able to transfer capabilities, which violates the hierarchy model.

>

> > > The graph shows lines for several choices of X, several crossing
> > > speeds, and several numbers of crossings.
> >
> > Do you have an URL on this?
>
> Sorry, I'm afraid I don't. I believe it appears in one of the L3/L4
> papers, which you can find off the L4 web site. I'ld send a URL for
> that, but Penn's web server isn't cooperating at the moment.
>
> > I picked the term 'capability' because I felt it described quite
> > well the basic idea of the model that I'm trying to develope. I was
> > not aware of existance of any such systems at the time...
>
> No problem. New ideas deserve new, catchy names.
>
> > The idea that I was (probably in a very clumsy way) proposing is
> > quite similar to a hyerarchycal capability model that you were talking
> > about.
>
> Hmm. If you will not take it amiss, let's talk about whether the
> hierarchical restriction buys you. I'ld propose two questions to
> start with:
>
> 1. Why is it useful to restrict capabilities to hierarchical
> transfer?
>

> 2. What (if any) marginal security is provided by such an
> restriction?
>
> 3. What (if any) simplification of engineering arises from this
> restriction?
>
> In fairness, I should say that I think the answers are (1) it isn't,
> (2) none, (3) none, actually more complex. I think I can support
> those answers.

Hm.

You're right. While at the beginning it seemed to be the obvious decision, it later turned out to be one of the major problems that I encountered (and one for which I still have no real solutions). This is largely connected to the way I do process startup, since the parent is the only one who can provide a process with the needed resources, and this inevitably leads to a hierarchical structure.

The idea was to provide a sort of a microkernel-like organization of the system, where each child had less (or at most equal) true privileges compared to the parent. But since it is sometimes desired that the child has more privileges, I was looking for a way for the child to obtain them. One solution was to use a two-way communication where a child could request that the parent (and all the levels above) hands over its privileges (that is, lower level functions, all the way to, for example, the entry to the supervisor mode of the processor, where the process had complete control over the machine).

The problem was that some 'capabilities' (functions really) were provided by processes in the different subtree than the one that the child was executing in, so the path could turn out to be quite long. I was considering to simply skip levels (if approved, no checks done anymore) all the way to the desired privilege level so that the process had direct access to the function thus breaking the hiearchy, but haven't gotten to think about it much yet, since the whole thing was getting a bit too complex for my taste.

Oh, well:) I guess this means back to the drawing board:) I have a few other ideas and concepts that I feel are worth exploring, but perhaps I really should read some of the documentation that is available on the web so I don't reinvent the broken wheel again:)

Andrej

-- 
Andrej Presern, andrejp@luz.fe.uni-lj.si
PGP public key: http://luna.s-gimsen.lj.edus.si/~andrejp/key.asc