Comments on Lex's "Object as Capabilities in Squeak"

Mark S. Miller markm at caplet.com
Wed Jan 29 22:37:06 UTC 2003


[cross-posted to squeak-dev and e-lang]

In rereading Lex's paper, Anthony's paper, and the Squeak-E thread on 
squeak-dev, there's a lot to say. I'm tempted to just jump right in and try 
to talk you guys out of dynamic scoping, just as Norm talked Dean and I out 
of it in the Joule-design days. I do think this is the most important 
remaining issue (though not the last one), but I think I'll instead build up 
to it slowly. It took Norm a long time to convince us, even when we already 
had a large base of common premises. I think I should first see what 
premises we all do have in common, and what premises we can come to have in 
common. I will start by commenting on Lex's paper. From there, I expect to 
go on to Anthony's, and then to the email thread.

On first reading Lex's paper, I assumed that we were all trying to solve the 
same set of problems, and that Lex (and squeak-dev) has not yet realized the 
incompatibility costs of addressing these. On rereading Lex's paper, I think 
this is still probably true, but it strikes me that y'all may in fact only 
be interested in a genuinely less challenging subset of these problems, in 
which case y'all can plausibly address these at a much lower incompatibility 
cost. In any case, none of us can make informed choices until we understand 
what these tradeoffs are. This will be new exploration for me too, as so far 
I've mostly only thought about the more complete problem.

All unattributed ">"-style quotes are taken from Lex's "Objects as 
Capabilities in Squeak", Aug2000 version, at 
http://minnow.cc.gatech.edu/squeak/uploads/2074/sandbox.html .


For a first attempt at a taxonomy, let's try the following four levels of 
ambition:


1) Applet-like security: 

>In order to widely share Squeak pages across the Internet, it will be 
>necessary to engineer a safe display environment--a sandbox--for such pages.

I've never heard of "Squeak pages" before. (My apologies again for my 
ignorance of Smalltalks post PPS2.5.) However, from the meaning I'm 
guessing, this sounds like a statement of purpose that could as well have 
been stated for Java applets. I would characterize this as:

Instantiate safe "mobile" code. The applet-like instance can render into its 
window, receive ui events and interact with the user, but cannot exercise 
any of the user's authority nor modify any of the user's persistent state -- 
beyond the state understood to be part of the applet-like instance itself.

The problem with applets is, they're useless. That's why you don't 
hardly see any. Their uselessness is precisely derived from a) their 
starting position of lack of authority (good), and b) the inability to 
incrementally and dynamically authorize them--well after they're 
instantiated--to do some useful job (bad). (See #2 below.)

For those seeking only this level of ambition, I see nothing at all wrong
with the mechanisms Lex proposes. (This is *not* to imply that these 
mechanisms aren't fine at higher ambition levels, or to fault Lex's paper 
for not articulating a more extreme goal-set. It's taken me decades to go 
from working on these problems to being able to explain what problems I'm 
solving, and I still feel like I haven't reached the halfway point on that 
journey.) I point this out so we can keep track of ambition-architecture 
correspondences as we go.

When I say "I see nothing wrong", I'm referring specifically to Lex's 
paper. In particular, please don't take this remark as an endorsement of 
dynamic scoping (currently being discussed on squeak-dev) even for this 
first level of ambition. The paper transforms globals into per-process 
variables, where a process serves much the same function as a ClassLoader 
unit (the ClassLoader, all the classes loaded by that loader, and all 
instances of those classes) in Java's applet architecture. Either of these 
mechanisms would be candidates for the least incompatible change to 
Smalltalk in order to rescue "globals", rather than replace them. If 
relevant objects are partitioned between such processes (as I take Lex to be 
mostly trying to do), then the two mechanisms may even be close to 
equivalent. If y'all's ambitions really do stop here, it would be good to 
argue their relative merits, but I'm hoping we won't need this argument.

(If the relevant objects cannot be so partitioned, such that the same object 
can be accessed from multiple such processes, then I would indeed argue, 
even for this ambitiion level, that process-scoping is wrong while loader 
scoping still looks acceptable. But I'll wait on that argument until it 
proves necessary.)

OTOH, Dynamic scoping goes well beyond trying to preserve compatibility in 
order to rescue old code, and instead introduces a new linguistic concept 
substantially more complicated and questionable that per-process-scoping or 
loader-scoping, solves no further security problems, and moves Squeak away 
from the idea of object oriented programming. But I'm getting ahead of 
myself again.


2) Caplet-like security, one level deep:
As safe as applets and as useful and usable as applications. 
http://www.skyhunter.com/marcs/securityTechComparisons/framed/goldenTriangle.html

Lex's reference to "pages" and the text spent on UI access leads one to 
expect that the user is able to operate the ui of untrusted code -- 
applet-like so far. In order to get beyond the sterile uselessness of 
untrusted Java applets, the user needs to be able to grant a caplet-like 
instance further specific authorities, as appropriate in order for the 
caplet to do some useful job on behalf of the user.

Authorization must happen by intuitive user interface actions, enabling the 
user to "naturally" (without thinking they're thinking about it) maintain a 
model in their heads on the bounds of authority they've granted to different 
caplets, so they can naturally reason about the bounds on risk to themselves 
when considering another act of authorization.

See http://www.sims.berkeley.edu/~ping/sid/ for an elaboration of these 
principles, and http://www.skyhunter.com/marcs/narratedIntros.html for a 
concrete system with UI (CapDesk) that implements 8 out of 10 of these 
principles. (Crucially, CapDesk turns the UI into a capability system using 
the same philosophy we're applying at the language level: turn each act of 
designation into also being the corresponding naturally-implied act of 
authorization. The user does essentially the same things he normally does. 
These user-interface actions are now understood to also have the security 
meaning that one would naturally expect. This is a bold claim. Look at the 
pages to see the system in action.)

The above systems go out of their way to look conventional while obeying 
these principles. It would be a very different, and quite exciting, 
investigation to apply these principles to Croquet.

Before speaking to what any of this might have to do with the architectural 
issues being discussed, I'll first wait to get a sense of whether y'all are 
interested in pursuing these overall goals.



3) Privileged security abstractions
4) Unprivileged security abstractions

By a "Security abstraction" I mean something like the revocable forwarder 
shown as Figure 6 on page 6 of http://lfw.org/ping/capmyths/usenix.pdf . 
Such a security abstraction often stands between several different 
interests, receiving and manipulating authority on behalf of each of them.
Such abstractions are also "deputies", as explained starting on page 11 of 
the same paper.

Modern CPUs have a "privileged mode" and an unprivileged, or "user" mode. 
E is built on Java. Java code in the E implementation is like code run on 
the privileged instruction set, and E code is "user code". Lex has a similar 
distinction between "privileged" and "unprivileged" Smalltalk code, and I 
suspect he has to. No matter how similar these are made, they constitute two 
separate languages which need two separate names. Let's call then "Squeak" 
and "SafeSqueak". Unconverted legacy code that remains in the image is 
Squeak code, all of which is in the TCB.

By "privileged" security abstractions, I'm attempting to define an 
intermediate level of ambition that may require less principled support. 

The issue is, how complete a language is SafeSqueak? It's easy to make it 
expressive enough to do applet-like things (#1 above). It's next easiest to 
also enable it to accept and use authorizations reified as 
capabilities-as-objects (in support of #2). But is the system such that 
SafeSqueak objects may find themselves between other SafeSqueak objects, 
each representing different interests? Is SafeSqueak a good language 
for writing security abstractions as deputies, in order to bring about 
cooperation without responsibility in such a system?

If the answers are yes, then we're in #4. Otherwise, different unprivileged 
interests may still be represented by SafeSqueak code, but the security 
abstractions that stand between these interests are all Squeak code. In this 
case, we're in #3. To determine which of these apply, a good exercise is to 
try to rewrite the money example from 
http://www.erights.org/elib/capability/ode/ode-capabilities.html#simple-money
in SafeSqueak. I would like to see Squeak evolve into a system that can 
support #4. I would hope y'all do as well. Only with #4 can SafeSqueak be a 
self-contained language, and Squeak become a shrinking legacy.


A note on consensus:

Above I've repeatedly talked about finding out what "y'all" want or believe. 
I don't imagine y'all are a collective, any more than I imagine e-lang is. 
I'm just referring to the sense of the community that I hope to gather from 
the reactions.

**********************************************************************

In the section
>The ObjectInspector Capability
a set of methods are listed. #realBlockFor: is missing, as it is mentioned in 
the section
>The ObjectInspector Capability
as
>realBlock := ObjectInspector realBlockFor: aRestrictedBlock

The ObjectInspector design seems sound, as a super-powerful part of the TCB, 
to be closely held within the TCB, in order to build things like privileged 
debuggers. (Unprivileged debuggers are hard, even in #4 architecture! KeyKOS 
and EROS do it. E does not.)

It seems to me that ObjectInspector can be refactored in a way that would 
make it both more convenient, and would naturally subdivide authority, 
simply by currying it. I assume that the listed methods of ObjectInspector 
are class methods, as shown by the above quoted use. Let's say that all 
these were instead instance methods, and that ObjectInspector instead had 
one instance variable and one "new:" class method. Then, instead of writing

    x := ObjectInspector instVatOf: obj at: i

you'd write:

    x := (ObjectInspector new: obj) at: i

The cool thing is that (ObjectInspector new: obj) evaluates to an object 
giving encapsulation-breaking, meta-level access only to the state of object 
"obj" rather than the system as a whole. This is a small but crucial step 
toward non-privileged debuggers, if you wish to get there eventually.

*******************************

In the section on Literals, you seem to use immutable and read-only as 
synonyms. I find this confusing. To me, "read-only" means I can observe the 
current state but modify it. It doesn't mean the state won't change. And if 
the state to which I have read-only access does change, I expect to be able 
to observe the new state.

Do you indeed mean "immutable" everywhere you say "read-only"?

******************************

The section "Dynamic Variable" seems to be to explain only per-process 
variables. Is this right?

Could someone explain about "World"?

This section refers to "methods that are marked as privileged". Even after 
reading the later section that explains this, I didn't feel like I 
understood how this marking happens, or how the authority to do this marking 
is controlled.

>are still be able to access variables
Bad "be"

Can the same dynamic-variable-using instance be invoked from different 
processes (causing different bindings of the same use-occurrence of the same 
variable), or are dynamic-variable-using instance partitioned among the 
processes that can invoke them. Unfortunately, I suspect it's the first, but 
I'll wait to find out before arguing against it.

************************************

>Whenever code is loaded from untrusted sources, it should be loaded into 
>unprivileged methods.

I'm not sure how to read this. If the code includes methods that were 
written assuming they would run privileged, what happens?

>Processes with direct access to

I don't understand what this means, but it alarms me. In a capability 
system, we should be speaking only of objects having access to other 
objects. I know that Processes are objects, which is good, but I don't think 
this accounts for what you mean.

*****************************

Are restricted classes (as implemented by the restricting proxy) read-only? 
(I mean, genuinely read-only, not immutable.) I think they should be.

> In particular, many class methods return "self"

This reminded me that Smalltalk methods by default return self. E had a 
different but similarly dangerous policy. We found to our terror that this 
was a pervasive source of accidental security holes in our code, very much 
along the lines of the specific security hole you found with classes. Rather 
than making a custom repair for each individual case, I fear that you'll 
find you'll want returns-to-SafeSqueak to return null, rather than self, by 
default. This is the first issue I've encountered that makes Java easier to 
tame than Squeak. In Java, methods are void return by "default". ("default" 
is a funny word. It's still explicit, but is the path of least resistance.)

>required to allow access one of these
Typo: insert "to"

**************************

>An incomplete list of such methods is the following:

I would be very interested to see the complete list of methods on Class, 
ClassDescription, Behavior, and Object that you consider safe. It's much 
more important to review the list of what's allowed than the list of what's 
disallowed (though you made the right choice of which to list for the paper).

I don't understand why you allow instaVarAt: and instVatAt:put: on 
non-proxies? Does this include non-proxies written in SafeSqueak?

I completely did not understand the section "Characters and Symbols", 
probably because of my ignorance of modern Smalltalks. Could you expand?

*********************

>This proxy might or might not immediately install the cursor as requested, 
>depending on the precise security policy that is being implemented.

Implemented by whom, how? This is the first I've seen of "security policy" 
used this way.

********************

I would hope to convince you that shared-memory multi-threading, locks, 
semaphores and such should not be part of SafeSqueak. But, scoping and 
partitioning issues aside, this is a mostly separate discussion we can leave 
till another time.

********************

You don't actually explain what the issue is with Exceptions.

******************

>In particular, the following primitives should be disabled:
>
>* instVarAt: and instVarAt:put:, because they allow directly breaking 
>  confinement
>
>* at:, basicAt:, at:put:, and basicAt:put:, if the proxy has indexed fields, 
>  because they would allow directly breaking confinement

I think I understand the others, but what's the problem with #at: and #at:put: ?

>Additionally, #shallowCopy and #clone make revocation much more difficult; 
>thus, they should most likely be overridden to return the receiver instead 
>of returning a true copy.

If you can't support their contract (and indeed you can't), then shouldn't 
you just suppress them?

>Note that all non-primitive methods from class Object may be safely left 
>accessible. Since such code must consist of message sends between 
>parameters, self, and globals, user code could emulate the code even it it 
>were disabled, and so disabling such methods gives no gain in security.

Are all the globals accessible from methods on Object necessarily accessible 
by both callers and subclasses?

********************************

>A primary advantage of the submemories approach is that there is no need to 
>add a special kind of cross-memory oop

Given the stated purpose of submemories, you need to be able to reclaim a 
submemory without being able to reclaim the parent memory. This means that 
oops from the parent into the sub need to spontaneously seem to become some 
innocuous object (like null) when this happens. I think you will find 
support for this hard at fine-grain. E supports this only between vats, 
where there's an enforced indirection through intermediate objects anyway, 
and where the possibility of partition is part of the semantics anyway.

In any case, I recommend postponing further worry about resource controls 
and denial of service until these other issues settle down.



----------------------------------------
Text by me above is hereby placed in the public domain

        Cheers,
        --MarkM



More information about the Squeak-dev mailing list