Fixed identity discontinuity in compartments
MODIFY
shim/examples/simple.html
shim/examples/simple.html
</p>
<script>
- var r1 = new Realm();
- var r2 = new r1.global.Realm({ intrinsics: 'inherit' });
// basic sanity check: they all should output true
- // for now extra () are needed as a signal that it should return something :/
- console.log(1, r1.eval('( JSON )'));
- console.log(2, r1.eval('( JSON )') !== JSON);
- console.log(3, r1.eval('( JSON )') === r1.eval('( JSON )'));
- console.log(4, r1.eval('( JSON )') === r1.eval('( eval("JSON") )'));
- console.log(5, r2.eval('( JSON )'));
- console.log(6, r2.eval('( JSON )') !== JSON);
- console.log(7, r2.eval('( JSON )') === r2.eval('( JSON )'));
- console.log(8, r2.eval('( JSON )') === r2.eval('( eval("JSON") )'));
+ // Root realm
+ const r1 = new Realm();
- console.log(9, r2.eval('( JSON )') === r1.eval('( JSON )'));
- console.log(10, r2.eval('( eval("JSON") )') === r1.eval('( eval("JSON") )'));
+ console.log(1.1, r1.eval('( JSON )'));
+ console.log(1.2, r1.eval('( JSON )') !== JSON);
+ console.log(1.3, r1.eval('( JSON )') === r1.eval('( JSON )'));
+ console.log(1.4, r1.eval('( JSON )') === r1.eval('( eval("JSON") )'));
+ console.log(1.5, r1.eval('( eval instanceof Function )'));
+ // Root realm vs root realm
+ const r2 = new Realm();
+
+ console.log(2.1, r2.eval('( JSON )'));
+ console.log(2.2, r2.eval('( JSON )') !== JSON);
+ console.log(2.3, r2.eval('( JSON )') === r2.eval('( JSON )'));
+ console.log(2.4, r2.eval('( JSON )') === r2.eval('( eval("JSON") )'));
+ console.log(2.5, r2.eval('( eval instanceof Function )'));
+
+ console.log(2.6, r2.eval('( JSON )') !== r1.eval('( JSON )'));
+ console.log(2.7, r2.eval('( eval("JSON") )') !== r1.eval('( eval("JSON") )'));
+
+ // Compartment realm vs root realm
+ const r3 = new r1.global.Realm({ intrinsics: 'inherit' });
+
+ console.log(3.1, r3.eval('( JSON )'));
+ console.log(3.2, r3.eval('( JSON )') !== JSON);
+ console.log(3.3, r3.eval('( JSON )') === r3.eval('( JSON )'));
+ console.log(3.4, r3.eval('( JSON )') === r3.eval('( eval("JSON") )'));
+ console.log(3.5, r1.eval('( eval instanceof Function )'));
+
+ console.log(3.6, r3.eval('( JSON )') === r1.eval('( JSON )'));
+ console.log(3.7, r3.eval('( eval("JSON") )') === r1.eval('( eval("JSON") )'));
+
+ const r = new Realm();
document.getElementById('run').addEventListener('click', function () {
- var sourceText = document.getElementById('sourceText').value;
- var result, output;
+ const sourceText = document.getElementById('sourceText').value;
+ let result, output;
try {
result = r.eval(sourceText);
} catch (e) {

MODIFY
shim/src/evaluators.js
shim/src/evaluators.js
// https://github.com/v8/v8/blob/master/src/builtins/builtins-function.cc
import { GlobalObject, Intrinsics, ShimSandbox } from './symbols';
-import { defineProperties } from './commons';
+import { defineProperty, getOwnPropertyDescriptor, setPrototypeOf } from './commons';
import { Handler } from './handler';
export function getDirectEvalEvaluatorFactory(sandbox) {
@@ -21,9 +21,7 @@ export function getDirectEvalEvaluatorFactory(sandbox) {
}
export function getDirectEvalEvaluator(realmRec) {
- const sandbox = realmRec[ShimSandbox];
- const globalObject = realmRec[GlobalObject];
- const intrinsics = realmRec[Intrinsics];
+ const { [ShimSandbox]:sandbox, [GlobalObject]:globalObject, [Intrinsics]:intrinsics } = realmRec;
// This proxy has several functions:
// 1. works with the sentinel to alternate between direct eval and confined eval.
@@ -34,32 +32,22 @@ export function getDirectEvalEvaluator(realmRec) {
const scopedEvaluator = sandbox.evalEvaluatorFactory(proxy);
- function evaluator(src) {
+ // Create an eval without a [[Construct]] behavior such that the
+ // invocation "new eval()" throws TypeError: eval is not a constructor".
+ const evaluator = {
+ eval(src) {
handler.isInternalEvaluation = true;
// Ensure that "this" resolves to the secure global.
const result = scopedEvaluator.call(globalObject, src);
handler.isInternalEvaluation = false;
return result;
}
+ }.eval;
- // Ensure that the different eval instances of the different
- // realms all answer properly when used with the instanceof
- // operator to preserve indentity.
- const FunctionPrototype = sandbox.unsafeFunction.prototype;
-
- // Mimic the native eval() function. New properties are
- // by default non-writable and non-configurable.
- defineProperties(evaluator, {
- name: {
- value: 'eval'
- },
- prototype: {
- value: FunctionPrototype
- }
- });
-
- // This instance is realm-specific, and therefore doesn't
- // need to be frozen (only the objects reachable from it).
+ // Ensure that eval from any compartment in a root realm is an
+ // instance of Function in any compartment of the same root ralm.
+ const { unsafeFunction } = sandbox;
+ setPrototypeOf(evaluator, unsafeFunction.prototype.constructor);
// Once created for a realm, the reference must be updated everywhere.
return evaluator;
@@ -70,11 +58,9 @@ export function getDirectEvalEvaluator(realmRec) {
* the safety of evalEvaluator for confinement.
*/
export function getFunctionEvaluator(realmRec) {
- const sandbox = realmRec[ShimSandbox];
- const globalObject = realmRec[GlobalObject];
- const intrinsics = realmRec[Intrinsics];
+ const { [ShimSandbox]:sandbox, [GlobalObject]:globalObject, [Intrinsics]:intrinsics } = realmRec;
- function evaluator(...params) {
+ const evaluator = function Function(...params) {
const functionBody = params.pop() || '';
let functionParams = params.join(',');
@@ -97,24 +83,16 @@ export function getFunctionEvaluator(realmRec) {
return intrinsics.eval(src);
}
- // Ensure that the different Function instances of the different
- // realms all answer properly when used with the instanceof
- // operator to preserve indentity.
- const FunctionPrototype = sandbox.unsafeFunction.prototype;
-
- // Mimic the native signature. New properties are
- // by default non-writable and non-configurable.
- defineProperties(evaluator, {
- name: {
- value: 'Function'
- },
- prototype: {
- value: FunctionPrototype
- }
- });
+ // Ensure that Function from any compartment in a root realm can be used
+ // with instance checks in any compartment of the same root realm.
+ const { unsafeFunction } = sandbox;
+ setPrototypeOf(evaluator, unsafeFunction.prototype.constructor);
- // This instance is namespace-specific, and therefore doesn't
- // need to be frozen (only the objects reachable from it).
+ // Ensure that any function created in any compartment in a root realm is an
+ // instance of Function in any compartment of the same root ralm.
+ const desc = getOwnPropertyDescriptor(evaluator, 'prototype');
+ desc.value = unsafeFunction.prototype
+ defineProperty(evaluator, 'prototype', desc);
// Once created for a realm, the reference must be everywhere.
return evaluator;

MODIFY
shim/src/functions.js
shim/src/functions.js
@@ -32,6 +32,7 @@ function repairFunction(sandbox, functionName, functionDecl) {
});
defineProperty(FunctionPrototype, 'constructor', { value: TamedFunction });
+ // Ensures that all functions meet "instanceof Function" in a realm.
setPrototypeOf(TamedFunction, unsafeFunction.prototype.constructor);
}
@@ -43,13 +44,14 @@ function repairFunction(sandbox, functionName, functionDecl) {
*/
export function repairFunctions(sandbox) {
const { unsafeGlobal: g } = sandbox;
- const hasAsyncIteration = typeof g.Symbol.asyncIterator !== 'undefined';
// Here, the order of operation is important: Function needs to be
// repaired first since the other constructors need it.
repairFunction(sandbox, 'Function', 'function');
repairFunction(sandbox, 'GeneratorFunction', 'function*');
repairFunction(sandbox, 'AsyncFunction', 'async function');
+
+ const hasAsyncIteration = typeof g.Symbol.asyncIterator !== 'undefined';
if (hasAsyncIteration) {
repairFunction(sandbox, 'AsyncGeneratorFunction', 'async function*');
}

MODIFY
shim/src/realm.js
shim/src/realm.js
@@ -18,22 +18,13 @@ function getCurrentContext() {
return new Function('return this')();
}
-function getCurrentRealmRecord() {
- const context = getCurrentContext();
- if (RealmRecord in context) {
- return context[RealmRecord];
- }
+function getCurrentSandbox() {
+ const context = getCurrentContext()
const sandbox = createSandbox(context);
- const realmRec = {
- [Intrinsics]: getIntrinsics(sandbox),
- [GlobalObject]: sandbox.unsafeGlobal,
- [ShimSandbox]: sandbox
- };
- context[RealmRecord] = realmRec;
- return realmRec;
+ return sandbox;
}
-function portRealmConstructor(sandbox) {
+function createRealmFacade(sandbox) {
const { unsafeFunction, unsafeGlobal } = sandbox;
// Rebuild a Realm class using inrinsics from the sandbox,
@@ -45,36 +36,39 @@ function portRealmConstructor(sandbox) {
// and properties on a realm instance already return
// values based on the intrinsics of the realm.
- const sub = unsafeFunction('base', `
-function Realm() {
- base.apply(this, arguments);
+ unsafeGlobal.Realm = unsafeFunction('base', 'ShimSandbox', 'sandbox', `
+
+function Realm(options) {
+ this[ShimSandbox] = sandbox;
+ base.call(this, options);
}
+
const descs = Object.getOwnPropertyDescriptors(base.prototype);
+
Object.defineProperties(Realm.prototype, {
intrinsics: {
get() {
- return descs.intrinsics.get.apply(this);
+ return descs.intrinsics.get.call(this);
}
},
global: {
get() {
- return descs.global.get.apply(this);
+ return descs.global.get.call(this);
}
},
eval: {
- value() {
- return descs.eval.value.apply(this, arguments);
+ value(x) {
+ return descs.eval.value.call(this, x);
}
}
});
+
+Realm.toString = () => base.toString();
+
return Realm;
-`)(Realm);
- defineProperties(unsafeGlobal, {
- Realm: {
- value: sub
- }
- });
+ `)(Realm, ShimSandbox, sandbox);
+
}
function setGlobaObject(realmRec) {
@@ -107,40 +101,36 @@ function setDefaultBindings(realmRec) {
defineProperties(realmRec[GlobalObject], descs);
}
+// The current sandbox is the sandbox where the
+// Realm shim is being parsed and executed.
+const currentSandbox = getCurrentSandbox();
+
export default function Realm(options) {
const O = this;
const opts = Object(options);
- // Limitation: the evaluators are tied to a sandbox,
- // and can't be completely abstracted from the inrinsics
- // of that sandbox. We impose in the shim the restriction
- // that intinsics must match the evaluator, and can't be
- // provided independently.
-
- if ('evaluator' in opts) {
- throw new TypeError('Realm does not support "evaluator" option.');
- }
-
let sandbox;
let intrinsics = opts.intrinsics;
if (intrinsics === 'inherit') {
// In "inherit" mode, we create a compartment realm and inherit
// the sandbox since we share the intrinsics. We create a new
// set to allow us to define eval() anf Function() for the realm.
- const parentRealm = getCurrentRealmRecord();
- sandbox = parentRealm[ShimSandbox];
- intrinsics = assign({}, parentRealm[Intrinsics]);
+ if (ShimSandbox in O) {
+ sandbox = O[ShimSandbox]
+ } else {
+ sandbox = currentSandbox;
+ }
} else if (intrinsics === undefined) {
// When intrinics are not provided, we create a root realm
// using the fresh set of new intrinics from a new sandbox.
sandbox = createSandbox();
- portRealmConstructor(sandbox);
- intrinsics = getIntrinsics(sandbox);
+ createRealmFacade(sandbox);
} else {
throw new TypeError('Realm only supports undefined or "inherited" intrinsics.');
}
+ intrinsics = getIntrinsics(sandbox);
const realmRec = {
[ShimSandbox]: sandbox,
@@ -190,4 +180,3 @@ defineProperties(Realm.prototype, {
});
Realm.toString = () => 'function Realm() { [shim code] }';
-

Fixed identity discontinuity in compartments