# Limitations of Stack Inspection

This is a follow up to the How does Stack Inspection work? that explores the notion in more detail

Stack inspection is a mechanism for ensuring security in the context of the JVM and CLR virtual machines when externally downloaded code modules of different levels of trust may be running together. That system libraries need some way of distinguishing between calls originating in untrusted code and calls originating from the trusted application itself. This is done by associating with code the principal corresponding to its origin. Then access permissions are recorded on the stack and whenever a call to a sensitive, system method is made, the stack is traversed to see whether the appropriate permissions for the principal making the call are present on the stack.

What are the limitations of stack inspection? What mechanisms have been proposed to replace it? Have any significant changes been made to the model since it was introduced in the late 90s?

Stack inspection is necessary because programs on the JVM and CLR have default access to dangerous operations, so something must be done to prevent disasters. For example an untrusted program can reference an I/O library and call it:

using IO;
...
IO.DeleteFile("/home/foo/bla");


So on each dangerous operation made, we need to check whether it is allowed. With stack inspection it is generally complicated to understand who gets access to what. It also makes optimizations such as inlining and tail calls difficult.

A superior mechanism is to not give each program automatic access to dangerous operations in the first place. In this model there is no way to import an IO library. The only way to get access to an IO library is if somebody else gave it to you. This is called capability security. An introduction can be found here.

Instead, we would write the previous program like this:

Main(IOLibrary IO){
IO.DeleteFile("/home/foo/bla");
}


The IO library is a parameter to the entry point of the program, and this is called a capability (because it gives use some capability, in this case to do IO). To be able to run this program, we must have access to an IO capability ourselves, and run the program by calling Main(ourIOlibrary). If we are running an untrusted program, we simply do not pass our IO library to it, since it might use that library to delete our files. In some cases we want to give an untrusted program limited access to the filesystem. In that case we create a wrapper around our own IO library that only allows access to a certain directory, and pass that one to the untrusted program instead of the full IO library

So if we need an IO capability to invoke a program that needs an IO capability, that also means that whatever invoked our program needed to have access to an IO capability to be able to give it to us. So where did its IO capability come from? Well, eventually there is a point where the human operating the computer invoked a program. This human has access to all the system capabilities, so he was able to pass the IO capability on. If this human does not trust the program he is running, then he simply will not pass his IO capability to it.

You can probably easily imagine other kinds of capabilities: internet access, access to draw stuff on your screen, etc. For example a secure browser plugin system might give a graphics capability to an untrusted plugin that only allows it to paint graphics in a predefined rectangle on the page.

One limitation of stack inspection as traditionally implemented is that it breaks proper tail calls. In particular, typical implementations need to keep the entire "stack" around at all times. Clements and Felleisen show how this problem can be remedied using a technique called "continuation marks" in their paper A Tail-Recursive Semantics for Stack Inspections in ESOP 2003.