[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...
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-mone... 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
Hi,
I'm trying to figure out how to write an external plugin (The specific application is OggVorbis, no promises). I'm having a hard time with it. I have reviewed the information on the Swiki and stuff in Squeak, but coul dstill use some help.
I am basically trying to 'wrap' high-level function calls in a library that do all the hard work. I think an external plugin is needed because: 1) at nearly 2 megs, the static libraries would nearly triple the size of the VM 2) FFI, while much easier, wouldn't be portable.
Here's an example of what I'm trying to do. For a C function residing in 'vorbisfile.dll' having the following prototype: int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes);
I create this method in my plugin class (using the specification system, I assume thats the best way to go):
primOvOpen: file oggVorbisFile: vf initial: initial long: ibytes |rcvr size | self primitive: 'primOvOpen' "unpleasant things"
The following questions come up: The c-land things like FILE and the struct OggVorbis_File need to be allocated and released in c, right? I would guess that you do it in another primitive method (to ease the error handling), pass a pointer back to Smalltalk, and have an additional primitive method to release them when explicitly asked or the holding object gets GC'd.
Whats an 'oop', in the context of passing parameters to primitives?
How do I make the actual function call to the DLL ?
I didn't think this would be as hard as it has turned out.. Thanks much for any advice.
Thanks, Eddie
On Wed, Jan 29, 2003 at 11:51:01PM -0500, Eddie Cottongim wrote:
Here's an example of what I'm trying to do. For a C function residing in 'vorbisfile.dll' having the following prototype: int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes);
I create this method in my plugin class (using the specification system, I assume thats the best way to go):
primOvOpen: file oggVorbisFile: vf initial: initial long: ibytes |rcvr size | self primitive: 'primOvOpen' "unpleasant things"
The following questions come up: The c-land things like FILE and the struct OggVorbis_File need to be allocated and released in c, right? I would guess that you do it in another primitive method (to ease the error handling), pass a pointer back to Smalltalk, and have an additional primitive method to release them when explicitly asked or the holding object gets GC'd.
The *vf would need to point to a data structure which you would allocate (or declare static) in your plugin. If you are just trying to get things working, you may want to start with a static allocation, and replace it with something better once you have the primitive working. In any case, I would not recommend handling your malloc() and free() from the Squeak side; just do it in the plugin. (Speaking of malloc, I have the sense that it is a Bad Idea to use malloc any more than absolutely necessary, on the theory that someone may someday redo Squeak object memory in some way that does not mix well with plugins using the malloc libraries. Just a hunch.)
As for the *f parameter, that is supposed to be a pointer to FILE, representing a previously opened file. Therefore you do not have to allocate anything for it. However, there's one glitch you will want to be aware of: although it would be nice to open the file in Squeak and just pass the FileStream to your primitive, it may not work nicely on various platforms. On Windows, the SQFile data structure (in FilePlugin) has a member which is a Win32 HANDLE, whereas on other platforms the same element of the data structure is a *FILE. There may be a way to make a *FILE out of a HANDLE on Win32, but I have not figured it out yet. You may be better off just passing the name of the file to your plugin, and doing the file open in the plugin prior to calling ov_open().
I think this would lead you to a design in which your plugin would have a primitiveOvOpen method which took the full file path name as its single parameter, and answer some object representing the open Ogg Vorbis file (could be an Integer which you would treat as a handle for the open file, or perhaps a ByteArray containing the address of the OggVorbis_File data structure -- but don't use the address as anything other than an opaque handle). To close the file, you would have have a second primitive, say primitiveOvClose, which would take one argument, the handle or ByteArray which represents the open file. This primitive would take care of any required cleanup, such as freeing the *vf data structure.
Whats an 'oop', in the context of passing parameters to primitives?
An OOP is a 32 bit int. This is what gets passed to a primitive on the stack. On the C side, it just looks like an int.
How do I make the actual function call to the DLL ?
I'm not sure how DLLs work on Windows, but calling the function from the primitive should just look like this: result _ self cCode: 'ov_open(f, vf, NULL, 0)'.
Dave
Here" http://cap-lore.com/CapTheory/Language/Scheme/sensoryScm.html are some notes I wrote on adding a "sensory" function to Scheme. They would allow an untrusted guest to peruse data structures without danger of damaging them.
Mark, I would like to thank you for this significant commitment you have made in engaging us with your ideas.
My goals have been to learn some of E's infrastructure, and to start building towards multi-user squeak and mobile-code. It looks like this fits at your second level of ambition. It would not surprise me if this is the level Croquet is reaching for.
However, I like the challenge and reward of your fourth level of ambition. It requires several orders of magnitude greater of a community commitment. If I understand correctly, with the benefit of having read on your site about financial capabilities, privileged security abstractions allows for trusted "arbitrage" objects (my terminology), which people could *really* trust. Here is where some of these ideas are published (http://www.erights.org/smart-contracts/index.html). That is a very valuable feature.
As you ask in your mail, what would we lose from Squeak, or be willing to lose, to do these things? I think we need our tool environment and the debugger. We need to have on-the-fly coding and state manipulation, inside the debugger and inspector. We should be able to turn down security, if authorized, to debug between secure components. If the user is not authorized to debug into a component, perhaps the execution stack inside of that component could be pickled for an authorized user to replay through the debugger, at a later time. Without these kinds of features, and certainly others I don't know about or can't express, it may turn out to be a very hard compromise.
We would need to secure the stack.
"No way Squeakers could do that" I hear.
"Who knows. let's at least find out", I respond.
sincerely, Rob
On Wednesday, January 29, 2003, at 05:37 PM, Mark S. Miller wrote:
[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:
- 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.
- 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.
- Privileged security abstractions
- 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
At 11:27 PM 1/29/2003 Wednesday, Robert Withers wrote:
Mark, I would like to thank you for this significant commitment you have made in engaging us with your ideas.
Hey, no commitment! Just a strong desire. I'll do what I can. ;) ;)
My goals have been to learn some of E's infrastructure, and to start building towards multi-user squeak and mobile-code. It looks like this fits at your second level of ambition. It would not surprise me if this is the level Croquet is reaching for.
However, I like the challenge and reward of your fourth level of ambition. It requires several orders of magnitude greater of a community commitment.
Perhaps, but I don't think so. I think the tradeoff is instead how similar SafeSqueak is to Squeak, and how much Squeak is ported to SafeSqueak rather than tamed or rewritten. I think what is simply unachievable, no matter what level of community involvement, is to achieve the forth level with a language that is only painlessly different than existing Squeak.
As for effort, I think the tradeoff is the other way around.
"Simplicity is the unavoidable price which we must pay for reliability." -- Tony Hoare.
Doubly so for security. Given a change of basic architectural principles, it is often easier to start over than it is to retrofit. Again, doubly so for security. E was born at Electric Communities under the same "The E Extensions to Java" http://www.erights.org/history/original-e/ . (That E is now known as "Original-E".) Java, like Squeak and Scheme, was already close to being a perfect pure capability language. (This is, in all three cases, ignoring the libraries.) So we thought we could define E as a capability secure variant of Java that would be close enough to port much old Java code.
Three years and $10M later, I think we understood the problem well enough that we could succeed at it, but even after that investment it would have been a much harder effort than what we did do: take those insights and build a new language from scratch a) on top of Java, b) designed to seem familiar to Java programmers, and c) inheriting the Java libraries via a taming mechanism so that E would start with a large endowment of working libraries.
OTOH, Jonathan Rees, in a one-man thesis-sized project, turned Scheme into W7. W7 is as fully capability secure as is E (actually, E without auditors), and is a minor an almost perfectly upwards compatible variant of Scheme. So it can go either way.
I can't say for certain which of these two cases is the better analogy to the task we face with Squeak, but I sadly suspect it's much more like the Java situation.
If I understand correctly, with the benefit of having read on your site about financial capabilities, privileged security abstractions allows for trusted "arbitrage" objects (my terminology), which people could *really* trust.
"trust" is tricky. I think this statement is too strong in any case.
As you ask in your mail, what would we lose from Squeak, or be willing to lose, to do these things?
Good questions. More later... Probably not till Monday.
---------------------------------------- Text by me above is hereby placed in the public domain
Cheers, --MarkM
Mark S. Miller squeak-dev@lists.squeakfoundation.org said:
I can't say for certain which of these two cases is the better analogy to the task we face with Squeak, but I sadly suspect it's much more like the Java situation.
Why? Squeak's flexibility is closer to Scheme's than to Java. My gut feeling is that it's doable. Maybe with some syntax additions, but nothing really big.
Mark,
On Thursday, January 30, 2003, at 02:36 PM, Mark S. Miller wrote:
At 11:27 PM 1/29/2003 Wednesday, Robert Withers wrote:
Mark, I would like to thank you for this significant commitment you have made in engaging us with your ideas.
Hey, no commitment! Just a strong desire. I'll do what I can. ;) ;)
:-) since you put it that way, thank you for your posts.
However, I like the challenge and reward of your fourth level of ambition. It requires several orders of magnitude greater of a community commitment.
Perhaps, but I don't think so. I think the tradeoff is instead how similar SafeSqueak is to Squeak, and how much Squeak is ported to SafeSqueak rather than tamed or rewritten. I think what is simply unachievable, no matter what level of community involvement, is to achieve the forth level with a language that is only painlessly different than existing Squeak.
How would it be different? Where would it hurt? I can imagine that it would be a beautiful engine, asynchronously sending messages and making sure things synchronize later. Events from the external system would just be another message send into the image. You wouldn't need an idle loop. You could schedule the message sends based on cost feedback. I don't have as good a feel for what SafeSqueak would feel like, though.
Well, it wouldn't take a community commitment, of course, but it would require some core group of enthusiasts. I think they are out there. :-)
As for effort, I think the tradeoff is the other way around.
"Simplicity is the unavoidable price which we must pay for reliability." -- Tony Hoare.
Doubly so for security. Given a change of basic architectural principles, it is often easier to start over than it is to retrofit. Again, doubly so for security.
Well, it has that burn the diskpacks feel to it, and that's a plus, i believe.
I look forward to the next episode. :)
cheers, rob
Robert Withers rwithers12@attbi.com wrote:
How would it be different? Where would it hurt? I can imagine that it would be a beautiful engine, asynchronously sending messages and making sure things synchronize later. Events from the external system would just be another message send into the image. You wouldn't need an idle loop. You could schedule the message sends based on cost feedback. I don't have as good a feel for what SafeSqueak would feel like, though.
Well, it wouldn't take a community commitment, of course, but it would require some core group of enthusiasts. I think they are out there. :-)
SafeSqueak would also demonstrate to the Smalltalk community that such a wonderful way to do in-language security is available. Honestly, one reason I got into this is that it drives me crazy whenever someone says "we need security, so lets add passwords" or "we need security, so lets start using cryptographic signatures". If I start telling them about capabilities, they currently just say "that's only theore. It's spinning my head, and I already understand and can use these other methods, anyway. Security = passwords. nya nya nya i can't here you".
Yarg!! If we start saying "Well, SafeSmalltalk does it this way", and even better "SafeSmalltalk automatically prevents the security hole you just got in your Java program", then these discussions will go a lot more nicely. Further, the world will start getting populated with more secure software. The most aggravating part of the computing industry is that people keep doing stupid things out of ignorance. There's little respect far what has been studied already. One of the chief designers actually stood up at OOPSLA in front of 1000+ people a few years ago and said he didn't see much point to having closures in his language.
Lex
On Friday, January 31, 2003, at 05:00 AM, Lex Spoon wrote:
Robert Withers rwithers12@attbi.com wrote:
How would it be different? Where would it hurt? I can imagine that it would be a beautiful engine, asynchronously sending messages and making sure things synchronize later. Events from the external system would just be another message send into the image. You wouldn't need an idle loop. You could schedule the message sends based on cost feedback. I don't have as good a feel for what SafeSqueak would feel like, though.
Well, it wouldn't take a community commitment, of course, but it would require some core group of enthusiasts. I think they are out there. :-)
SafeSqueak would also demonstrate to the Smalltalk community that such a wonderful way to do in-language security is available. Honestly, one reason I got into this is that it drives me crazy whenever someone says "we need security, so lets add passwords" or "we need security, so lets start using cryptographic signatures". If I start telling them about capabilities, they currently just say "that's only theore. It's spinning my head, and I already understand and can use these other methods, anyway. Security = passwords. nya nya nya i can't here you".
A perfect application of revoking the audible capability to the poor little non-squeaklet. Feed the annoyance some bytecodes and perhaps he'll pipe down. Java, JMS, J2EE, JTS, XML, XTS, XSL, SOAP, RMI, CLR, ML, Ocaml... whatever. If there's no market for secure Smalltalk, so we just have to create our own.
Yarg!!
yeah, Lex. Let it be known.
If we start saying "Well, SafeSmalltalk does it this way", and even better "SafeSmalltalk automatically prevents the security hole you just got in your Java program", then these discussions will go a lot more nicely.
such perfect restrained sarcasm and disdain for the opponent. I love it! :)
Further, the world will start getting populated with more secure software. The most aggravating part of the computing industry is that people keep doing stupid things out of ignorance. There's little respect far what has been studied already. One of the chief designers actually stood up at OOPSLA in front of 1000+ people a few years ago and said he didn't see much point to having closures in his language.
Which nincompoop was that? Squeakers are the smartest group of people I have ever had the pleasure of working with. Come join the new list and let's see what ferments, or rather foments.
Thanks for your fantastic posts these past few days. I have really enjoyed reading them.
cheers, rob
ps. Let's transition the discussion to squeak-e. We should leave the other 900 squeakers alone, at this point ;-)
Mark S. Miller squeak-dev@lists.squeakfoundation.org said:
- Unprivileged security abstractions
[...] I would like to see Squeak evolve into a system that can support #4.
I do. Thanks for the extensive write-up. In order to do it justice, I've forwarded it to my mail account (I read this with a newsreader) and will study it carefully and slowly ;-).
Could someone explain about "World"?
It's one of these ugly globals. The current Morphic graphics environment. An instance of a PasteUpMorph.
Every object that wants to do something with the UI needs to go directly or indirectly to World. One of the main issues is - if you replace World by a World-like capability - where the capability comes from. This is what triggered this whole dynamic scoping discussion.
I'll look at the URL's you pointed out before mingling in, though :-)
At 02:51 AM 1/30/2003 Thursday, cg@cdegroot.com wrote:
Every object that wants to do something with the UI needs to go directly or indirectly to World.
When would an object do this without being passed or holding some other ui object that gives it context? A button doesn't draw on the screen, it draws within some containing thing, etc, recursively. At least this is how it works in every ui toolkit I'm familiar with (including AWT, Swing, SWT). Could you give an example that I could understand without learning Morphic? Thanks.
---------------------------------------- Text by me above is hereby placed in the public domain
Cheers, --MarkM
Mark, your review of security possibilities in Squeak is deeply appreciated.
Your notion of 4 levels of effort is a great basis of discussion. In fact, I believe Islands works at level 4, though it is more awkward for such thigns than E. I'll attach my solution to the little-money problem at the end of the message. Still, as you guessed, the goal of the project was more for level 1 or maybe level 2 (eg, retracting sound access from annoying squeaklets), with hopes of making a basis for level 4.
In this message, I'll first elaborate on some big ideas, and then try to respond to questions on the details of Islands. The code example is atthe very end. Thus everyone can read as far down as they care too.
The main thing you'll care about is that, during the money exrecise, I became completely convinced that dynamic class lookups are bad. They are a convenient means to the end of reusing Squeak code quickly, but they mean that to be safe, you have to ensure your own island is running whenever an untrusted user calls you. This is a real nuisance! Otherwise, even things like integer addition can be a security breach, because in Squeak this will fall back on language-level code. Imagine what could happen if LargeInteger were replaced by something nasty!
There is a big engineering effort to get away from dynamic binding of class names yet still have limited access to classes. Happily, though, the independent modules efforts for Squeak seem to be solving this exact issue along with their other goals.
The more general dynamic global variables are annoying, but the one case of World is hard to do away with in Squeak. World refers to the current morphic rendering unit. Currently it is an entire desktop, though in a Squeaklet it would be more like one page or one subwindow. From another viewpoint, World is the root window from XWindows.
Code uses World all over the place: there are 98 direct accesses to the variable, and 119 senders of #currentWorld. Plus, there is a tricky *conceptual* issue with getting rid of the variable. It's just really convenient to have the world floating in the ether, and it would suck if Squeak code became significantly harder to write. But maybe it can be done; someone may want to try seeing how many of these method can be rewritten without the variable!
Note that you don't have to use dynamic variables if you don't want them. It's entirely reasonable to think of a future partitioning of Squeak where none of Morphic runs in any context that needs security, and thus nothing unsafe is accessing World. All the other globals variables seem easy to move around, by attaching the right objects to World. If you write code that doesn't use any global variables, and if we had static binding for classes, then you wouldn't have to fiddle with installing proper islands before running your code.
On another topic, you say that level-1 success, where you have completely isolated applets, is useless. That is not actually true in Squeak. The most important goal of Squeak is to allow a new way of composing electronic documents. Level-1 success means Squeak documents can be exchanged safely, which is a huge step forward for Squeak.
Something that is easy to overlook in Islands is that the restricted class methods are very important. The class methods that a limited proxy will execute are *very* limited. In fact, they are so limited, that you can always accomplish the same thing the class method does by inlining the code. The main thing is that in addition to the regular restrictions on safe methods, these methods may not access instance variables. Any class method that does not follow the restrictions, will be invisible to anyone who cannot touch raw classes (which is pretty much everyone, since touching raw classes is an immediate security problem).
Okay, let me respond to specific questions.
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
I believe this is implemented. However, the instance methods just call the class methods. :) I guess you'd actually want to use something other than this exact message sequence to *create* an ObjectInspector instance, though, in order to defeat using "myObjectInspector class" to create arbitrary new ObjectInspectors.
Note that the automatic restriction on class methods is pulling weight in this scenario.
Anyway, I never made a serious pass at secure debugging, and it seems like a low priority. It seems very complicated -- e.g., the debugger itself may have to worry about being corrupted by the code it is debugging!
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 distinction is unhelpful for Squeak literals. I suppose immutable is the better word. Currently, literals in Squeak change all too often; specifically, I have a literal change about once a year, and it sucks when it happens. :) Making literals immutable along with read-only is a terrific thing. The only time they are mutated in Islands, is when they are instantiated by the compiler when a method is compiled.
The section "Dynamic Variable" seems to be to explain only per-process variables. Is this right?
There's a little more to it. Dynamic variables have a stack of bindings within each process. All accesses modify the most deeply nested binding. When you pop off the deepest binding, the old binding will return along with its old value.
As a final tweak, the bindings come a table at a time. When you install an island, it installs a new set of bindings (and hides anything not explicitly listed in the island). So you can install and uninstall islands to restore a complete set of predictable bindings. This idea is used in the example below.
(Implementation note: I never made an island-creating capability for Islands, and I never implemented a thisIsland facility for grabbing the island you are running in. It seems like these would be very easy, however.)
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.
You mark it by sending messages to the real, low-level class object, the same class object that the VM uses. Thus it is as privilaged as it can be.
I really hope that Islands reaches the 4th level of success that you talk about, in which case normal users never need to write privilaged methods. Who needs to define primitives, or access variables from the system's most privilaged island, or touch thisContext? Logically no one, and hopefully this works out in practice.
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.
It's possible for it to be the first. For example, if a squeaklet forks a thread, then the thread will surely be given the same island and thus the same bindings.
It is perfectly reasonable, though, for secure code to keep track of a predictable island for its own use. When you install an island you have created, you get no accidental variable bindings.
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?
A compile error.
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.
I'm not sure what sentense this was. Process is probably the wrong word, and island should be used instead.
Are restricted classes (as implemented by the restricting proxy) read-only? (I mean, genuinely read-only, not immutable.) I think they should be.
Yes, that's part of the point. Incidentally, all classes are restricted unless you have a "SystemIsland" installed. It just doesn't seem like the more powerful features of classes are very useful in practice. For example, how often do you really need a class instance variable instead of a regular class variable? Privilaged class methods also become a convenient place to stick methods that only privilaged code be using; there is no way for unprivilaged code to directly invoke such methods.
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.)
I believe I solved this quite sufficiently, however. There aren't any more specific cases. When you run a method on a restricted class proxy, then anything that method can do, you can already do yourself.
Smalltalk doesn't have many statement types. All of them can be reviewed. I haven't done it *real* carefully, but it ceartinly seems like anything a restricted class method can do, the caller of that method could do already, with the one exception of instantiating classes. The other statements are things like creating temporary variables, sending messages to objects you already have, creating literals, creating blocks, and so on.
My intuition for this design approach is that class methods fall into three categories:
1. Utility methods, eg returning a constant. These need no privilage.
2. Instantiation methods. Except for #new itself, these are the same as #1.
3. Primitives. These should not be directly accessible in restricted contexts.
The Islands solution allows for #1 automatically, and for the two necessary methods in #2 (basicNew and basicNew:) to be punched through explicitly. Eliminating #3 automatically is a nice side benefit, which means that a lot of legacy code doesn't have to be rewritten immediately.
Incidentally, this notion of super-safe mehods is useful for instance methods, as well. If you are auditing proxies, then you can immediately ignore super-safe methods.
Also incidentally, my favorite solution to this problem would be to get rid of class methods. Even if we have to write explicit factories like the Java guys, it would simplify Smalltalk so much that it may well be worth it. This analysis and solution to class methods are probably the hardest part of Islands, and yet class methods are supposed to be simplifying things! (Alternatively, the class-ish methods and the utility-ish methods of classes could be separate aspects of some kind, eg using Nathanael's and Stephen's "Traits", but this is starting to make my head spin. I'll let them try, if they want 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).
It's an algorithmic definition. The compiler automatically marks which methods stay within the super-safe subset of Smalltalk described above, and proxies check whether this mark is present.
This is why there is no list of rejected methods. I agree that having a reject list is easy to screw up with.
I don't understand why you allow instaVarAt: and instVatAt:put: on non-proxies? Does this include non-proxies written in SafeSqueak?
Umm, you don't have to except for legacy code. However, it loses nothing for non-proxies. If instVarAt: is unsafe, then the object should probably be a proxy.
The idea is that if you instatiate a normal object, then you already can get access to all the things the object will have in its instance variables. And if that is true, then you can extend the argument used above with super-safe class methods: you cannot do anything with instVarAt:, or by calling any method compiled in restricted mode, that you could not do with your own code.
Thus, it's almost a definition: Proxies are objects which protect their instance variables from outsiders. The name "proxy" thus works very well (though I'm not sure I had all this in mind at the time the word was chosen!)
I completely did not understand the section "Characters and Symbols", probably because of my ignorance of modern Smalltalks. Could you expand?
The issue is that both characters and symbols should be == whenever they are =. For example:
(Character value: 10) == (Character value: 10)
To accomplish this, there must be a system-wide table of characters and symbols. Currently these are in a class variable (incidentally, Islands was still secure before this work was done -- it was just that creating characters and symbols from their constituents did not work, outside of literals.)
Thus, the two creation methods (#value: and #intern:) for these classes are made into instance methods of capability objects.
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.
Similarly, installing a cursor is accomplished by sending a method to a known global variable. (It could just as well be an instance method of the world....) Depending on what is in that variable, the cursor will become the hardware cursor, or something else will happen.
In order for an island to change the hardware cursor, it will have to be given the capability that influences the hardware cursor from outside.
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.
Interesting thought. They often get you in trouble, anyway, just from accidental problems. :)
You don't actually explain what the issue is with Exceptions.
Exceptions, like many things in Squeak, are implemented in Smalltalk-level code. They start with thisContext and then trace around the call stack. Instances of exceptions actually have contexts stored in instance variables.
Access to stack frames has not been secured at all, and it seems difficult to do so. Further, there is little use for it in a restricted context. It's a neat exercise to try and come up with safe access to stack frames, but instead I went after the easier problem of nailing down exceptions.
Only, well, it's still hard. A complication is that users can write subclasses of exception and have their code run! My idea for Isladns was that, to get things going, you could only *define* new subclasses of Exception, and that you could not add any method to them. This solves most uses of exceptions, but it's a kludge and it should be rethought at some point. Most likely, Exceptions are screwed up in Islands in multiple places.
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: ?
It's the same with instVarAt: and instVarAtPut:, only for indexed (numbered) variables instead of named variables. Arrays are probably unlikely to be used as proxies, but then again it's extremely easy to block, anyway.
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?
It's already allowed in Squeak. For example, "3 clone" returns 3. Arguably, #shallowCopy should be general and #clone should insist on a real copy, but that's not the way the contracts are right now.
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?
Not right now, due to the dynamic binding of globals.
There probably aren't many globals floating around up there except for classes, though, so reviewing all the code may not be as tedious as it sounds.
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.
I dunno, it seems like you could do an allObjectsDo: inside the VM and look for instance variables pointing into the submemory. Remember than in Squeak, we do have easy access to the VM.
In any case, I recommend postponing further worry about resource controls and denial of service until these other issues settle down.
Agreed.
Okay, here's my solution to the money problem.
Mint, Purse are normal classes with a rich set of methods. The only strange thing is that, Mints instances know of some "money island", which should be easily created via something like "Island new". Money island has all the normal classes installed.
LimitedMint, LimitedPurse are a subclasses of LimitedProxy, and these are what restricted code will see. Probably this is a common pattern in Smalltalk capability implementations, since there are no private methods.
Here are the interesting methods.
===================================================== LimitedPurse>>deposit: rawAmount from: rawOtherPurse | amount otherPurse | amount := self safeIntegerFrom: rawAmount. (self hasSameClassAs: rawPurse) ifFalse: [ self error: 'invalid purse supplied' ]. otherPurse := rawOtherPurse realPurseOnBehalfOf: self.
purse mint moneyIsland installDuring: [ purse mint == otherPurse mint ifFalse: [ self error: 'incompatible mints' ].
amount < 0 ifTrue: [ self error: 'only positive amounts may be deposited' ].
otherPurse decreaseBalance: amount. purse increaseBalance: amount. ].
realPurseOnBehalfOf: aLimitedPurse (self hasSameClassAs: aLimitedPurse) ifTrue: [ ^purse ]
LimitedMint>>newPurse: rawStartingBalance | startingBalance realPurse newPurse | startingBalance := self safeIntegerFor: rawStartingBalance.
mint moneyIsland installDuring: [ startingBalance < 0 ifTrue: [ self error: 'initial balance must be non-negative' ]. realPurse := mint newPurseWithBalance: startingBalance. newPurse := LimitedPurse new initialize: realPurse ]. ^newPurse.
LimitedPurse>>initialize: realPurseToUse purse == nil ifFalse: [ self error: 'dual initialization' ]. purse := realPurseToUse.
=====================================================
Four things seem interesting:
1. There is a ton of code doing input validation. This seems unavoidable with objects-as-capabilities, because callers can pass you any object they please. Some automation would probably be helpful. For example, it looks like E's soft typing is making the E solution shorter (and more reliable). As is, there are a bunch of things in RestrictedProxy such as safeIntegerFor: which will halt execution on bad input.
2. There is no direct support for sealers and unsealers, so the code uses a method realPurseOnBehalfOf:, which in turn relies on hasSameClassAs: to detect valid objects. I believe Sealers and Unsealers, however, could be implemented even within SafeSqueak. (I never saw the point of such a facility until doing this exercise, incidentally -- it's a good one!)
3. Every time the code calls across to a real Mint or Purse object, it has to set the island to Money Island. So the limited methods mostly have the pattern: check arguments, install a sane island, and do the real work inside the sane island. With static binding, this wouldn't be necessary.
4. You have to explicitly check for dual initialization, since construction methods are just normal methods in Smalltalk.
Lex Spoon
At 12:26 AM 1/31/2003 Friday, Lex Spoon wrote:
The main thing you'll care about is that, during the money exrecise, I became completely convinced that dynamic class lookups are bad.
That's a real relief. But before I'm too relieved, is there anyone else who still thinks that dynamic scoping should be part of the Squeak, Squeak-E, or Croquet efforts (or any decent oo language, for that matter)? If so, I'd love to hear the reasons. I thought there were many valid reasons myself once, and I'm eager to help you guys avoid the mistake we almost made.
Unfortunately, I won't be able to answer these at the rate I'd like -- I haven't even answered the ones already posted to the Squeak-E thread. But I do expect to.
---------------------------------------- Text by me above is hereby placed in the public domain
Cheers, --MarkM
squeak-dev@lists.squeakfoundation.org