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.