- What Are You Hiding?
- Impedance Mismatch
- Unifying Abstractions
- Is Your Abstraction Recursive?
- Think Twice, Code Once
Is Your Abstraction Recursive?
It's important for a large abstraction category to be recursive. A good example of an abstraction that fails this rule is the process. A modern CPU contains a memory management unit (MMU) that, among other things, allows the currently running code to have its access to memory regions restricted or prevented. The operating system uses this capability to prevent one process from interfering with another. The only way for processes to communicate is via the operating system.
This abstraction is not recursive: A process cannot contain other processes. Would that feature be useful? In many cases, yes. You're probably reading this article in a web browser, which provides an excellent case study. The web browser downloads various forms of executable code, most commonly JavaScript. It then just-in-time compiles (or interprets) that code in an isolated context.
What does isolated mean in this context? As long as the JavaScript engine has no bugs, the script can't escape. As long as the Flash plug-in has no bugs, the Flash applet can't escape. As long as the Java virtual machine has no bugs, the Java applet can't escape. You can check a recent list of security advisories for how good an assumption this is.
Now, imagine if processes were a truly recursive abstraction. The browser would be able to create sub-processes that wouldn't be allowed to make any system calls, other than performing inter-process communication with the parent process. The JavaScript engine could be made entirely out of bugs, but the worst action that the script would be able to take would be to crash itself. Even if it could execute arbitrary code, the script still would only be able to send messages to the parent process, and the parent process could validate these messages before allowing any access to the filesystem or any other resources.
Hardware isolation of processes does a much better job than software-based protection does, but it's not available to userspace developers because operating system designers made some bad decisions in terms of abstractions. Yet, all the way back in the '70s, IBM mainframes supported a fully recursive abstraction here; you could recursively partition the mainframe into virtual machines, each of which could contain virtual machines, and so on.
Processes aren't the only abstraction to suffer from this lack of foresight. One issue came up in my own research area. In the '60s, Alan Kay defined objects as simple models of computers that communicate by passing messages. In the very first version of the Smalltalk system, which was designed to embody this model, the elephant in the room was the virtual machine. The virtual machine contained objects and mediated communication between them, just as the OS contains processes and mediates communication between them. As simple models of computers, objects should have had the same capability, but didn't.
Recently I've been working on fixing this problem, using the Object Planes abstraction. It allows object graphs to be isolated so that they can't communicate outside of their plane, unless this communication is mediated by the plane object.