Virtual Machine: Processing Model

The Sandbox establishes a virtual environment with a virtual independent process context around all executing code. Since in reality, a single process and therefore thread is shared across all intra-page instances, the Sandbox enforces a number of controls to minimize the impact any single Sandbox can have across the entire system.

To enforce the process model (and provide better security), all execution within a Sandbox instance originates from within the Sandbox. Within an execution context, all invocations are first intercepted by the Sandbox. No code is permitted to freely execute. This interception allows both security and performance policies to be enforced.

Instancing Model

Instantiating any number of unique and isolated instances from any code reference is core to the Sandbox. To support the instancing model, the Sandbox creates a factory for any content that can be used to instantiate a new virtual environment. This enables the environment to automatically support the dynamic creation, multi-instancing, and reloading of any content. Any Sandboxes can be easily re-enabled (even if the code fails) merely by reloading the instance. Multiple instances can be created simply by providing a code reference to a Sandbox constructor.

Code Isolation Challenges

There are a few execution challenges with arbitrary code:

  1. Single shared execution thread
  2. Single shared exception stack
  3. Namespace Collisions
JavaScript execution within a page shares the same thread as the page itself and the surrounding user interface. Long-executing code can cause the page and therefore the browser to appear blocked and non-responsive. The browser has mechanisms that may detect long-running code, offering the user the opportunity to disable it. However, doing so leads to challenges with the exception stack. Since all code shares a single exception stack, any error can potentially disrupt the entire page and “crash” the application. For example, deep recursion, long-running code, or simply unhandled exceptions can crash a page. The most egregious errors can occur if code enters an infinite loop of alerts (which requires killing the task) or generates the IE "Operation aborted" error disabling the entire page.

Isolating Execution

Execution is isolated by a number of mechanisms. First, the untrusted content is transformed into a class-factory privatizing all the content's member within a closure. This prevents any namespace leakage or collisions with the outer page. When instantiated, each instance is provided a context that synthesizes a unique namespace so the code never has direct access to the global window or functions. To protect the callstack, all called user-defined functions are validated to be within the scope calling them.

Isolating the Exception Stack

As part of the QoS architecture, exceptions in each Sandbox instance are kept in isolation. This is accomplished via a number of mechanisms. First, each time the instance begins execution, it is wrapped in a try-catch block. This try-catch ensures that any uncaught exceptions are processed by the Sandbox and never handed to the surrounding environment. When an uncaught exception occurs, the Sandbox checks if the instance has established a private onerror event. If so, the onerror event for the instance is fired.

To ensure exceptions generated by the QoS layer are propogated to the sandbox, the transformation process of script always injects a special call in all user-defined exception handlers. This call will force any exceptions that need to reach the outer Sandbox to be propogated by rethrowing them until they reach the outer environment. Also, the Sandbox marks a flag for any failed code to prevent re-entrance.

Isolating the Namespaces

To establish a virtual machine around content, the content is transformed to direct all invocations through the Sandbox Virtual Machine. This transformation is not designed to do security checks (although it will do syntactic validation). Instead, its purpose is to guarantee that all execution is properly intercepted. The most severe failure of the Sandbox would be for code to bypass the virtual machine and execute directly. The system is a white-list-based system; no execution is redirected to the native browser’s implementation that has not been explicitly permitted.

Dynamic Processing

The decision process is more than a binary decision. Rather, the Sandbox takes into account the execution context and applied policies before processing. The processing may either be the built-in browser implementation or it can be augmented or even replaced by the engine. By applying the execution context, the system not only examines the type of object but also the actual object instance when making security determinations.

Referencing Model

Since the system examines every instance, the system assumes an object reference by itself does not create security risks. Instead, it’s the operations on an object that need to be protected.

Type System

To accurately apply security policies, the system needs to strongly type all instances as they are processed by the Sandbox. Typing occurs during execution and the set of types beyond the JavaScript primitives is defined by the Policies themselves.

Strong typing can occur at runtime since the system bounds everything to its own unique global object. All invocations originate from the synthesized root object and the return values are automatically typed during execution. While a reference may be shared across multiple Sandboxes, the types are uniquely maintained. This typing, combined with the execution context, provides the metadata necessary for a policy to make security decisions.

Shadow Model

JavaScript supports both the ability to extend an object (expando) since every object is essentially a hash table, as well as the ability to extend the prototypes shared across each object instance. The Sandbox can shadow any defined type with a scoped prototype and any object reference with a shadowed expando. This shadowing occurs for each instance and guarantees that each context gets a unique system to model and manipulate. For example, if code within one Sandbox extends the JavaScript Array with a new method, that method would only be available to that Sandbox. If another Sandbox defined its own method of the same name, the shadowing model ensures they do not collide.

Context Sharing and Global Variables

When instantiating any code, the Sandbox can provide access to an existing shared context. This context provides the typical global scope as an isolated set of shared variables and functions. This modeling allows the system to dynamically load and attach code to an existing scope during execution. This will be utilized to support dynamic script library loading which is a fundamental operation in modern JavaScript frameworks.

Processing Overhead

The Virtual Machine introduces overhead on every invocation as the statement is evaluated against the security system and monitored for QoS issues. Below is a simple set of tests that illustrate the overhead for basic operations against user-defined objects: